首页 > 编程语言 >Python模块和包

Python模块和包

时间:2024-01-26 17:23:15浏览次数:27  
标签:__ Python py pkg 模块 import foo

目录

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!

标签:__,Python,py,pkg,模块,import,foo
From: https://www.cnblogs.com/zh-geek/p/17989825

相关文章

  • Python_numpy-增加以及修改维度
    gradio组件输入组件-输出组件输入输出组件 多输入和多输出组件gr.State是一个不可见的组件,目的是在后台存储一些变量方便访问和交互BlockcomponentsTextbox:interactiveinteractive=TrueEventlistenerchange()e......
  • python之常用标准库-random
    1.randomdefrandom(self):"""Getthenextrandomnumberintherange[0.0,1.0)."""return(int.from_bytes(_urandom(7),'big')>>3)*RECIP_BPF翻译:获取0,1之间的随机浮点数1#!/usr/bin/python2importrandom3p......
  • Python中为何使用新语法而不是装饰器来实现async/await异步功能
    Python是一种多范式编程语言,通过引入新的语法和特性,不断提升其功能和灵活性。在异步编程领域,Python引入了async/await关键字来实现协程和异步操作,而不是使用已有的装饰器语法。本文将探讨为何Python选择引入新语法来实现async/await异步功能,以及与装饰器的区别和优势。一、理解异步......
  • 在 Python 中如何实现列表元素去重
    在日常的Python编程中,我们经常需要对列表进行去重操作,以便保证程序的正确性和效率。Python提供了多种方法来实现列表元素去重,本文将介绍其中的四种方法。一、使用set()函数set()函数是Python内置的去重函数,它可以使列表中的元素不重复,并将其转换为集合类型,最后再转换回列表类型。具......
  • 使用 Python 的 Paramiko 库实现远程文件复制
    本文将介绍如何使用Paramiko库在Python中实现远程访问并复制文件到本地。Paramiko是一个用于SSHv2协议的Python实现,它提供了简单而强大的功能来进行远程操作。我们将学习如何建立SSH连接、执行远程命令以及复制文件到本地。一、安装Paramiko首先,我们需要安装Paramiko库。可以使用pi......
  • Python 多线程的局限性及适用场景解析
     Python是一门功能强大且广泛应用的编程语言,然而在使用多线程方面,它存在一些局限性。本文将探讨Python多线程的局限性,并分析其适用场景,帮助读者更好地理解Python多线程的实际运用。 正文: 一、Python的全局解释器锁(GIL) Python的全局解释器锁(GlobalInterpreterLock,简称GIL)是P......
  • itop-RK3588开发板机器视觉开发OpenCV-Python的安装
    由于 iTOP-RK3588 编译安卓和 Linux 源码使用的 ubuntu 版本为 ubuntu20.04,为了方便和统一,本手册的实验环境也为 Ubuntu20.04,如果使用的是其他版本的 ubuntu。可能会存在一些细微的区别,建议大家所使用的 ubuntu 版本和我们保持一致。使用以下命令安装 OpenCV-Python,安......
  • itop-RK3588开发板机器视觉开发OpenCV-Python的安装
    由于 iTOP-RK3588 编译安卓和 Linux 源码使用的 ubuntu 版本为 ubuntu20.04,为了方便和统一,本手册的实验环境也为 Ubuntu20.04,如果使用的是其他版本的 ubuntu。可能会存在一些细微的区别,建议大家所使用的 ubuntu 版本和我们保持一致。使用以下命令安装 OpenC......
  • Python3 md5
    Python3md5MD5信息摘要算法(英语:MD5Message-DigestAlgorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hashvalue),用于确保信息传输完整一致。在python3的标准库中,已经移除了md5模块,而关于hash加密算法都放在hashlib这个标准库中,hashlib提供了常见的摘要......
  • python之常用标准库-时间
    1.time时间戳:它代表了从格林尼治时间1970年01月01日00时00分00秒(即北京时间的1970年01月01日08时00分00秒)开始到现在经过的总秒数。struct_time:用一个包含9个序列的元组组成(tm_year=2024,tm_mon=1,tm_mday=26,tm_hour=2,tm_min=49,tm_sec=56,tm_wday=4,tm_yday=26,......