了解Python包资源。我知道我可以使用include_package_data
在包中包含任意文件和目录。
假设我编写了一个包含单个类的包:
(mypackage / __init__.py)
from importlib_resources import files
class myClass:
def getResource(self, filename):
return files('mypackage').joinpath(filename).read_text()
现在,我使用setup.py
文件将这些代码打包到一个可安装的Python包中,并进行安装
现在,另一个开发人员出现了,希望将我的类子类化。该用户还将把他们的代码打包在一个包中并发布它,其中一个依赖项是我的包。他们希望getResource
函数从他们的包中提取资源,而不是我的:
( otherpackage / __init__.py )
from importlib_resources import files
from mypackage import myClass
class myNewClass(myClass):
def getResource(self, filename):
return files('otherpackage').joinpath(filename).read_text()
这并不理想,因为其他开发人员必须取代getResource
方法,复制我的代码,然后只更改包名称。(编辑:这可以通过super
和传递包名称来完成,但这仍然是一个额外的步骤,可能会被认为违反了DRY原则(
问题是:有没有办法获得当前执行的代码所属的Python包的名称?(或者,也许更好的方法是确定一个类属于哪个包(这样,我的包就可以简单地发现它现在在otherpackage
的子类中运行,并从otherpackage
中适当地获取资源。(我可以编写一个回退代码,这样,如果用户的包中没有包含资源,我就可以从mypackage
中的默认资源中检索它(。
在这个例子中,我希望在myClass
的实例中运行的代码可以得到mypackage
,但在myNewClass
的实例中执行的代码可以获得otherpackage
,所以我可以将其作为参数传递给files()
。我希望,如果代码没有在包中运行,我可以简单地取回None
,并使用利用__file__
的回退来处理这种情况。
换句话说,我希望有一个函数,如果开发人员直接实例化myClass
,并且该函数在类的代码中被调用,那么它应该从mypackage
中提取资源;如果开发人员实例化CCD_ 18,则应该使用该开发人员的包。如果开发人员在包外实例化了myNewClass
,我应该能够检测到这一点,并通过与__file__
的路径组合来回退定位资源。我希望在其他开发人员不需要在我的代码中明确提供包的名称的情况下实现这一点——DRY原则适用于此。
次要问题:如果第二个开发人员以dev/link模式(在pip-install上使用-e
(设置他们的软件包,那么在开发软件包时这会起作用吗?换句话说,其他开发人员是否可以使用importlib.resources
库从代码中透明地访问开发树中的文件?
当然,我会记录API的子类开发人员必须如何处理包资源(例如,将文件放在哪里(,因此他们只需遵循我在myClass
的期望中构建的任何目录名或其他约定。
为了找到定义了某个类的模块,可以使用inspect.getmodule
:
from importlib.resources import read_text
from inspect import getmodule
class myClass:
def getResource(self, filename):
return read_text(getmodule(self).__name__, filename)
如果你想考虑某个类在哪里被实例化,你必须依赖于帧检查,例如通过inspect.stack
:
from inspect import stack
class myClass:
def getResource(self, filename):
caller_filename = stack()[1].filename
... # do something based on `caller_filename`
return read_text(getmodule(self).__name__, filename)
然而,您的问题描述听起来像是命名空间包会很有用(另请参见PEP420(,开发人员可以将自己的东西放入命名空间包中。因此,只有您的分发版才会包含获取各种资源的逻辑。