ron*_*zon 75 python file package
你能告诉我如何读取Python包中的文件?
我加载的包有许多我想从程序中加载的模板(用作字符串的文本文件).但是如何指定此类文件的路径?
想象一下,我想从以下位置读取文件:
package\templates\temp_file
Run Code Online (Sandbox Code Playgroud)
某种路径操纵?包基路径跟踪?
ank*_*tis 133
假设您的模板位于此路径的模块包中:
<your-package>
+--<module-asking-the-file>
+--templates/
+--temp_file <-- We want this file.
Run Code Online (Sandbox Code Playgroud)
读取模板的正确方法是使用setuptools发行版中的importlib.resources
包:
import pkg_resources
# Could be any dot-separated package/module name or a "Requirement"
resource_package = __name__
resource_path = '/'.join(('templates', 'temp_file')) # Do not use os.path.join()
template = pkg_resources.resource_string(resource_package, resource_path)
# or for a file-like stream:
template = pkg_resources.resource_stream(resource_package, resource_path)
Run Code Online (Sandbox Code Playgroud)
提示:
这会读,即使你的分布是压缩数据,所以你可以设置pkg_resources
你的setuptools
,和/或使用期待已久的__file__
打包机从蟒蛇- 3.5打造自成体系的分布.
根据Setuptools/package_data
docs,不要使用data_files
:
基本资源访问
请注意,资源名称必须是
setup.py
分隔路径,不能是绝对路径(即无引导路径pkg_resources
)或包含相对名称,如"setuptools
".千万不能使用pkg_resources
程序来操作的资源路径,因为它们不是文件系统路径.
wim*_*wim 116
在您甚至担心读取资源文件之前,第一步是确保首先将数据文件打包到您的发行版中 - 直接从源代码树中读取它们很容易,但重要的部分是确保这些资源文件可以从已安装包中的代码访问。
像这样构建您的项目,将数据文件放入包内的子目录中:
.
??? package
? ??? __init__.py
? ??? templates
? ? ??? temp_file
? ??? mymodule1.py
? ??? mymodule2.py
??? README.rst
??? MANIFEST.in
??? setup.py
Run Code Online (Sandbox Code Playgroud)
你应该通过include_package_data=True
在setup()
呼叫。仅当您想使用 setuptools/distutils 和构建源分发时才需要清单文件。要确保templates/temp_file
为此示例项目结构打包,请在清单文件中添加如下一行:
recursive-include package *
Run Code Online (Sandbox Code Playgroud)
历史遗留注释: 现代构建后端(例如 flit、poetry)不需要使用清单文件,默认情况下这些后端将包含包数据文件。因此,如果您正在使用pyproject.toml
并且没有setup.py
文件,那么您可以忽略所有关于MANIFEST.in
.
现在,不用包装,进入阅读部分……
使用标准库pkgutil
API。它在库代码中看起来像这样:
# within package/mymodule1.py, for example
import pkgutil
data = pkgutil.get_data(__name__, "templates/temp_file")
Run Code Online (Sandbox Code Playgroud)
它适用于拉链。它适用于 Python 2 和 Python 3。它不需要第三方依赖项。我并没有真正意识到任何缺点(如果你是,那么请对答案发表评论)。
这是目前公认的答案。充其量,它看起来像这样:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
Run Code Online (Sandbox Code Playgroud)
那有什么问题?假设您有可用的文件和子目录是不正确的。如果执行打包在 zip 或轮子中的代码,则此方法不起作用,并且您的包是否被提取到文件系统可能完全不受用户控制。
这在最高投票的答案中有所描述。它看起来像这样:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
Run Code Online (Sandbox Code Playgroud)
那有什么问题?它添加了对setuptools的运行时依赖,最好仅是安装时依赖。导入和使用可能会变得非常缓慢,因为代码构建了所有已安装包的工作集,即使您只对自己的包资源感兴趣。这在安装时没什么大不了的(因为安装是一次性的),但在运行时很丑陋。pkg_resources
这是目前最高投票答案中的建议。这是最近添加的标准库(Python 3.7 中的新内容)。它看起来像这样:
from importlib.resources import read_binary
data = read_binary("package.templates", "temp_file")
Run Code Online (Sandbox Code Playgroud)
那有什么问题?好吧,不幸的是,它不起作用......还没有。这仍然是一个不完整的 API,使用importlib.resources
将要求您添加一个空文件templates/__init__.py
,以便数据文件驻留在子包中而不是子目录中。它还会将package/templates
子目录本身公开为可导入的package.templates
子包。如果这不是什么大不了的事情并且不会打扰您,那么您可以继续将__init__.py
文件添加到那里并使用导入系统访问资源。但是,当您在使用它时,您也可以将其改为my_resources.py
文件,只需在模块中定义一些字节或字符串变量,然后在 Python 代码中导入它们。无论哪种方式,都是进口系统在这里承担重任。
这在任何其他答案中尚未提及,但importlib_resources
不仅仅是 Python 3.7+importlib.resources
代码的简单向后移植。它具有可遍历的 API,您可以像这样使用:
import importlib_resources
my_resources = importlib_resources.files("package")
data = (my_resources / "templates" / "temp_file").read_bytes()
Run Code Online (Sandbox Code Playgroud)
这适用于 Python 2 和 3,它适用于 zip,并且不需要__init__.py
在资源子目录中添加虚假文件。pkgutil
我能看到的唯一缺点是这些新的 API 尚未出现在 stdlib 中,因此仍然存在第三方依赖性。较新的 APIimportlib_resources
应该importlib.resources
在 Python 3.9 中到达 stdlib 。
我在github上创建了一个示例项目并上传到PyPI,它演示了上面讨论的所有五种方法。试试看:
.
??? package
? ??? __init__.py
? ??? templates
? ? ??? temp_file
? ??? mymodule1.py
? ??? mymodule2.py
??? README.rst
??? MANIFEST.in
??? setup.py
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅https://github.com/wimglenn/resources-example。
Mar*_*oma 14
如果你有这种结构
lidtk
??? bin
? ??? lidtk
??? lidtk
? ??? analysis
? ? ??? char_distribution.py
? ? ??? create_cm.py
? ??? classifiers
? ? ??? char_dist_metric_train_test.py
? ? ??? char_features.py
? ? ??? cld2
? ? ? ??? cld2_preds.txt
? ? ? ??? cld2wili.py
? ? ??? get_cld2.py
? ? ??? text_cat
? ? ? ??? __init__.py
? ? ? ??? README.md <---------- say you want to get this
? ? ? ??? textcat_ngram.py
? ? ??? tfidf_features.py
? ??? data
? ? ??? __init__.py
? ? ??? create_ml_dataset.py
? ? ??? download_documents.py
? ? ??? language_utils.py
? ? ??? pickle_to_txt.py
? ? ??? wili.py
? ??? __init__.py
? ??? get_predictions.py
? ??? languages.csv
? ??? utils.py
??? README.md
??? setup.cfg
??? setup.py
Run Code Online (Sandbox Code Playgroud)
你需要这个代码:
import pkg_resources
# __name__ in case you're within the package
# - otherwise it would be 'lidtk' in this example as it is the package name
path = 'classifiers/text_cat/README.md' # always use slash
filepath = pkg_resources.resource_filename(__name__, path)
Run Code Online (Sandbox Code Playgroud)
我不太确定"总是使用斜线"部分.它可能来自setuptools
另请注意,如果使用路径,则必须使用正斜杠(/)作为路径分隔符,即使您在Windows上也是如此.Setuptools在构建时自动将斜杠转换为适当的特定于平台的分隔符
如果您想知道文档的位置:
David Beazley和Brian K. Jones撰写的Python Cookbook第三版“ 10.8。读取包中的数据文件”中的内容给出了答案。
我将它送到这里:
假设您有一个软件包,其文件组织如下:
mypackage/
__init__.py
somedata.dat
spam.py
Run Code Online (Sandbox Code Playgroud)
现在假设文件spam.py要读取文件somedata.dat的内容。为此,请使用以下代码:
import pkgutil
data = pkgutil.get_data(__package__, 'somedata.dat')
Run Code Online (Sandbox Code Playgroud)
结果变量数据将是一个字节字符串,其中包含文件的原始内容。
get_data()的第一个参数是包含程序包名称的字符串。您可以直接提供它,也可以使用特殊变量,例如__package__
。第二个参数是包中文件的相对名称。如有必要,只要最终目录仍位于软件包中,就可以使用标准Unix文件名约定导航到其他目录。
这样,该软件包可以安装为目录,.zip或.egg。
Flo*_*ian -3
假设您使用的是 Egg 文件;未提取:
我在最近的一个项目中通过使用安装后脚本“解决”了这个问题,该脚本将我的模板从 Egg(zip 文件)提取到文件系统中的正确目录。这是我发现的最快、最可靠的解决方案,因为使用__path__[0]
有时会出错(我不记得名字了,但我至少找到了一个库,它在该列表前面添加了一些东西!)。
此外,egg 文件通常会即时提取到称为“egg 缓存”的临时位置。您可以在启动脚本之前甚至稍后使用环境变量更改该位置,例如。
os.environ['PYTHON_EGG_CACHE'] = path
Run Code Online (Sandbox Code Playgroud)
然而,有pkg_resources可以正确完成这项工作。
归档时间: |
|
查看次数: |
34564 次 |
最近记录: |