doc22_066.md 52.8 KB
Newer Older
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1 2
# torch.package

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
3
> 原文:[`pytorch.org/docs/stable/package.html`](https://pytorch.org/docs/stable/package.html)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
4

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
5
`torch.package` 支持创建包含工件和任意 PyTorch 代码的包。这些包可以保存、共享,用于在以后的日期或不同机器上加载和执行模型,甚至可以使用`torch::deploy`部署到生产环境。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
6

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
7
本文包含教程、指南、解释和 API 参考,将帮助您更多地了解`torch.package`及如何使用它。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
8 9 10 11 12

警告

这个模块依赖于不安全的`pickle`模块。只有解包您信任的数据。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
13
可能构造恶意的 pickle 数据,将在解包时执行任意代码。永远不要解包可能来自不受信任来源或可能被篡改的数据。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
14 15 16

有关`pickle`模块的更多信息,请查看[文档](https://docs.python.org/3/library/pickle.html)

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
17
+   教程
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
18

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
19
    +   打包您的第一个模型
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
20

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
21
+   我该如何...
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
22

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
23
    +   查看包内部内容?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
24

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
25
    +   查看为什么某个模块被包含为依赖?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
26

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
27
    +   如何在我的包中包含任意资源并以后访问它们?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
28

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
29
    +   自定义类如何打包?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
30 31 32

    +   在我的源代码中测试是否在包内执行?

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
33
    +   将代码打补丁到包中?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
34

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
35
    +   从打包的代码中访问包内容?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
36

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
37
    +   区分打包代码和非打包代码?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
38

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
39
    +   重新导出导入的对象?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
40

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
41
    +   打包一个 TorchScript 模块?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
42

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
43
+   解释
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
44

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
45
    +   `torch.package` 格式概述
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
46

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
47
    +   `torch.package` 如何找到您代码的依赖
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
48

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
49
    +   依赖管理
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
50

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
51
    +   `torch.package` 的尖锐边缘
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
52

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
53
    +   `torch.package` 如何保持包之间的隔离
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
54

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
55
+   API 参考
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
56

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
57
## 教程
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
58

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
59
### 打包您的第一个模型
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
60

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
61
提供了一个指导您打包和解包简单模型的教程[在 Colab 上](https://colab.research.google.com/drive/1lFZkLyViGfXxB-m3jqlyTQuYToo3XLo-)。完成这个练习后,您将熟悉用于创建和使用 Torch 包的基本 API。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
62

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
63
## 我该如何...
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
64

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
65
### 查看包内部内容?
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
66

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
67
#### 将包像 ZIP 存档一样处理
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
68

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
69
`torch.package`的容器格式是 ZIP,因此任何可以处理标准 ZIP 文件的工具都可以用于探索内容。与 ZIP 文件交互的一些常见方法:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

+   `unzip my_package.pt``torch.package`存档解压到磁盘上,您可以自由检查其内容。

```py
$ unzip my_package.pt && tree my_package
my_package
├── .data
   ├── 94304870911616.storage
   ├── 94304900784016.storage
   ├── extern_modules
   └── version
├── models
   └── model_1.pkl
└── torchvision
    └── models
        ├── resnet.py
        └── utils.py
~ cd my_package && cat torchvision/models/resnet.py
... 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
91
+   Python 的`zipfile`模块提供了一种标准的方法来读取和写入 ZIP 存档内容。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
92 93 94 95 96 97 98 99 100

```py
from zipfile import ZipFile
with ZipFile("my_package.pt") as myzip:
    file_bytes = myzip.read("torchvision/models/resnet.py")
    # edit file_bytes in some way
    myzip.writestr("torchvision/models/resnet.py", new_file_bytes) 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
101
+   vim 有本地读取 ZIP 存档的能力。您甚至可以编辑文件并将其`:write`回存档中!
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
102 103 104 105 106 107 108 109 110 111

```py
# add this to your .vimrc to treat `*.pt` files as zip files
au BufReadCmd *.pt call zip#Browse(expand("<amatch>"))

~ vi my_package.pt 
```

#### 使用`file_structure()` API

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
112
`PackageImporter` 提供了一个 `file_structure()` 方法,它将返回一个可打印和可查询的 `Directory` 对象。`Directory` 对象是一个简单的目录结构,您可以用它来探索 `torch.package` 的当前内容。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
113

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
114
`Directory` 对象本身是可以直接打印的,将打印出一个文件树表示。要过滤返回的内容,使用类似 glob 的 `include``exclude` 过滤参数。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

```py
with PackageExporter('my_package.pt') as pe:
    pe.save_pickle('models', 'model_1.pkl', mod)

importer = PackageImporter('my_package.pt')
# can limit printed items with include/exclude args
print(importer.file_structure(include=["**/utils.py", "**/*.pkl"], exclude="**/*.storage"))
print(importer.file_structure()) # will print out all files 
```

输出:

```py
# filtered with glob pattern:
#    include=["**/utils.py", "**/*.pkl"], exclude="**/*.storage"
─── my_package.pt
    ├── models
       └── model_1.pkl
    └── torchvision
        └── models
            └── utils.py

# all files
─── my_package.pt
    ├── .data
       ├── 94304870911616.storage
       ├── 94304900784016.storage
       ├── extern_modules
       └── version
    ├── models
       └── model_1.pkl
    └── torchvision
        └── models
            ├── resnet.py
            └── utils.py 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
153
您还可以使用 `has_file()` 方法查询 `Directory` 对象。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
154 155 156 157 158 159

```py
importer_file_structure = importer.file_structure()
found: bool = importer_file_structure.has_file("package_a/subpackage.py") 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
160
### 查看为什么一个给定模块被包含为一个依赖项?[](#see-why-a-given-module-was-included-as-a-dependency "Permalink to this heading")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
161

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
162
假设有一个给定的模块 `foo`,您想知道为什么您的 `PackageExporter``foo` 作为一个依赖项引入。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
163

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
164
`PackageExporter.get_rdeps()` 将返回所有直接依赖于 `foo` 的模块。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
165

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
166
如果您想查看给定模块 `src` 如何依赖于 `foo``PackageExporter.all_paths()` 方法将返回一个以 DOT 格式显示的图形,显示 `src``foo` 之间的所有依赖路径。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
167

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
168
如果您只想查看您的 `PackageExporter` 的整个依赖图,您可以使用 `PackageExporter.dependency_graph_string()`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
169

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
170
### 如何在我的包中包含任意资源并以后访问它们?[](#include-arbitrary-resources-with-my-package-and-access-them-later "Permalink to this heading")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
171

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
172
`PackageExporter` 提供了三种方法,`save_pickle``save_text``save_binary`,允许您将 Python 对象、文本和二进制数据保存到一个包中。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
173 174 175 176 177 178 179 180 181

```py
with torch.PackageExporter("package.pt") as exporter:
    # Pickles the object and saves to `my_resources/tensor.pkl` in the archive.
    exporter.save_pickle("my_resources", "tensor.pkl", torch.randn(4))
    exporter.save_text("config_stuff", "words.txt", "a sample string")
    exporter.save_binary("raw_data", "binary", my_bytes) 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
182
`PackageImporter` 提供了名为 `load_pickle``load_text``load_binary` 的补充方法,允许您从一个包中加载 Python 对象、文本和二进制数据。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
183 184 185 186 187 188 189 190

```py
importer = torch.PackageImporter("package.pt")
my_tensor = importer.load_pickle("my_resources", "tensor.pkl")
text = importer.load_text("config_stuff", "words.txt")
binary = importer.load_binary("raw_data", "binary") 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
191
### 如何自定义类的打包方式?[](#customize-how-a-class-is-packaged "Permalink to this heading")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
192

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
193
`torch.package` 允许自定义类的打包方式。通过在一个类上定义方法 `__reduce_package__` 并定义相应的解包函数来访问这种行为。这类似于为 Python 的普通 pickling 过程定义 `__reduce__`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

步骤:

1.  在目标类上定义方法 `__reduce_package__(self, exporter: PackageExporter)`。这个方法应该完成将类实例保存在包中的工作,并应该返回一个元组,其中包含相应的解包函数以及调用解包函数所需的参数。当 `PackageExporter` 遇到目标类的实例时,会调用这个方法。

1.  为这个类定义一个解包函数。这个解包函数应该完成重建并返回类实例的工作。函数签名的第一个参数应该是一个 `PackageImporter` 实例,其余参数由用户定义。

```py
# foo.py [Example of customizing how class Foo is packaged]
from torch.package import PackageExporter, PackageImporter
import time

class Foo:
    def __init__(self, my_string: str):
        super().__init__()
        self.my_string = my_string
        self.time_imported = 0
        self.time_exported = 0

    def __reduce_package__(self, exporter: PackageExporter):
  """
 Called by ``torch.package.PackageExporter``'s Pickler's ``persistent_id`` when
 saving an instance of this object. This method should do the work to save this
 object inside of the ``torch.package`` archive.

 Returns function w/ arguments to load the object from a
 ``torch.package.PackageImporter``'s Pickler's ``persistent_load`` function.
 """

        # use this pattern to ensure no naming conflicts with normal dependencies,
        # anything saved under this module name shouldn't conflict with other
        # items in the package
        generated_module_name = f"foo-generated._{exporter.get_unique_id()}"
        exporter.save_text(
            generated_module_name,
            "foo.txt",
            self.my_string + ", with exporter modification!",
        )
        time_exported = time.clock_gettime(1)

        # returns de-packaging function w/ arguments to invoke with
        return (unpackage_foo, (generated_module_name, time_exported,))

def unpackage_foo(
    importer: PackageImporter, generated_module_name: str, time_exported: float
) -> Foo:
  """
 Called by ``torch.package.PackageImporter``'s Pickler's ``persistent_load`` function
 when depickling a Foo object.
 Performs work of loading and returning a Foo instance from a ``torch.package`` archive.
 """
    time_imported = time.clock_gettime(1)
    foo = Foo(importer.load_text(generated_module_name, "foo.txt"))
    foo.time_imported = time_imported
    foo.time_exported = time_exported
    return foo 
```

```py
# example of saving instances of class Foo

import torch
from torch.package import PackageImporter, PackageExporter
import foo

foo_1 = foo.Foo("foo_1 initial string")
foo_2 = foo.Foo("foo_2 initial string")
with PackageExporter('foo_package.pt') as pe:
    # save as normal, no extra work necessary
    pe.save_pickle('foo_collection', 'foo1.pkl', foo_1)
    pe.save_pickle('foo_collection', 'foo2.pkl', foo_2)

pi = PackageImporter('foo_package.pt')
print(pi.file_structure())
imported_foo = pi.load_pickle('foo_collection', 'foo1.pkl')
print(f"foo_1 string: '{imported_foo.my_string}'")
print(f"foo_1 export time: {imported_foo.time_exported}")
print(f"foo_1 import time: {imported_foo.time_imported}") 
```

```py
# output of running above script
─── foo_package
    ├── foo-generated
       ├── _0
          └── foo.txt
       └── _1
           └── foo.txt
    ├── foo_collection
       ├── foo1.pkl
       └── foo2.pkl
    └── foo.py

foo_1 string: 'foo_1 initial string, with reduction modification!'
foo_1 export time: 9857706.650140837
foo_1 import time: 9857706.652698385 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
292
### 在我的源代码中测试是否正在包内执行?[](#test-in-my-source-code-whether-or-not-it-is-executing-inside-a-package "Permalink to this heading")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
293

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
294
`PackageImporter`将在初始化的每个模块上添加属性`__torch_package__`。您的代码可以检查此属性的存在来确定它是否在打包上下文中执行。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

```py
# In foo/bar.py:

if "__torch_package__" in dir():  # true if the code is being loaded from a package
    def is_in_package():
        return True

    UserException = Exception
else:
    def is_in_package():
        return False

    UserException = UnpackageableException 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
311
现在,代码的行为将取决于它是通过 Python 环境正常导入还是从`torch.package`导入。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
312 313 314 315 316 317 318 319 320 321 322 323

```py
from foo.bar import is_in_package

print(is_in_package())  # False

loaded_module = PackageImporter(my_package).import_module("foo.bar")
loaded_module.is_in_package()  # True 
```

**警告**:通常,根据代码是打包还是未打包而表现不同是不好的实践。这可能导致难以调试的问题,这些问题对您导入代码的方式很敏感。如果您的包打算被广泛使用,请考虑重新组织您的代码,使其无论如何加载都表现相同。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
324
### 将代码打补丁到包中?[](#patch-code-into-a-package "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
325

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
326
`PackageExporter`提供了一个`save_source_string()`方法,允许将任意 Python 源代码保存到您选择的模块中。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

```py
with PackageExporter(f) as exporter:
    # Save the my_module.foo available in your current Python environment.
    exporter.save_module("my_module.foo")

    # This saves the provided string to my_module/foo.py in the package archive.
    # It will override the my_module.foo that was previously saved.
    exporter.save_source_string("my_module.foo", textwrap.dedent(
  """\
 def my_function():
 print('hello world')
 """
    ))

    # If you want to treat my_module.bar as a package
    # (e.g. save to `my_module/bar/__init__.py` instead of `my_module/bar.py)
    # pass is_package=True,
    exporter.save_source_string("my_module.bar",
                                "def foo(): print('hello')\n",
                                is_package=True)

importer = PackageImporter(f)
importer.import_module("my_module.foo").my_function()  # prints 'hello world' 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
353
### 从打包代码中访问包内容?[](#access-package-contents-from-packaged-code "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
354

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
355
`PackageImporter`实现了[importlib.resources](https://docs.python.org/3/library/importlib.html#module-importlib.resources) API,用于从包内访问资源。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368

```py
with PackageExporter(f) as exporter:
    # saves text to my_resource/a.txt in the archive
    exporter.save_text("my_resource", "a.txt", "hello world!")
    # saves the tensor to my_pickle/obj.pkl
    exporter.save_pickle("my_pickle", "obj.pkl", torch.ones(2, 2))

    # see below for module contents
    exporter.save_module("foo")
    exporter.save_module("bar") 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
369
`importlib.resources` API 允许从打包代码中访问资源。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
370 371 372 373 374 375 376 377 378 379 380

```py
# foo.py:
import importlib.resources
import my_resource

# returns "hello world!"
def get_my_resource():
    return importlib.resources.read_text(my_resource, "a.txt") 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
381
使用`importlib.resources`是从打包代码中访问包内容的推荐方式,因为它符合 Python 标准。但是,也可以从打包代码中访问父`PackageImporter`实例本身。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396

```py
# bar.py:
import torch_package_importer # this is the PackageImporter that imported this module.

# Prints "hello world!", equivalent to importlib.resources.read_text
def get_my_resource():
    return torch_package_importer.load_text("my_resource", "a.txt")

# You also do things that the importlib.resources API does not support, like loading
# a pickled object from the package.
def get_my_pickle():
    return torch_package_importer.load_pickle("my_pickle", "obj.pkl") 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
397
### 区分打包代码和非打包代码?[](#distinguish-between-packaged-code-and-non-packaged-code "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
398 399 400 401 402 403 404 405 406 407 408 409 410 411

要确定对象的代码是否来自`torch.package`,请使用`torch.package.is_from_package()`函数。注意:如果对象来自包但其定义来自标记为`extern``stdlib`的模块,此检查将返回`False`

```py
importer = PackageImporter(f)
mod = importer.import_module('foo')
obj = importer.load_pickle('model', 'model.pkl')
txt = importer.load_text('text', 'my_test.txt')

assert is_from_package(mod)
assert is_from_package(obj)
assert not is_from_package(txt) # str is from stdlib, so this will return False 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
412
### 重新导出已导入的对象?[](#re-export-an-imported-object "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
413

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
414
重新导出之前由`PackageImporter`导入的对象,您必须让新的`PackageExporter`知道原始的`PackageImporter`,以便它可以找到您对象的依赖项的源代码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
415 416 417 418 419 420 421 422 423 424

```py
importer = PackageImporter(f)
obj = importer.load_pickle("model", "model.pkl")

# re-export obj in a new package
with PackageExporter(f2, importer=(importer, sys_importer)) as exporter:
    exporter.save_pickle("model", "model.pkl", obj) 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
425
### 打包 TorchScript 模块?[](#package-a-torchscript-module "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
426

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
427
要打包 TorchScript 模型,请使用与任何其他对象相同的`save_pickle``load_pickle` API。支持保存属性或子模块的 TorchScript 对象,无需额外工作。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
428 429 430 431 432 433 434 435 436 437 438 439

```py
# save TorchScript just like any other object
with PackageExporter(file_name) as e:
    e.save_pickle("res", "script_model.pkl", scripted_model)
    e.save_pickle("res", "mixed_model.pkl", python_model_with_scripted_submodule)
# load as normal
importer = PackageImporter(file_name)
loaded_script = importer.load_pickle("res", "script_model.pkl")
loaded_mixed = importer.load_pickle("res", "mixed_model.pkl" 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
440
## 解释
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
441

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
442
### `torch.package`格式概述[](#torch-package-format-overview "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
443

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
444
`torch.package`文件是一个 ZIP 存档,通常使用`.pt`扩展名。在 ZIP 存档中,有两种类型的文件:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
445 446 447 448 449

+   框架文件,放置在`.data/`中。

+   用户文件,即其他所有文件。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
450
例如,这是一个来自`torchvision`的完全打包的 ResNet 模型的样子:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470

```py
resnet
├── .data  # All framework-specific data is stored here.
         # It's named to avoid conflicts with user-serialized code.
   ├── 94286146172688.storage  # tensor data
   ├── 94286146172784.storage
   ├── extern_modules  # text file with names of extern modules (e.g. 'torch')
   ├── version         # version metadata
   ├── ...
├── model  # the pickled model
   └── model.pkl
└── torchvision  # all code dependencies are captured as source files
    └── models
        ├── resnet.py
        └── utils.py 
```

#### 框架文件

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
471
`.data/`目录由 torch.package 拥有,其内容被视为私有实现细节。`torch.package`格式不对`.data/`的内容做任何保证,但任何更改都将向后兼容(即,新版本的 PyTorch 始终能够加载旧的`torch.packages`)。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491

目前,`.data/`目录包含以下项目:

+   `version`:序列化格式的版本号,以便`torch.package`导入基础设施知道如何加载此包。

+   `extern_modules`:一个被视为`extern`的模块列表。`extern`模块将使用加载环境的系统导入器进行导入。

+   `*.storage`:序列化的张量数据。

```py
.data
├── 94286146172688.storage
├── 94286146172784.storage
├── extern_modules
├── version
├── ... 
```

#### 用户文件

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
492
存档中的所有其他文件都是用户放置的。布局与 Python[常规包](https://docs.python.org/3/reference/import.html#regular-packages)相同。要深入了解 Python 打包的工作原理,请参考[这篇文章](https://www.python.org/doc/essays/packages/)(它略有过时,因此请通过[Python 参考文档](https://docs.python.org/3/library/importlib.html)双重检查实现细节)。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507

```py
<package root>
├── model  # the pickled model
   └── model.pkl
├── another_package
   ├── __init__.py
   ├── foo.txt         # a resource file , see importlib.resources
   └── ...
└── torchvision
    └── models
        ├── resnet.py   # torchvision.models.resnet
        └── utils.py    # torchvision.models.utils 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
508
### 如何`torch.package`找到您代码的依赖项[](#how-torch-package-finds-your-code-s-dependencies "跳转到此标题")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
509 510 511

#### 分析对象的依赖项[](#analyzing-an-object-s-dependencies "跳转到此标题")

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
512
当您发出`save_pickle(obj, ...)`调用时,`PackageExporter`将正常对对象进行 pickle。然后,它使用`pickletools`标准库模块来解析 pickle 字节码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
513

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
514
在 pickle 中,对象与描述对象类型实现位置的`GLOBAL`操作码一起保存,例如:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
515 516 517 518 519

```py
GLOBAL 'torchvision.models.resnet Resnet` 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
520
依赖解析器将收集所有`GLOBAL`操作,并将它们标记为您的 pickled 对象的依赖项。有关 pickling 和 pickle 格式的更多信息,请参考[Python 文档](https://docs.python.org/3/library/pickle.html)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
521 522 523

#### 分析模块的依赖项[](#analyzing-a-module-s-dependencies "跳转到此标题")

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
524
当 Python 模块被识别为依赖项时,`torch.package`会遍历模块的 Python AST 表示,并查找具有标准形式的导入语句的支持:`from x import y``import z``from w import v as u`等。当遇到这些导入语句之一时,`torch.package`会将导入的模块注册为依赖项,然后以相同的 AST 遍历方式解析它们自己。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
525

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
526
**注意**:AST 解析对于`__import__(...)`语法有限支持,并且不支持`importlib.import_module`调用。一般来说,您不应该期望`torch.package`能够检测到动态导入。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
527

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
528
### 依赖管理[](#dependency-management "跳转到此标题")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
529

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
530
`torch.package`会自动找到您的代码和对象依赖的 Python 模块。这个过程称为依赖解析。对于依赖解析器找到的每个模块,您必须指定要执行的*操作*
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545

允许的操作是:

+   `intern`:将此模块放入包中。

+   `extern`:将此模块声明为包的外部依赖项。

+   `mock`:模拟此模块。

+   `deny`:依赖于此模块将在包导出期间引发错误。

最后,还有一个重要的操作不是技术上`torch.package`的一部分:

+   重构:删除或更改代码中的依赖项。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
546
请注意,操作仅在整个 Python 模块上定义。无法仅打包模块中的“只是”函数或类并将其余部分留出。这是有意设计的。Python 没有为模块中定义的对象提供清晰的边界。依赖组织的唯一定义单元是模块,因此`torch.package`使用它。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
547

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
548
使用模式对模块应用操作。模式可以是模块名称(`"foo.bar"`)或通配符(如`"foo.**"`)。您可以使用`PackageExporter`上的方法将模式与操作关联起来,例如:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
549 550 551 552 553 554 555 556 557 558 559 560

```py
my_exporter.intern("torchvision.**")
my_exporter.extern("numpy") 
```

如果模块与模式匹配,则将对其应用相应的操作。对于给定的模块,将按照定义的顺序检查模式,并采取第一个操作。

#### `intern`

如果一个模块被`intern`,它将被放入包中。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
561
这个操作是您的模型代码,或者您想要打包的任何相关代码。例如,如果您正在尝试从`torchvision`打包一个 ResNet,您将需要`intern`模块 torchvision.models.resnet。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
562

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
563
在包导入时,当您的打包代码尝试导入一个`intern`-ed 模块时,PackageImporter 将在您的包内查找该模块。如果找不到该模块,将引发错误。这确保了每个`PackageImporter`与加载环境隔离——即使您的包和加载环境中都有`my_interned_module``PackageImporter`也只会使用您包中的版本。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
564

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
565
**注意**:只有 Python 源模块可以被`intern`。其他类型的模块,如 C 扩展模块和字节码模块,如果您尝试`intern`它们,将引发错误。这些类型的模块需要被`mock``extern`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
566 567 568 569 570

#### `extern`

如果一个模块被`extern`,它将不会被打包。相反,它将被添加到此包的外部依赖项列表中。您可以在`package_exporter.extern_modules`中找到此列表。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
571
在包导入时,当打包的代码尝试导入一个`extern`-ed 模块时,`PackageImporter`将使用默认的 Python 导入器来查找该模块,就好像您执行了`importlib.import_module("my_externed_module")`。如果找不到该模块,将引发错误。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590

通过这种方式,您可以依赖于第三方库,如`numpy``scipy`,而无需将它们也打包。

**警告**:如果任何外部库以不兼容的方式更改,您的包可能无法加载。如果您需要长期的包可重现性,请尽量限制对`extern`的使用。

#### `mock`

如果一个模块被`mock`,它将不会被打包。相反,将会打包一个存根模块。存根模块将允许您从中检索对象(因此`from my_mocked_module import foo`不会出错),但对该对象的任何使用将引发`NotImplementedError`

`mock`应该用于您“知道”在加载的包中不会需要的代码,但您仍希望在非打包内容中可用。例如,初始化/配置代码,或仅用于调试/训练的代码。

**警告**:一般来说,`mock`应该作为最后的手段使用。它会引入打包代码和非打包代码之间的行为差异,可能会导致后续混淆。相反,最好重构您的代码以删除不需要的依赖项。

#### 重构

管理依赖项的最佳方法是根本不要有依赖项!通常,代码可以重构以删除不必要的依赖项。以下是编写具有干净依赖项的代码的一些指导原则(这些也通常是良好的实践!):

**只包含你使用的内容**。不要在你的代码中留下未使用的导入。依赖解析器不够智能,无法判断它们是否确实未使用,并将尝试处理它们。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
591
**限定您的导入**。例如,不要写 import foo 然后在后面使用`foo.bar.baz`,最好写`from foo.bar import baz`。这更精确地指定了您的真实依赖项(`foo.bar`),并让依赖解析器知道您不需要`foo`的全部内容。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
592 593 594 595 596

**将具有无关功能的大文件拆分为较小的文件**。如果您的`utils`模块包含各种无关功能,任何依赖于`utils`的模块都将需要引入大量无关的依赖项,即使您只需要其中的一小部分。相反,最好定义单一用途的模块,可以独立打包。

#### 模式

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
597
模式允许您使用方便的语法指定模块组。模式的语法和行为遵循 Bazel/Buck [glob()](https://docs.bazel.build/versions/master/be/functions.html#glob)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634

我们正在尝试匹配的模块称为候选模块。候选模块由一系列由分隔符字符串分隔的段组成,例如`foo.bar.baz`

模式包含一个或多个段。段可以是:

+   文字字符串(例如`foo`),精确匹配。

+   包含通配符的字符串(例如`torch``foo*baz*`)。通配符匹配任何字符串,包括空字符串。

+   双通配符(`**`)。这与零个或多个完整段匹配。

示例:

+   `torch.**`:匹配`torch`及其所有子模块,例如`torch.nn``torch.nn.functional`

+   `torch.*`:匹配`torch.nn``torch.functional`,但不匹配`torch.nn.functional``torch`

+   `torch*.**`:匹配`torch``torchvision`和它们的所有子模块

在指定操作时,可以传递多个模式,例如。

```py
exporter.intern(["torchvision.models.**", "torchvision.utils.**"]) 
```

如果模块与任何模式匹配,它将匹配此操作。

您还可以指定要排除的模式,例如。

```py
exporter.mock("**", exclude=["torchvision.**"]) 
```

如果匹配任何排除模式,模块将不匹配此操作。在此示例中,我们模拟所有模块,除了`torchvision`及其子模块。

当一个模块可能匹配多个操作时,将采取首先定义的操作。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
635
### `torch.package`尖锐边缘[](#torch-package-sharp-edges "跳转到此标题")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
636 637 638

#### 避免在您的模块中使用全局状态[](#avoid-global-state-in-your-modules "跳转到此标题")

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
639
Python 使绑定对象和在模块级别范围运行代码变得非常容易。这通常没问题——毕竟,函数和类是以这种方式绑定到名称的。但是,当您在模块范围定义一个对象以进行突变时,引入可变全局状态时,情况会变得更加复杂。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
640 641 642

可变全局状态非常有用——它可以减少样板文件,允许向表中开放注册等。但是,除非非常小心地使用,否则在与`torch.package`一起使用时可能会引起复杂性。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
643
每个`PackageImporter`为其内容创建一个独立的环境。这很好,因为这意味着我们加载多个包并确保它们彼此隔离,但是当模块以假定共享可变全局状态的方式编写时,此行为可能会导致难以调试的错误。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
644 645 646

#### 类型不在包之间共享,并且加载环境[](#types-are-not-shared-between-packages-and-the-loading-environment "跳转到此标题")

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
647
`PackageImporter`导入的任何类都将是特定于该导入器的类的版本。例如:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

```py
from foo import MyClass

my_class_instance = MyClass()

with PackageExporter(f) as exporter:
    exporter.save_module("foo")

importer = PackageImporter(f)
imported_MyClass = importer.import_module("foo").MyClass

assert isinstance(my_class_instance, MyClass)  # works
assert isinstance(my_class_instance, imported_MyClass)  # ERROR! 
```

在此示例中,`MyClass``imported_MyClass`不是*相同类型*。在这个特定示例中,`MyClass``imported_MyClass`具有完全相同的实现,因此您可能认为可以将它们视为相同的类。但是请考虑`imported_MyClass`来自具有完全不同`MyClass`实现的旧包的情况——在这种情况下,将它们视为相同类是不安全的。

在幕后,每个导入器都有一个前缀,允许它唯一识别类:

```py
print(MyClass.__name__)  # prints "foo.MyClass"
print(imported_MyClass.__name__)  # prints <torch_package_0>.foo.MyClass 
```

这意味着当一个参数来自一个包而另一个参数不是时,您不应该期望`isinstance`检查能够工作。如果需要此功能,请考虑以下选项:

+   进行鸭子类型(只使用类而不是明确检查它是给定类型)。

+   使类型关系成为类合同的显式部分。例如,您可以添加一个属性标签`self.handler = "handle_me_this_way"`,并让客户端代码检查`handler`的值而不是直接检查类型。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
679
### 如何使`torch.package`中的包相互隔离[](#how-torch-package-keeps-packages-isolated-from-each-other "跳转到此标题的永久链接")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
680

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
681
每个`PackageImporter`实例为其模块和对象创建一个独立的、隔离的环境。包中的模块只能导入其他打包的模块,或标记为`extern`的模块。如果您使用多个`PackageImporter`实例来加载单个包,您将获得多个独立的环境,它们不会相互交互。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
682

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
683
这是通过使用自定义导入器扩展 Python 的导入基础设施来实现的。`PackageImporter`提供与`importlib`导入器相同的核心 API;即,它实现了`import_module``__import__`方法。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
684

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
685
当调用`PackageImporter.import_module()`时,`PackageImporter`将构建并返回一个新模块,就像系统导入器一样。但是,`PackageImporter`会修补返回的模块,以使用`self`(即`PackageImporter`实例)来满足未来的导入请求,通过在包中查找而不是搜索用户的 Python 环境。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
686 687 688

#### 混淆

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
689
为了避免混淆(“这个`foo.bar`对象是来自我的包还是来自我的 Python 环境?”),`PackageImporter`通过为导入的所有模块添加*混淆前缀*来篡改它们的`__name__``__file__`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
690 691 692 693 694 695 696

对于`__name__`,类似`torchvision.models.resnet18`的名称变为`<torch_package_0>.torchvision.models.resnet18`

对于`__file__`,类似`torchvision/models/resnet18.py`的名称变为`<torch_package_0>.torchvision/modules/resnet18.py`

名称混淆有助于避免不同包之间模块名称的无意间双关,并通过使堆栈跟踪和打印语句更清晰地显示它们是指向打包代码还是其他内容来帮助您调试。有关混淆的面向开发人员的详细信息,请参阅`torch/package/`中的`mangling.md`

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
697
## API 参考
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
698 699

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
700
class torch.package.PackagingError(dependency_graph, debug=False)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
701 702 703 704 705
```

在导出包时出现问题时会引发此异常。`PackageExporter`将尝试收集所有错误并一次性呈现给您。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
706
class torch.package.EmptyMatchError
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
707 708 709 710 711
```

当在打包过程中遇到将 mock 或 extern 标记为`allow_empty=False`,并且在打包期间未匹配到任何模块时,将引发此异常。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
712
class torch.package.PackageExporter(f, importer=<torch.package.importer._SysImporter object>, debug=False)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
713 714 715 716
```

导出器允许您将代码包、Python 数据、以及任意二进制和文本资源写入一个独立的包中。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
717
导入可以以封闭的方式加载代码,使代码从包中加载而不是从正常的 Python 导入系统加载。这允许打包 PyTorch 模型代码和数据,以便在服务器上运行或将来用于迁移学习。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
718 719 720

当创建包时,包中包含的代码是从原始源文件逐个文件复制的,文件格式是一个特别组织的压缩文件。包的未来用户可以解压包,并编辑代码以执行自定义修改。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
721
包导入器确保模块中的代码只能从包内部加载,除非明确列出为外部模块使用`extern()`。压缩文件中的`extern_modules`列出了包在外部依赖的所有模块。这可以防止“隐式”依赖,即因为导入了本地安装的包而在本地运行,但在将包复制到另一台机器时失败。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
722

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
723
当源代码添加到包中时,导出器可以选择扫描它以获取更多的代码依赖项(`dependencies=True`)。它查找导入语句,解析相对引用以获取限定模块名称,并执行用户指定的操作(参见:`extern()``mock()``intern()`).
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
724 725

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
726
__init__(f, importer=<torch.package.importer._SysImporter object>, debug=False)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
727 728 729 730 731 732
```

创建一个导出器。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
733
+   **f** ([*Union*](https://docs.python.org/3/library/typing.html#typing.Union "(in Python v3.12)")*[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*,* [*Path*](https://docs.python.org/3/library/pathlib.html#pathlib.Path "(in Python v3.12)")*,* [*BinaryIO*](https://docs.python.org/3/library/typing.html#typing.BinaryIO "(in Python v3.12)")*]*) – 导出位置。可以是包含文件名的`string`/`Path`对象,也可以是二进制 I/O 对象。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
734

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
735
+   **importer** ([*Union*](https://docs.python.org/3/library/typing.html#typing.Union "(in Python v3.12)")*[**Importer**,* [*Sequence*](https://docs.python.org/3/library/typing.html#typing.Sequence "(in Python v3.12)")*[**Importer**]**]*) – 如果传递了单个 Importer,则使用该 Importer 搜索模块。如果传递了一系列 importers,将从中构建一个`OrderedImporter`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
736

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
737
+   **debug** ([*bool*](https://docs.python.org/3/library/functions.html#bool "(in Python v3.12)")) – 如果设置为 True,则将损坏模块的路径添加到 PackagingErrors 中。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
738 739

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
740
add_dependency(module_name, dependencies=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
741 742 743 744 745
```

给定一个模块,根据用户指定的模式将其添加到依赖图中。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
746
all_paths(src, dst)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
747 748 749 750
```

返回子图的点表示

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
751
从 src 到 dst 的所有路径。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
752 753 754

返回

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
755
包含从 src 到 dst 的所有路径的点表示。([`graphviz.org/doc/info/lang.html`](https://graphviz.org/doc/info/lang.html))
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
756 757 758 759 760 761

返回类型

[str](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
762
close()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
763 764
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
765
将包写入文件系统。`close()`之后的任何调用现在都是无效的。最好使用资源保护语法:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
766 767 768 769 770 771 772

```py
with PackageExporter("file.zip") as e:
    ... 
```

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
773
denied_modules()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
774 775 776 777 778 779 780 781 782 783 784 785 786
```

返回当前被拒绝的所有模块。

返回

包含将在此包中被拒绝的模块名称的列表。

返回类型

[*List*](https://docs.python.org/3/library/typing.html#typing.List "(in Python v3.12)")[[str](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")]

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
787
deny(include, *, exclude=())
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
788 789
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
790
从包可以导入的模块列表中阻止与给定 glob 模式匹配的模块名称。如果找到任何匹配的包的依赖项,将引发`PackagingError`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
791 792 793

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
794
+   **include** (*Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]*) – 一个字符串,例如 `"my_package.my_subpackage"`,或者字符串列表,用于指定要外部化的模块的名称。这也可以是一个类似于 glob 的模式,如`mock()`中所述。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
795 796 797 798

+   **exclude** (*Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]*) – 一个可选模式,用于排除与包含字符串匹配的某些模式。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
799
dependency_graph_string()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
800 801 802 803 804 805 806 807 808 809 810 811 812
```

返回包中依赖项的双字母字符串表示。

返回

包中依赖项的字符串表示。

返回类型

[str](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
813
extern(include, *, exclude=(), allow_empty=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
814 815 816 817 818 819
```

`module`包含在包可以导入的外部模块列表中。这将阻止依赖项发现将其保存在包中。导入程序将直接从标准导入系统加载外部模块。外部模块的代码也必须存在于加载包的进程中。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
820
+   **包括***Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*])- 一个字符串,例如`"my_package.my_subpackage"`,或者是要导出的模块的名称列表。这也可以是一个类似 glob 的模式,如`mock()`中所述。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
821

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
822
+   **排除***Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*])- 一个可选模式,排除一些与包含字符串匹配的模式。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
823

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
824
+   **allow_empty**[*bool*](https://docs.python.org/3/library/functions.html#bool "(在 Python v3.12 中)"))- 一个可选标志,指定此调用`extern`方法指定的外部模块是否必须在打包过程中与某个模块匹配。如果添加了一个`allow_empty=False`的外部模块 glob 模式,并且在任何模块匹配该模式之前调用了`close()`(显式调用或通过`__exit__`),则会抛出异常。如果`allow_empty=True`,则不会抛出此类异常。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
825 826

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
827
externed_modules()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
828 829
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
830
返回当前所有已经 externed 的模块。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
831 832 833

返回

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
834
包含在此包中将被 externed 的模块的名称列表。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
835 836 837

返回类型

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
838
[*List*](https://docs.python.org/3/library/typing.html#typing.List "(在 Python v3.12 中)")[[str](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")]
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
839 840

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
841
get_rdeps(module_name)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
842 843 844 845 846 847 848 849 850 851
```

返回一个依赖于模块`module_name`的所有模块的列表。

返回

包含依赖于`module_name`的模块的名称列表。

返回类型

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
852
[*List*](https://docs.python.org/3/library/typing.html#typing.List "(在 Python v3.12 中)")[[str](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")]
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
853 854

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
855
get_unique_id()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
856 857
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
858
获取一个 id。此 id 保证仅在此包中分配一次。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
859 860 861

返回类型

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
862
[str](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
863 864

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
865
intern(include, *, exclude=(), allow_empty=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
866 867 868 869 870 871
```

指定应该打包的模块。模块必须匹配一些`intern`模式才能包含在包中,并且其依赖项会被递归处理。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
872
+   **包括***Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*])- 一个字符串,例如“my_package.my_subpackage”,或者是要导出的模块的名称列表。这也可以是一个类似 glob 的模式,如`mock()`中所述。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
873

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
874
+   **排除***Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")*])- 一个可选模式,排除一些与包含字符串匹配的模式。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
875

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
876
+   **allow_empty**[*bool*](https://docs.python.org/3/library/functions.html#bool "(在 Python v3.12 中)"))- 一个可选标志,指定此调用`intern`方法指定的 intern 模块是否必须在打包过程中与某个模块匹配。如果添加了一个`allow_empty=False``intern`模块 glob 模式,并且在任何模块匹配该模式之前调用了`close()`(显式调用或通过`__exit__`),则会抛出异常。如果`allow_empty=True`,则不会抛出此类异常。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
877 878

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
879
interned_modules()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
880 881
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
882
返回当前所有已经 interned 的模块。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
883 884 885

返回

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
886
包含将在此软件包中 intern 的模块名称的列表。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
887 888 889 890 891 892

返回类型

[*List*](https://docs.python.org/3/library/typing.html#typing.List "(in Python v3.12)")[[str](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")]

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
893
mock(include, *, exclude=(), allow_empty=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
894 895 896 897 898 899 900 901
```

用模拟实现替换一些必需的模块。模拟的模块将为从中访问的任何属性返回一个虚假对象。因为我们是逐个文件复制的,所以依赖关系解析有时会找到由模型文件导入但其功能从未被使用的文件(例如自定义序列化代码或训练助手)。使用此函数可以模拟此功能,而无需修改原始代码。

参数

+   **include***Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]*) -

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
902
    一个字符串,例如`"my_package.my_subpackage"`,或模块名称的字符串列表。字符串也可以是匹配多个模块的 glob 样式模式字符串。与此模式字符串匹配的任何必需依赖项将自动被模拟。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
903 904 905

    示例:

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
906
    `'torch.**'` - 匹配`torch`和 torch 的所有子模块,例如`'torch.nn'`和`'torch.nn.functional'`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
907 908 909

    `'torch.*'` - 匹配`'torch.nn'`或`'torch.functional'`,但不匹配`'torch.nn.functional'`

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
910
+   **exclude***Union**[**List**[*[*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]**,* [*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*]*) - 一个可选模式,用于排除与包含字符串匹配的某些模式。例如 `include='torch.**', exclude='torch.foo'` 将模拟所有 torch 包,除了`'torch.foo'`,默认值为`[]`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
911

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
912
+   **allow_empty***bool*)- 一个可选标志,指定此调用`mock()`方法指定的模拟实现是否在打包期间必须与某个模块匹配。如果使用`allow_empty=False`添加了一个模拟,并且调用了`close()`(显式调用或通过`__exit__`),并且该模拟未与要导出的包使用的模块匹配,则会抛出异常。如果`allow_empty=True`,则不会抛出此类异常。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
913 914

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
915
mocked_modules()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
916 917 918 919 920 921 922 923 924 925 926 927 928
```

返回当前模拟的所有模块。

返回

包含将在此软件包中模拟的模块名称的列表。

返回类型

[*List*](https://docs.python.org/3/library/typing.html#typing.List "(in Python v3.12)")[[str](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")]

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
929
register_extern_hook(hook)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
930 931
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
932
在导出器上注册一个 extern 钩子。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
933

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
934
每次模块与`extern()`模式匹配时都会调用钩子。它应该具有以下签名:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950

```py
hook(exporter: PackageExporter, module_name: str) -> None 
```

钩子将按注册顺序调用。

返回

通过调用`handle.remove()`可以删除添加的钩子的句柄。

返回类型

`torch.utils.hooks.RemovableHandle`

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
951
register_intern_hook(hook)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
952 953
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
954
在导出器上注册一个 intern 钩子。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
955

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
956
每次模块与`intern()`模式匹配时都会调用钩子。它应该具有以下签名:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972

```py
hook(exporter: PackageExporter, module_name: str) -> None 
```

钩子将按注册顺序调用。

返回

通过调用`handle.remove()`可以删除添加的钩子的句柄。

返回类型

`torch.utils.hooks.RemovableHandle`

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
973
register_mock_hook(hook)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
974 975 976 977
```

在导出器上注册一个模拟钩子。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
978
每次模块与`mock()`模式匹配时都会调用钩子。它应该具有以下签名:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994

```py
hook(exporter: PackageExporter, module_name: str) -> None 
```

钩子将按注册顺序调用。

返回

可以通过调用`handle.remove()`来删除添加的钩子的句柄。

返回类型

`torch.utils.hooks.RemovableHandle`

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
995
save_binary(package, resource, binary)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
996 997 998 999 1000 1001
```

将原始字节保存到包中。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1002
+   **package**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 此资源应该放在的模块包的名称(例如`"my_package.my_subpackage"`)。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1003

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1004
+   **资源**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 用于标识要加载的资源的唯一名称。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1005

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1006
+   **二进制**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 要保存的数据。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1007 1008

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1009
save_module(module_name, dependencies=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1010 1011 1012 1013 1014 1015
```

`module`的代码保存到包中。使用`importers`路径解析模块对象的代码,然后使用其`__file__`属性查找源代码。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1016
+   **module_name**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 例如`my_package.my_subpackage`,将保存代码以提供此包的代码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1017

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1018
+   **依赖项**[*bool*](https://docs.python.org/3/library/functions.html#bool "(在 Python v3.12 中)"),可选) - 如果为`True`,我们会扫描源代码以查找依赖项。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1019 1020

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1021
save_pickle(package, resource, obj, dependencies=True, pickle_protocol=3)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1022 1023
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1024
使用 pickle 将 Python 对象保存到存档中。相当于`torch.save()`,但保存到存档而不是独立文件。标准 pickle 不保存代码,只保存对象。如果`dependencies`为 true,则此方法还将扫描 pickled 对象以确定重建它们所需的模块,并保存相关代码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1025 1026 1027 1028 1029

为了能够保存一个对象,其中`type(obj).__name__``my_module.MyObject``my_module.MyObject`必须根据`importer`顺序解析为对象的类。当保存先前已打包的对象时,导入程序的`import_module`方法将需要存在于`importer`列表中才能正常工作。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1030
+   **package**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 此资源应该放在的模块包的名称(例如`"my_package.my_subpackage"`)。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1031

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1032
+   **资源**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 用于标识要加载的资源的唯一名称。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1033

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1034
+   **obj***Any*) - 要保存的对象,必须是可 picklable 的。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1035

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1036
+   **依赖项**[*bool*](https://docs.python.org/3/library/functions.html#bool "(在 Python v3.12 中)"),可选) - 如果为`True`,我们会扫描源代码以查找依赖项。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1037 1038

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1039
save_source_file(module_name, file_or_directory, dependencies=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1040 1041 1042 1043 1044 1045
```

将本地文件系统中的`file_or_directory`添加到源包中,以提供`module_name`的代码。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1046
+   **module_name**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 例如`"my_package.my_subpackage"`,将保存代码以提供此包的代码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1047

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1048
+   **file_or_directory**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 文件或代码目录的路径。当目录时,使用`save_source_file()`递归复制目录中的所有 python 文件。如果文件命名为`"/__init__.py"`,则将代码视为包。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1049

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1050
+   **依赖项**[*bool*](https://docs.python.org/3/library/functions.html#bool "(在 Python v3.12 中)"),可选) - 如果为`True`,我们会扫描源代码以查找依赖项。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1051 1052

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1053
save_source_string(module_name, src, is_package=False, dependencies=True)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1054 1055 1056 1057 1058 1059
```

在导出的包中将`src`作为`module_name`的源代码添加。

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1060
+   **module_name**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 例如`my_package.my_subpackage`,将保存代码以提供此包的代码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1061

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1062
+   **src**[*str*](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")) - 要保存到此包的 Python 源代码。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1063 1064 1065 1066 1067 1068

+   **is_package***bool*,可选)- 如果为`True`,则将此模块视为包。允许包具有子模块(例如`my_package.my_subpackage.my_subsubpackage`),并且资源可以保存在其中。默认为`False`

+   **依赖***bool*,可选)- 如果为`True`,我们会扫描源代码以获取依赖项。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1069
save_text(package, resource, text)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
```

将文本数据保存到包中。

参数

+   **package***str*)- 此资源应该放在的模块包的名称(例如`"my_package.my_subpackage"`)。

+   **resource***str*)- 用于标识资源的唯一名称,用于加载。

+   **text***str*)- 要保存的内容。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1083
class torch.package.PackageImporter(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1084 1085
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1086
导入器允许您加载由`PackageExporter`编写的包中的代码。代码以封闭方式加载,使用包中的文件而不是正常的 python 导入系统。这允许将 PyTorch 模型代码和数据打包,以便在服务器上运行或在将来用于迁移学习。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1087

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1088
包的导入器确保模块中的代码只能从包内加载,除了在导出期间明确列出为外部模块的模块。zip 存档中的`extern_modules`文件列出了包在外部依赖的所有模块。这可以防止“隐式”依赖,其中包在本地运行,因为它正在导入一个本地安装的包,但是当包被复制到另一台机器时会失败。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1089 1090

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1091
__init__(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
```

打开`file_or_buffer`以进行导入。这将检查导入的包是否仅需要`module_allowed`允许的模块。

参数

+   **file_or_buffer***Union*[*[*str*]**PyTorchFileReader**[*Path*]**[*BinaryIO*]*])- 类似文件的对象(必须实现`read()``readline()``tell()``seek()`),一个字符串,或包含文件名的`os.PathLike`对象。

+   **module_allowed***Callable**[*[*str*]*,*bool*]*,可选)- 用于确定是否应允许外部提供的模块的方法。可以用于确保加载的包不依赖于服务器不支持的模块。默认允许任何内容。

引发

[**ImportError**](https://docs.python.org/3/library/exceptions.html#ImportError "(in Python v3.12)")- 如果包将使用不允许的模块。

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1107
file_structure(*, include='**', exclude=())
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1108 1109
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1110
返回包的 zip 文件结构表示。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1111 1112 1113

参数

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1114
+   **include***Union**[**List**[*[*str*]*]**,*[*str*]*])- 一个可选的字符串,例如`"my_package.my_subpackage"`,或者包含在 zip 文件表示中的文件名称的可选字符串列表。这也可以是一个类似 glob 的模式,如`PackageExporter.mock()`中所述。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1115 1116 1117 1118 1119

+   **exclude***Union**[**List**[*[*str*]*]**,*[*str*]*])- 一个可选的模式,用于排除与模式匹配的文件。

返回

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1120
`Directory`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1121 1122 1123

返回类型

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1124
*Directory*
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1125 1126

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1127
id()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1128 1129
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1130
返回内部标识符,torch.package 用于区分 `PackageImporter` 实例。看起来像:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1131 1132 1133 1134 1135 1136

```py
<torch_package_0> 
```

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1137
import_module(name, package=None)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156
```

如果尚未加载包中的模块,则加载该模块,然后返回模块。模块在导入程序本地加载,并将出现在 `self.modules` 而不是 `sys.modules` 中。

参数

+   **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 要加载的模块的完全限定名称。

+   **package** (*[*[*type*](https://docs.python.org/3/library/functions.html#type "(in Python v3.12)")*]**,* *optional*) – 未使用,但出现以匹配 importlib.import_module 的签名。默认为 `None`

返回

(可能已经)加载的模块。

返回类型

[types.ModuleType](https://docs.python.org/3/library/types.html#types.ModuleType "(in Python v3.12)")

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1157
load_binary(package, resource)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
```

加载原始字节。

参数

+   **package** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 模块包的名称(例如 `"my_package.my_subpackage"`)。

+   **resource** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 资源的唯一名称。

返回

加载的数据。

返回类型

[bytes](https://docs.python.org/3/library/stdtypes.html#bytes "(in Python v3.12)")

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1177
load_pickle(package, resource, map_location=None)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1178 1179
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1180
从包中反序列化资源,加载任何需要构造对象的模块,使用 `import_module()`
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198

参数

+   **package** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 模块包的名称(例如 `"my_package.my_subpackage"`)。

+   **resource** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 资源的唯一名称。

+   **map_location** – 传递给 torch.load 以确定张量如何映射到设备。默认为 `None`

返回

反序列化的对象。

返回类型

任何

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1199
load_text(package, resource, encoding='utf-8', errors='strict')
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
```

加载字符串。

参数

+   **package** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 模块包的名称(例如 `"my_package.my_subpackage"`)。

+   **resource** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 资源的唯一名称。

+   **encoding** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*,* *optional*) – 传递给 `decode`。默认为 `'utf-8'`

+   **errors** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")*,* *optional*) – 传递给 `decode`。默认为 `'strict'`

返回

加载的文本。

返回类型

[str](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1223
python_version()
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
```

返回用于创建此包的 python 版本。

注意:此函数是实验性的,不具有向前兼容性。计划将其稍后移入锁定文件。

返回

`Optional[str]` 一个 python 版本,例如 3.8.9,如果没有存储与此包相关的版本,则为 None

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1235
class torch.package.Directory(name, is_dir)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1236 1237
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1238
文件结构表示。组织为具有其 Directory 子节点列表的 Directory 节点。通过调用 `PackageImporter.file_structure()` 来创建包的目录。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1239 1240

```py
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1241
has_file(filename)
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1242 1243
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1244
检查文件是否存在于 `Directory` 中。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1245 1246 1247 1248 1249 1250 1251

参数

**filename** ([*str*](https://docs.python.org/3/library/stdtypes.html#str "(in Python v3.12)")) – 要搜索的文件路径。

返回

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1252
如果 `Directory` 包含指定的文件。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1253 1254 1255 1256

返回类型

[bool](https://docs.python.org/3/library/functions.html#bool "(in Python v3.12)")