Python模块和包

Python模块和包

Python模块概述

在Python中定义模块有三种不同的方式:

  1. 用Python本身编写的模块;

  2. 用C语言编写,并在运行时动态加载,例如re模块;

  3. 内置模块,本质上包含在解释器中,如itertools模块

使用这三种模块的方式都是相同的,就是使用import语句。

在本文中,我们主要讨论用Python语言本身编写模块。用Python编写模块非常简单。你需要做的就是创建一个包含合法Python代码的文件,然后给文件取一个以.py结尾的文件名。仅此而以!不需要任何特殊的语法。

例如,假设您创建了一个名为mod.py的文件,其中包含以下内容:

s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

mod.py中定义了如下几个对象:

  • s (一个字符串)
  • a (一个列表)
  • foo() (一个函数)
  • Foo (一个类)

假设mod.py位于合适的位置(您很快就会了解到,什么是“合适的位置”)就可以通过下面的方式导入模块来访问这些对象:

>>> import mod
>>> print(mod.s)
If Comrade Napoleon says it, it must be right.
>>> mod.a
[100, 200, 300]
>>> mod.foo(['quux', 'corge', 'grault'])
arg = ['quux', 'corge', 'grault']
>>> x = mod.Foo()
>>> x
<mod.Foo object at 0x03C181F0>

模块搜索路径

继续上面的例子,让我们看看 Python 执行下面的语句时会发生什么:

import mod

当解释器执行上述 import 语句时,它会在下列目录列表中搜索 mod.py

  • 被运行的脚本所处的目录,如果以交互方式运行解释器,则为当前目录;

  • 环境变量PYTHONPATH中包含的目录列表。(PYTHONPATH的格式取决于操作系统,但应与 环境变量PATH一致);

  • 安装 Python 时配置的依赖于安装的目录列表。

最终的搜索路径可以通过Python中的变量sys.path进行访问,该变量从名为sys的模块中获得:

>>> import sys
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages']

注意 sys.path的确切内容与安装有关。上面的内容在您的计算机肯定会略有不同。

因此,要确保能够找到您的模块,您需要执行以下操作之一:

  • mod.py放在与脚本相同的目录或当前目录(如果是交互运行的话);

  • 在启动解释器之前,修改环境变量PYTHONPATH,将mod.py所处的目录包含在其中;

    • 或者,将mod.py保存在环境变量PYTHONPATH包包含的目录的其中之一;
  • mod.py放在一个与安装相关的目录中,根据操作系统的不同,你可能有也可能没有对该目录的写权限。

实际上,还有一个额外的办法:你可以将模块文件放在任何目录中,然后在运行时修改sys.path变量的值,使其包含保存模块的目录。例如,在本例中,您可以将mod.py放在目录C:\Users\John中,然后执行以下语句:

>>> sys.path.append(r'C:\Users\john')
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'C:\\Users\\john']
>>> import mod

导入模块后,您可以使用模块的__FILE__属性来确定该模块的处置:

>>> import mod
>>> mod.__file__
'C:\\Users\\john\\mod.py'

>>> import re
>>> re.__file__
'C:\\Python36\\lib\\re.py'

__FILE__的目录部分应该是sys.path中的目录之一。

import语句

模块的内容可以通过import语句导入给调用方。import语句可以有多种不同的形式,如下所示:

import <module_name>

这是最简单的形式。

请注意,这不会使得调用者可以直接访问模块的内容。每个模块都有自己的私有符号表,作为模块中定义的所有对象的全局符号表。因此,如前所述,模块创建了一个单独的命名空间。

语句import <module_name>仅仅是把<module_name>放进了调用者的符号表中。模块中定义的对象仍然保留在模块自己的私有符号表中。

以调用者的角度看,模块中的对象只有通过module_name.object的形式才能访问,如下所示:

下面的import语句执行后,mod被放入了本地符号表中。因此,mod在调用者的本地环境中具有含义:

>>> import mod
>>> mod
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>

但是sFoo仍然保留在模块的私有符号表中,在本地环境中没有意义:

>>> s
NameError: name 's' is not defined
>>> foo('quux')
NameError: name 'foo' is not defined

要从本地环境中进行访问模块中定义的对象,必须以mod作为前缀:

>>> mod.s
'If Comrade Napoleon says it, it must be right.'
>>> mod.foo('quux')
arg = quux

可以在一条import语句中导入多个以逗号分隔的模块:

import <module_name>[, <module_name> ...]

from <module_name> import <name(s)>

import语句的另一种形式允许将模块中的单个对象直接导入到调用者的本地符号表中:

>>> from mod import s, foo
>>> s
'If Comrade Napoleon says it, it must be right.'
>>> foo('quux')
arg = quux

>>> from mod import Foo
>>> x = Foo()
>>> x
<mod.Foo object at 0x02E3AD50>

由于这种形式的导入会将对象的名称直接放入调用者的符号表中,因此已存在的任何同名对象都将被覆盖掉:

>>> a = ['foo', 'bar', 'baz']
>>> a
['foo', 'bar', 'baz']

>>> from mod import a
>>> a
[100, 200, 300]

甚至可以不加区别地一口气从一个模块导入所有内容:

from <module_name> import *

这将把<module_name>中的所有对象的名称放置到本地符号表中,以下划线(_)字符开头的对象除外。

例如:

>>> from mod import *
>>> s
'If Comrade Napoleon says it, it must be right.'
>>> a
[100, 200, 300]
>>> foo
<function foo at 0x03B449C0>
>>> Foo
<class 'mod.Foo'>

在大规模的生产代码中不推荐这样做。这有点危险,因为您是成批地在本地符号表中输入名称。除非你对它们相当了解,并且确信不会发生冲突,否则你很有可能会不经意间覆盖掉现有的名字。但是,当您出于测试或探索的目的而使用交互式解释器时,这种语法非常方便,因为它使您可以快速访问模块必须提供的所有内容,而无需大量输入。

from <module_name> import as <alt_name>

您也可以导入单个对象,但是将它们导入到本地符号表中并使用备用名称:

from <module_name> import <name> as <alt_name>[, <name> as <alt_name> ...]

这使得可以将名称直接放入本地符号表中,但避免了与当前存在的名称冲突:

>>> s = 'foo'
>>> a = ['foo', 'bar', 'baz']

>>> from mod import s as string, a as alist
>>> s
'foo'
>>> string
'If Comrade Napoleon says it, it must be right.'
>>> a
['foo', 'bar', 'baz']
>>> alist
[100, 200, 300]

import <module_name> as <alt_name>

您还可以使用备用名称导入整个模块:

>>> import mod as my_module
>>> my_module.a
[100, 200, 300]
>>> my_module.foo('qux')
arg = qux

模块内容可以从函数定义中导入。在这种情况下,直到调用函数时才会进行导入:

>>> def bar():
...     from mod import foo
...     foo('corge')
...

>>> bar()
arg = corge

但是,Python 3 不允许在函数内部不加区别地使用import *语法:

>>> def bar():
...     from mod import *
...
SyntaxError: import * only allowed at module level

最后,可以使用带有except ImportError子句的try语句来防止不成功的导入尝试:

>>> try:
...     # Non-existent module
...     import baz
... except ImportError:
...     print('Module not found')
...

Module not found
>>> try:
...     # Existing module, but non-existent object
...     from mod import baz
... except ImportError:
...     print('Object not found in module')
...

Object not found in module

dir()函数

内置的dir()函数返回命名空间中已定义名称的列表。在没有参数的情况下,它会生成当前本地符号表中按字母排序的名称列表:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> qux = [1, 2, 3, 4, 5]
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'qux']

>>> class Bar():
...     pass
...
>>> x = Bar()
>>> dir()
['Bar', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'qux', 'x']

请注意,上面对dir()的第一次调用列出了几个在解释器启动时已在名称空间中自动定义的名称。定义新名称(quxBarx)时,它们会出现在后续的dir()调用中。

这对于确定import语句到底向命名空间添加了什么内容非常有用:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> import mod
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod']
>>> mod.s
'If Comrade Napoleon says it, it must be right.'
>>> mod.foo([1, 2, 3])
arg = [1, 2, 3]

>>> from mod import a, Foo
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'mod']
>>> a
[100, 200, 300]
>>> x = Foo()
>>> x
<mod.Foo object at 0x002EAD50>

>>> from mod import s as string
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'mod', 'string', 'x']
>>> string
'If Comrade Napoleon says it, it must be right.'

当给定一个模块名称作为参数时,dir()会列出该模块中定义的名称:

>>> import mod
>>> dir(mod)
['Foo', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'a', 'foo', 's']
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from mod import *
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'foo', 's']

将模块作为脚本执行

任何包含模块的.py文件本质上也是一个Python脚本,没有任何理由不能像脚本一样运行它。

下面是之前所定义的mod.py的副本:

s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

它可以作为脚本去运行:

C:\Users\john\Documents> python mod.py
C:\Users\john\Documents>

没有错误发生,所以,它显然是有效的。虽然不是很有趣。脚本只定义了对象,但是没有对这些对象做任何事情,也不会产生任何输出。

让我们修改上面的Python模块,以便它在作为脚本运行时能够生成一些输出:

s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

print(s)
print(a)
foo('quux')
x = Foo()
print(x)

现在事情变得更有趣了:

C:\Users\john\Documents>python mod.py
If Comrade Napoleon says it, it must be right.
[100, 200, 300]
arg = quux
<__main__.Foo object at 0x02F101D0>

不幸的是,现在它作为模块被导入时仍然会产生输出:

>>> import mod
If Comrade Napoleon says it, it must be right.
[100, 200, 300]
arg = quux
<mod.Foo object at 0x0169AD50>

这可能不是你想要的。模块在导入时产生输出是不常见的。

如果您能够区分文件何时作为模块被加载,以及何时作为独立脚本被运行,这不是很好吗?

有求必应。

当一个.py文件作为模块被导入时,Python会将特殊的变量__name__设置为模块的名字。但是,如果文件作为独立脚本运行时,__name__将(创造性地)设置为字符串__main__。基于这一事实,您可以在运行时识别到底是哪种情况,并相应地改变行为:

s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

if (__name__ == '__main__'):
    print('Executing as standalone script')
    print(s)
    print(a)
    foo('quux')
    x = Foo()
    print(x)

现在,如果你作为一个脚本运行,你会得到输出:

C:\Users\john\Documents>python mod.py
Executing as standalone script
If Comrade Napoleon says it, it must be right.
[100, 200, 300]
arg = quux
<__main__.Foo object at 0x03450690>

但如果您将其作为模块导入,则不会有输出:

>>> import mod
>>> mod.foo('grault')
arg = grault

模块通常被设计成能够作为独立脚本运行,以测试模块中包含的功能。这称为单元测试。例如,假设您创建了一个包含阶乘函数的模块fact.py,如下所示:

def fact(n):
    return 1 if n == 1 else n * fact(n-1)

if (__name__ == '__main__'):
    import sys
    if len(sys.argv) > 1:
        print(fact(int(sys.argv[1])))

可以将文件作为模块导入,并导入fact()函数:

>>> from fact import fact
>>> fact(6)
720

但也可以通过在命令行上传递一个整型参数来作为独立程序运行以进行测试:

C:\Users\john\Documents>python fact.py 6
720

重新加载模块

出于效率的原因,一个模块在每个解释器会话中只会加载一次。这对于函数和类定义来说很好,它们通常构成模块的大部分内容。但模块也可以包含可执行语句,通常用于初始化。请注意,只有在第一次导入模块时才会执行这些语句。

请看下面的mod.py文件:

a = [100, 200, 300]
print('a =', a)
>>> import mod
a = [100, 200, 300]
>>> import mod
>>> import mod

>>> mod.a
[100, 200, 300]

在后续的导入中,print()语句并不会执行。(其实赋值语句也没有执行)

如果您对模块进行了更改并需要重新加载它,则需要重新启动解释器或使用importlib中名为reload()的函数:

>>> import mod
a = [100, 200, 300]

>>> import mod

>>> import importlib
>>> importlib.reload(mod)
a = [100, 200, 300]
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>

Python包

假设您开发了一个包含许多模块的大型应用程序。随着模块数量的增加,如果它们被转储到另外一个位置,则跟踪所有模块是很困难的。如果它们具有相似的名称或功能,情况尤其如此。您可能希望有一种对它们进行分组和组织的方法。

包允许使用.符号对模块命名空间进行分层组织。模块有助于避免全局变量名之间的冲突,同样,包也有助于避免模块名之间的冲突。

创建包非常简单,因为它利用了操作系统固有的分层文件结构。请考虑以下目录结构:

pkg/
  ├── mod1.py
  └── mod2.py

这里有一个名为pkg的目录,其中包含两个模块:mod1.pymod2.py。这些模块的内容包括:

# mod1.py
def foo():
    print('[mod1] foo()')

class Foo:
    pass
# mod2.py
def bar():
    print('[mod2] bar()')

class Bar:
    pass

根据这种结构,如果pkg目录位于可以找到它的位置(在sys.path中包含的目录之一中),则可以使用点符号(pkg.mod1pkg.mod2)引用这两个模块,并使用您已经熟悉的语法导入它们:

>>> import pkg.mod1, pkg.mod2
>>> pkg.mod1.foo()
[mod1] foo()
>>> x = pkg.mod2.Bar()
>>> x
<pkg.mod2.Bar object at 0x033F7290>
>>> from pkg.mod1 import foo
>>> foo()
[mod1] foo()
>>> from pkg.mod2 import Bar as Qux
>>> x = Qux()
>>> x
<pkg.mod2.Bar object at 0x036DFFD0>

您还可以使用以下语句导入模块:

>>> from pkg import mod1
>>> mod1.foo()
[mod1] foo()

>>> from pkg import mod2 as quux
>>> quux.bar()
[mod2] bar()

从技术上讲,您也可以直接导入该包:

>>> import pkg
>>> pkg
<module 'pkg' (namespace)>

但是这没有什么作用。尽管严格地说,这是一条语法正确的Python语句,但它并没有做什么有用的事情。特别是,它没有将pkg中的任何模块加载本地命名空间中:

>>> pkg.mod1
Traceback (most recent call last):
  File "<pyshell#34>", line 1, in <module>
    pkg.mod1
AttributeError: module 'pkg' has no attribute 'mod1'
>>> pkg.mod1.foo()
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    pkg.mod1.foo()
AttributeError: module 'pkg' has no attribute 'mod1'
>>> pkg.mod2.Bar()
Traceback (most recent call last):
  File "<pyshell#36>", line 1, in <module>
    pkg.mod2.Bar()
AttributeError: module 'pkg' has no attribute 'mod2'

要实际导入模块或其内容,您需要使用上面所示的形式之一。

包初始化

如果包目录中存在名为__init__.py的文件,则在导入包或包中的模块时会调用该文件。这可用于执行程序包初始化代码,如程序包级数据的初始化。

例如,考虑以下__init__.py文件:

print(f'Invoking __init__.py for {__name__}')
A = ['quux', 'corge', 'grault']

让我们将它添加到pkg目录中:

pkg/
  ├── __init__.py
  ├── mod1.py
  └── mod2.py

现在,当导入包时,全局列表A被初始化:

>>> import pkg
Invoking __init__.py for pkg
>>> pkg.A
['quux', 'corge', 'grault']

包中的模块可以通过依次导入全局变量来访问它:

# mod1.py
def foo():
    from pkg import A
    print('[mod1] foo() / A = ', A)

class Foo:
    pass
>>> from pkg import mod1
Invoking __init__.py for pkg
>>> mod1.foo()
[mod1] foo() / A =  ['quux', 'corge', 'grault']

__init__.py还可以实现从包中自动导入模块。例如,前面您已经看到,import pkg语句只将名称pkg放在调用方的本地符号表中,而不会导入任何模块。但如果pkg目录中的__init__.py包含以下内容:

print(f'Invoking __init__.py for {__name__}')
import pkg.mod1, pkg.mod2

然后在执行导入pkg时,会自动导入模块mod1mod2

>>> import pkg
Invoking __init__.py for pkg
>>> pkg.mod1.foo()
[mod1] foo()
>>> pkg.mod2.bar()
[mod2] bar()

注意:许多Python文档都指出,在创建包时,包目录中必须存在__init__.py文件。这曾经是真的。过去,__init__.py的出现向Python表示正在定义一个包。该文件可能包含初始化代码,甚至可能是空的,但它必须存在。

从Python3.3开始,引入了隐式命名空间包。它允许创建没有任何__init__.py文件的包。当然,如果需要包初始化,它仍然可以存在。但它不再是必需的。了解什么是Python命名空间包,它的用途是什么?了解更多信息。

从包不import *

出于以下讨论的目的,我对之前定义的包进行了扩展,以包含一些附加模块:

pkg/
  ├── mod1.py
  ├── mod2.py
  ├── mod3.py
  └── mod4.py

pkg目录中现在定义了四个模块。其内容如下:

# mod1.py
def foo():
    print('[mod1] foo()')

class Foo:
    pass
# mod2.py
def bar():
    print('[mod2] bar()')

class Bar:
    pass
# mod3.py
def baz():
    print('[mod3] baz()')

class Baz:
    pass
# mod4.py
def qux():
    print('[mod4] qux()')

class Qux:
    pass

(很有想象力,不是吗?)

您已经看到,当对模块使用import * 时,模块中的所有对象都将导入到本地符号表中,除了名字以下划线开头的对象,一如既往:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg.mod3 import *

>>> dir()
['Baz', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'baz']
>>> baz()
[mod3] baz()
>>> Baz
<class 'pkg.mod3.Baz'>

包的类似语句是这样的:

from <package_name> import *

这有什么作用?

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

Hmph.没什么。你可能会期望 Python 会潜入软件包目录,找到它能找到的所有模块,并将它们全部导入。但如您所见,默认情况下并非如此。

相反,Python 遵循这样的约定:如果软件包目录中的 __init__.py 文件包含一个名为 __all__ 的列表,那么当遇到from <package_name> import * 的语句时,它就被认为是一个应该被导入的模块列表。

# pkg/__init__.py

__all__ = ['mod1', 'mod2', 'mod3', 'mod4']

from pkg import * 导入所有四个模块:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod1', 'mod2', 'mod3', 'mod4']
>>> mod2.bar()
[mod2] bar()
>>> mod4.Qux
<class 'pkg.mod4.Qux'>

使用import *仍然被认为不好的形式,对于包和模块都是如此。但是这个工具至少让包的创建者在一定程度上控制了当指定import *时抽发生的事情。(事实上,它提供了完全禁止它的能力,只需拒绝定义__all__。如您所见,包的默认行为是不导入任何内容。)

顺便说一句,__all__也可以在模块中定义,并且服务于相同的目的:控制使用import *导入的内容。例如,修改mod1.py如下:

__all__ = ['foo']

def foo():
    print('[mod1] foo()')

class Foo:
    pass

现在,from pkg.mod1 import *只导入__all__中包含的内容:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg.mod1 import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'foo']

>>> foo()
[mod1] foo()
>>> Foo
Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    Foo
NameError: name 'Foo' is not defined

foo()(函数)现在在本地名称空间中定义,但Foo(类)没有定义,因为后者不在__all__当中。

总之,包和模块都使用__all__来控制指定import *时导入的内容。但默认行为有所不同:

  • 对于包,如果未定义__all__import *不会导入任何内容。

  • 对于模块,当没有定义 __all__ 时,import * 导入所有内容(除了--你猜对了--以下划线开头的名字)。

子包

包可以包含任意深度的嵌套子包。例如,我们再对示例包目录进行一次修改,如下所示:

pkg/
  ├── sub_pkg1/
  │      ├── mod1.py
  │      └── mod2.py
  └── sub_pkg2/
         ├── mod3.py
         └── mod4.py

四个模块(mod1.pymod2.pymod3.pymod4.py)的定义与前面一样。但现在,它们不是集中在pkg目录中,而是拆分到两个子包目录中:sub_pkg1sub_pkg2

导入的工作原理仍与前面所示相同。语法类似,但使用附加点表示法将包名和子包名分开:

>>> import pkg.sub_pkg1.mod1
>>> pkg.sub_pkg1.mod1.foo()
[mod1] foo()

>>> from pkg.sub_pkg1 import mod2
>>> mod2.bar()
[mod2] bar()

>>> from pkg.sub_pkg2.mod3 import baz
>>> baz()
[mod3] baz()

>>> from pkg.sub_pkg2.mod4 import qux as grault
>>> grault()
[mod4] qux()

此外,一个子包中的模块可以引用同级子包中的对象(如果同级包含您需要的某些功能)。例如,假设您想要从模块mod3中导入并执行函数foo()(在模块mod1中定义)。您可以使用绝对导入:

# pkg/sub__pkg2/mod3.py
def baz():
    print('[mod3] baz()')

class Baz:
    pass

from pkg.sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
[mod1] foo()
>>> mod3.foo()
[mod1] foo()

或者,您可以使用相对导入,其中..指的是上一级的包。

  • ..表示父包(pkg)

  • ..sub_pkg1 表示父包的sub_pkg1子包

# pkg/sub__pkg2/mod3.py
def baz():
    print('[mod3] baz()')

class Baz:
    pass

from .. import sub_pkg1
print(sub_pkg1)

from ..sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
<module 'pkg.sub_pkg1' (namespace)>
[mod1] foo()

结论

在本教程中,您尝到了以下内容:

  • 如何创建一个Python模块

  • Python解释器如何搜索模块的位置

  • 如何使用 import 语句访问模块中定义的对象

  • 如何创建可作为独立脚本执行的模块

  • 如何将模块组织成包和子包

  • 如何控制包的初始化

希望这能让您更好地理解如何访问 Python 中许多第三方模块和内置模块提供的功能。

此外,如果您正在开发自己的应用程序,创建自己的模块和包将有助于您组织和模块化代码,从而使编码、维护和调试变得更容易。

如果您想了解更多,请查看 Python.org 上的以下文档:

Happy Pythoning!

posted @ 2024-01-26 17:17  fmcdr  阅读(9)  评论(0编辑  收藏  举报