天天看点

Python module

Python 模块导入

请注意,通常不赞成*从模块或包中导入的做法,因为它通常会导致代码可读性差。但是,可以使用它来保存交互式会话中的输入。

import fibo    # 隐式相对导入
from fibo import fibo1, fibo2    # 绝对路径导入
import fibo as fib    # 重命名
from fibo import fib as fibonacci
      

笔记

出于效率原因,每个模块在每个解释器会话中仅导入一次。因此,如果您更改您的模块,您必须重新启动解释器——或者,如果它只是您想要交互式测试的一个模块,请使用​

​importlib.reload()​

​,例如.

import importlib; 
importlib.reload(modulename)
      

​from package import item​

​,item可以是package的子module,也可以是子package, 还可以是其他各种定义在这个包里面的变量、类和函数。

​import item.subitem.subsubitem​

​ ,这种语法则要求最后一层item必须是子package,或者子module,不能是item中定义的变量、类或者函数。如果最后一层是一个变量、类或者函数,则在编译改文件的时候会报错ModuleNotFoundError: No module named 'xxx'; 'xxx' is not a package

from <module> import *

包外的可见效是通过在包里面的__init.py__里面设置__all__来控制包内的对象和方法等对包外的可见性,如:如果设置了__all__,如果这个包内的类或方法不在__all__里面,外面的包就不能正常使用这些方法和类。如果没有设置__all__,那么外面可以通过包内的约定来显示可见性。python通过private对应两个下划线__,protected对应一个下划线_,public 对应没有下划线,来约定类似于java的可见性控制。

__all__作用是是限制​

​from <module> import *​

​中​

​import​

​的包名。需要注意的是 ​

​__all__​

​ 只影响到了 ​

​from <module> import *​

​ 这种导入方式,对于 ​

​from <module> import <member>​

​ 导入方式并没有影响,仍然可以从外部导入。

all__如果在module文件中,则限制的是module中变量、类和函数的可见性,所以这种情况下__all__变量的元素都是变量名、类名和函数名;如果是在__init.py中,则设置的是该package下的module的可见性,这种情况下,__all__变量的元素都是module名。

├──__init__.py
├── package1
│ ├── __init__.py
│ ├── foo.py 
│ ├── foo1.py
│ └── foo2.py
└──example.py
      

每个文件的内容分别如下

package1.init.py,在__all__变量中只定义了"foo1"和"foo2"两个module,当使用​

​from package1 import *​

​导入package1下的module时,只会导入foo1和foo2两个module

__all__ = ["foo1", "foo2"]
      

package1.foo.py

ccc = 5
bar = 10
def baz(): return 'baz'
      

package1.foo1.py

aaa = 123
str = "aaa"
      

package1.foo2.py

bbb = 456
      

example.py

from package1 import *

if __name__ == "__main__":
    print(foo1.aaa)
    print(foo2.bbb)
    print(foo.ccc)    # 报错,NameError: name 'foo' is not defined
      

因为​

​__all__​

​from <module> import *​

​from <module> import <member>​

​ 导入方式并没有影响,仍然可以从外部导入。所以我们可以使用​

​from package1 import foo​

​来导入foo这个module。

example.py改成如下,即不会报错。

from package1 import *
from package1 import foo

if __name__ == "__main__":
    print(foo1.aaa)
    print(foo2.bbb)
    print(foo.ccc)    # 不报错
      

相对导入

from . import echo    # 表示从当前文件所在目录导入echo这个module
from .. import formats    # 表示从当前文件所在目录的上层目录导入formats这个package或者moudle
from ..filters import equalizer # 表示从当前文件所在目录的上层目录的filters这个子package或者子module中导入equalizer
      

相对导入基于当前模块的名称。由于主模块的名称始终为​

​"__main__"​

​,因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。主模块所在文件夹不会被视作package,因此除了主模块外,与主模块处在同个文件夹的模块(也就是同级的模块)也必须使用绝对导入。

关于相对导入,建议阅读,能让你对相对导入有一个深刻的认识,再也不用怕相对导入引起的编译失败了。

将模块作为脚本执行

fibo.py

# Fibonacci numbers module
def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))
      

脚本中的__main__="__name"下的代码仅在模块作为“主”文件执行时才运行:

$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34
      

如果module是作为导入的模块,则不会执行该模块的__main__代码:

>>>import fibo
>>>
      

这通常用于为模块提供方便的用户界面,或用于测试目的(将模块作为脚本执行测试套件运行)。

模块搜索路径

当import一个module时,比如spam,解释器首先搜索具有该名称的内置模块。如果未找到,它将从​

​sys.path​

​这个目录列表中搜索spam.py这个文件。 ​

​sys.path​

​初始状态一般由三部分组成:python正在执行的脚本的目录,PYTHONPATH路径,包的默认安装路径。

在支持符号链接的文件系统上,包含输入脚本的目录是在符号链接之后计算的。换句话说,包含符号链接的目录不会添加到模块搜索路径中。

初始化后,Python 程序可以修改​

​sys.path​

​. 包含正在运行的脚本的目录位于搜索路径的开头,在标准库路径之前。这意味着将加载该目录中的脚本而不是库目录中的同名模块。最好不要有和内置模块相同的名字的module, 因为当前文件夹的module在被搜索时会被优先搜索。

“已编译”的 Python 文件

我们在运行python文件后经常能看到被运行的文件的目录下增加了一个__pycache__文件夹,里面多了很多.pyc文件。以前可能不太清楚这些.pyc文件是什么,重要不重要,能不能随便删除以及这些文件是怎么产生的等等疑问。现在看完这个章节就完全了解了__pycache__这个神秘的文件夹。

为了加速模块载入,Python会把每个模块的编译后版本存在在 pycache 目录中,每个module的命名格式为 moduleName.cpython-version.pyc ,其中名称中的version是python的版本号。例如,在CPython版本3.3中,spam.py的编译版本将被缓存为 pycache/spam.cpython-33.pyc。因为带上了版本号,所以不同版本的python的已编译模块可以共存。

Python根据编译版本检查源的修改日期,以查看它是否已过期并判断需要重新编译。这是一个完全自动化的过程。

Python在两种情况下不会检查缓存。首先,对于从命令行直接载入的模块,它从来都是重新编译并且不存储编译结果;其次,如果没有源模块,它不会检查缓存。为了支持无源文件(仅编译)发行版本, 编译模块必须是在源目录下,并且绝对不能有源模块。

  • 程序从​

    ​.pyc​

    ​ 文件中读取时的运行速度并不比从文件中读取时快​

    ​.py​

    ​;唯一比​

    ​.pyc​

    ​文件更快的是它们的加载速度。

dir()函数

dir()函数会找到并列出一个module定义的所有函数、变量和类。

>>> import fibo, sys
 >>> dir(fibo)
['__name__', 'fib', 'fib2']
 >>> dir(sys)  ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
 '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
 '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
 '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
 'warnoptions']
      

如果dir()函数的参数为空,会默认列出当前已经定义的所有变量、函数名和类。

>>> import fibo
 >>> fib = fibo.fib
 >>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
      

​dir()​

​默认不列出内置函数和变量的名称。所有内置函数的名称和变量都定义在标准模块​

​builtins​

​中。

>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
      

可以看到,很多我们过去使用过的函数其实都是内置函数,包括len, list, int, print,pow等等。

参考: ​​6.模块​​