python模块化
模块化
一般来说,编程语言中,库、包、模块是同一种概念,是代码组织方式。
Python中只有一种模块对象类型,但是为了模块化组织模块的便利,提供了”包”的概念。
模块module:指的是Python的源代码文件。
包package:指的是模块组织在一起的和包名同名的目录及其相关文件
导入语句语句
语句 | 含义 |
---|---|
import 模块1[,模块2,…] | 完全导入 |
import … as … | 模块别名 |
import 语句
- 找到指定的模块,加载和初始化它,生成模块对象。找不到抛出异常
- 在import所在的作用域的局部命名空间中,增加名称和上一步创建的对象关联
单独运行下面例子,体会区别:
import functools #导入模块
print(dir())
print(functools , type(functools))
print(functools.wraps)
#输出
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'functools']
<module 'functools' from '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py'> <class 'module'>
<function wraps at 0x7fdc700f45e0>
import os.path #导入os.path,os加入当前名词空间
print(dir())
print(os,type(os))
print(os.path) #完全限定名称访问path
#输出
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'os']
<module 'os' from '/usr/local/bin/../../../Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/os.py'> <class 'module'>
<module 'posixpath' from '/usr/local/bin/../../../Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/posixpath.py'>
import os.path as osp #导入os.path并赋给osp
print(dir())
print(osp)
import sys
import os.path
import test
def testimport():
import os.path
print(dir())
testimport()
print(globals().keys())
print(locals().keys())
print(dir())
print(*reversed(sys.modules.keys()))
#输出
['os']
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'sys', 'os', 'test', 'testimport'])
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'sys', 'os', 'test', 'testimport'])
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'os', 'sys', 'test', 'testimport']
test site _distutils_hack re copyreg sre_compile sre_parse sre_constants _sre enum _virtualenv importlib.util contextlib importlib.abc importlib.machinery importlib warnings types importlib._bootstrap_external importlib._bootstrap functools _functools collections _collections reprlib itertools heapq _heapq keyword operator _operator _bootlocale _locale _sitebuiltins os os.path posixpath genericpath _collections_abc stat _stat io abc _abc encodings.latin_1 __main__ _signal encodings.utf_8 encodings encodings.aliases codecs _codecs zipimport time _weakref _thread _frozen_importlib_external posix marshal _io _warnings _imp _frozen_importlib builtins sys
Process finished with exit code 0
总结:
- 导入顶级模块,其名称会加入到本地名词空间中,并绑定到其模块对象
- 导入非顶级模块,只将其顶级模块名称加入到本地名词空间中。导入的模块必须使用完全限定名称来访问
- 如果使用了as,as后的名称直接绑定到导入的模块对象,并将该名称加入到本地名词空间中
- import之后只能是模块类型
语句 | 含义 |
---|---|
from … import … | 部分导入 |
from … import … as … | 别名 |
from语句
from pathlib import Path,PosixPath #在当前名词空间导入该模块的指定的成员
print(dir())
#输出
['Path', 'PosixPath', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
from pathlib import * #在当前名词空间导入该模块所有公共成员(非下划线开头的成员)或指定成员__all__
print(dir())
#输出
['Path', 'PosixPath', 'PurePath', 'PurePosixPath', 'PureWindowsPath', 'WindowsPath', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
from functools import wraps as wr,partial #别名
print(dir())
#输出
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'partial', 'wr']
from os.path import exists #加载、初始化os、os.path模块,exists加入本地名词空间并绑定
if exists('test'):
print("Found")
else:
print('Not Found')
print(dir())
print(exists)
import os
#4钟方式获得同一个对象exists
print(os.path.exists)
print(exists)
print(os.path.__dict__['exists'])
print(getattr(os.path,'exists'))
#输出
Found
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'exists']
<function exists at 0x7fcd48168040>
<function exists at 0x7fcd48168040>
<function exists at 0x7fcd48168040>
<function exists at 0x7fcd48168040>
<function exists at 0x7fcd48168040>
总结:
- 找到from子句中指定的模块,加载并初始化它(注意不是导入)
- 对于import子句后的名称
- 先查from子句导入的模块是否具有该名称的属性
- 如果不是,则尝试导入该名称的子模块
- 还没有找到,则抛出importError异常
- 这个名称保存到本地名词空间中,如果有as子句,则使用as子句后的名称
from pathlib import Path #导入类Path
print(Path,id(Path))
import pathlib as pl #导入模块使用别名
print(dir())
print(pl)
print(pl.Path,id(pl.Path))
#输出 ,可以看出导入的名词Path和pl.Path是同一个对象
<class 'pathlib.Path'> 140408755686416
['Path', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'pl']
<module 'pathlib' from '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pathlib.py'>
<class 'pathlib.Path'> 140408755686416
自定义模块
自定义模块:.py就是一个模块
#module.py文件
print('This is modeules 模块')
class A:
def showmodule(self):
print(1,self.__module__,self)
print(2,__class__,id(__class__))
if __name__ == '__main__':
a = A()
a.showmodule()
print(3,__name__)
#test2.py
import module
print(module.a)
print(__name__)
#输出
This is modeules 模块
1 module <module.A object at 0x7fd6c0070fa0>
2 <class 'module.A'> 140560824288896
3 module
<module.A object at 0x7fd6c0070fa0>
__main__
#test3.py
from module import A as cls
if __name__ == '__main__':
a = cls()
a.showmodule()
#输出
This is modeules 模块
3 module
1 module <module.A object at 0x7f8db008e6a0>
2 <class 'module.A'> 140246493782864
自定义模块命名规范
- 模块名就是文件名
- 模块名必须符合标识符的要求,是非数字开头的字母、数字、下划线的组合。test-module.py这样的文件名不能作为模块名,也不要使用中文
- 不要使用系统模块名来避免冲突,除非你明确知道这个模块名的用途
- 通常模块名为全小写,下划线来分割
模块搜索顺序
使用sys.path 查看搜索顺序
import sys
print(*sys.path,sep='\n')
#输出
/Users/dujie/Desktop/t1/bin/python /Users/dujie/PycharmProjects/t1/bao.py
/Users/dujie/PycharmProjects/t1
/Users/dujie/PycharmProjects/t1
/Library/Frameworks/Python.framework/Versions/3.8/lib/python38.zip
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/lib-dynload
/Users/dujie/Desktop/t1/lib/python3.8/site-packages
显示结果为,python模块的路径搜索顺序
当加载一个模块的时候,需要从这些搜索路径中从前到后依次查找,并不搜索这些目录的子目录。
搜索到模块就加载,搜索不到就抛异常。
路径也可以为字典、zip文件、egg文件
.egg文件,有setuptools库创建的包,第三方库常用的格式。添加了元数据(版本号、依赖项等)信息的zip文件
路径顺序为:
- 程序主目录,程序运行的主程序脚本所在的目录
- PYTHONPATH目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径
- 标准库目录,python自带的库模块所在的目录
sys.path可以被修改,增加新的目录
模块的重复导入
class A:
def showmodule(self):
print(1,self.__module__,self)
print(2,self.__class__,id(__class__))
a = A()
a.showmodule()
import module
print('localmodule')
import module
import module
#输出
1 __main__ <__main__.A object at 0x7fbe3801af70>
2 <class '__main__.A'> 140454665431680
This is modeules 模块
3 module
localmodule
print(*sys.modules.keys(),sep='\n')
#输出
...
importlib.abc
contextlib
importlib.util
_virtualenv
enum
_sre
sre_constants
sre_parse
sre_compile
copyreg
re
_distutils_hack
site
module
从执行结果看,不会产生重复导入的现象。
所有加在的模块都会记录到sys.modules中,sys.modules是存储已经加载过的所有模块的字典。
打印sys.modules可以看到builtins、os、os.path、sys等模块都已经加载了。
模块运行
__name__
每个模块都会定义一个__name__
特殊变量来存储当前模块的名称,如果不指定,则默认为源代码文件名,如果是包则有限定名。
解释器初始化的时候,会初始化sys.modules字典(保存已加载的模块),加载builtins(全局函数、常量)模块、__main__
模块、sys模块,以及初始化模块搜索路径sys.path
Python是脚本语言,任何一个脚本都可以直接被执行,也可以作为模块被导入。
当从标准输入(命令行方式敲代码)、脚本($ python test.py)或交互式读取的时候,会将模块的__name__
设置为__main__
,模块的顶层代码就在__main__
这个作用域中执行。顶层代码:模块中缩进最外层的代码
如果是import导入的,其__name__
默认就是模块名
# test1.py文件
import test2
# test2.py文件
# 判断模块是否以程序的方式运行 $python test.py
if __name__ == '__main__':
print('in __main__') # 程序的方式运行的代码
else:
print('in imported module') # 模块导入的方式运行的代码
if __name__ == '__main__'
:用途
- 本模块的功能测试
- 对于非主模块,测试本模块的函数、类
- 避免主模块变更的副作用
- 顶层代码,没有封装,主模块使用时没有问题。但是,一旦有了新的主模块,老的主模块就成了被导入模块,由于原来代码没有封装,一并执行了
模块的属性
属性 | 含义 |
---|---|
__file__ |
字符串,源文件路径 |
__cached__ |
字符串,编译后的字节码文件路径 |
__spec__ |
显示模块的规范 |
__name__ |
模块名 |
__package__ |
当模块是包,同__name__,否则可以设置为顶级模块的空字符串 |
import test3
for k,v in test3.__dict__.items():
print(k,str(v)[:120])
print(dir(test3))
for name in dir(test3):
print(getattr(test3,name))
#输出
This is modeules 模块
3 module
__name__ test3
__doc__ None
__package__
__loader__ <_frozen_importlib_external.SourceFileLoader object at 0x7fddf0070f70>
__spec__ ModuleSpec(name='test3', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7fddf0070f70>, origin='/Users/d
__file__ /Users/dujie/PycharmProjects/t1/test3.py
__cached__ /Users/dujie/PycharmProjects/t1/__pycache__/test3.cpython-38.pyc
__builtins__ {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil'
cls <class 'module.A'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'cls']
{'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'breakpoint': <built-in function breakpoint>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>, 'exec': <built-in function exec>, 'format': <built-in function format>, 'getattr': <built-in function getattr>, 'globals': <built-in function globals>, 'hasattr': <built-in function hasattr>, 'hash': <built-in function hash>, 'hex': <built-in function hex>, 'id': <built-in function id>, 'input': <built-in function input>, 'isinstance': <built-in function isinstance>, 'issubclass': <built-in function issubclass>, 'iter': <built-in function iter>, 'len': <built-in function len>, 'locals': <built-in function locals>, 'max': <built-in function max>, 'min': <built-in function min>, 'next': <built-in function next>, 'oct': <built-in function oct>, 'ord': <built-in function ord>, 'pow': <built-in function pow>, 'print': <built-in function print>, 'repr': <built-in function repr>, 'round': <built-in function round>, 'setattr': <built-in function setattr>, 'sorted': <built-in function sorted>, 'sum': <built-in function sum>, 'vars': <built-in function vars>, 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': <class 'bool'>, 'memoryview': <class 'memoryview'>, 'bytearray': <class 'bytearray'>, 'bytes': <class 'bytes'>, 'classmethod': <class 'classmethod'>, 'complex': <class 'complex'>, 'dict': <class 'dict'>, 'enumerate': <class 'enumerate'>, 'filter': <class 'filter'>, 'float': <class 'float'>, 'frozenset': <class 'frozenset'>, 'property': <class 'property'>, 'int': <class 'int'>, 'list': <class 'list'>, 'map': <class 'map'>, 'object': <class 'object'>, 'range': <class 'range'>, 'reversed': <class 'reversed'>, 'set': <class 'set'>, 'slice': <class 'slice'>, 'staticmethod': <class 'staticmethod'>, 'str': <class 'str'>, 'super': <class 'super'>, 'tuple': <class 'tuple'>, 'type': <class 'type'>, 'zip': <class 'zip'>, '__debug__': True, 'BaseException': <class 'BaseException'>, 'Exception': <class 'Exception'>, 'TypeError': <class 'TypeError'>, 'StopAsyncIteration': <class 'StopAsyncIteration'>, 'StopIteration': <class 'StopIteration'>, 'GeneratorExit': <class 'GeneratorExit'>, 'SystemExit': <class 'SystemExit'>, 'KeyboardInterrupt': <class 'KeyboardInterrupt'>, 'ImportError': <class 'ImportError'>, 'ModuleNotFoundError': <class 'ModuleNotFoundError'>, 'OSError': <class 'OSError'>, 'EnvironmentError': <class 'OSError'>, 'IOError': <class 'OSError'>, 'EOFError': <class 'EOFError'>, 'RuntimeError': <class 'RuntimeError'>, 'RecursionError': <class 'RecursionError'>, 'NotImplementedError': <class 'NotImplementedError'>, 'NameError': <class 'NameError'>, 'UnboundLocalError': <class 'UnboundLocalError'>, 'AttributeError': <class 'AttributeError'>, 'SyntaxError': <class 'SyntaxError'>, 'IndentationError': <class 'IndentationError'>, 'TabError': <class 'TabError'>, 'LookupError': <class 'LookupError'>, 'IndexError': <class 'IndexError'>, 'KeyError': <class 'KeyError'>, 'ValueError': <class 'ValueError'>, 'UnicodeError': <class 'UnicodeError'>, 'UnicodeEncodeError': <class 'UnicodeEncodeError'>, 'UnicodeDecodeError': <class 'UnicodeDecodeError'>, 'UnicodeTranslateError': <class 'UnicodeTranslateError'>, 'AssertionError': <class 'AssertionError'>, 'ArithmeticError': <class 'ArithmeticError'>, 'FloatingPointError': <class 'FloatingPointError'>, 'OverflowError': <class 'OverflowError'>, 'ZeroDivisionError': <class 'ZeroDivisionError'>, 'SystemError': <class 'SystemError'>, 'ReferenceError': <class 'ReferenceError'>, 'MemoryError': <class 'MemoryError'>, 'BufferError': <class 'BufferError'>, 'Warning': <class 'Warning'>, 'UserWarning': <class 'UserWarning'>, 'DeprecationWarning': <class 'DeprecationWarning'>, 'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>, 'SyntaxWarning': <class 'SyntaxWarning'>, 'RuntimeWarning': <class 'RuntimeWarning'>, 'FutureWarning': <class 'FutureWarning'>, 'ImportWarning': <class 'ImportWarning'>, 'UnicodeWarning': <class 'UnicodeWarning'>, 'BytesWarning': <class 'BytesWarning'>, 'ResourceWarning': <class 'ResourceWarning'>, 'ConnectionError': <class 'ConnectionError'>, 'BlockingIOError': <class 'BlockingIOError'>, 'BrokenPipeError': <class 'BrokenPipeError'>, 'ChildProcessError': <class 'ChildProcessError'>, 'ConnectionAbortedError': <class 'ConnectionAbortedError'>, 'ConnectionRefusedError': <class 'ConnectionRefusedError'>, 'ConnectionResetError': <class 'ConnectionResetError'>, 'FileExistsError': <class 'FileExistsError'>, 'FileNotFoundError': <class 'FileNotFoundError'>, 'IsADirectoryError': <class 'IsADirectoryError'>, 'NotADirectoryError': <class 'NotADirectoryError'>, 'InterruptedError': <class 'InterruptedError'>, 'PermissionError': <class 'PermissionError'>, 'ProcessLookupError': <class 'ProcessLookupError'>, 'TimeoutError': <class 'TimeoutError'>, 'open': <built-in function open>, 'quit': Use quit() or Ctrl-D (i.e. EOF) to exit, 'exit': Use exit() or Ctrl-D (i.e. EOF) to exit, 'copyright': Copyright (c) 2001-2021 Python Software Foundation.
All Rights Reserved.
Copyright (c) 2000 BeOpen.com.
All Rights Reserved.
Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.
Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.}
/Users/dujie/PycharmProjects/t1/__pycache__/test3.cpython-38.pyc
None
/Users/dujie/PycharmProjects/t1/test3.py
<_frozen_importlib_external.SourceFileLoader object at 0x7fddf0070f70>
test3
ModuleSpec(name='test3', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7fddf0070f70>, origin='/Users/dujie/PycharmProjects/t1/test3.py')
<class 'module.A'>
包
包,即特殊的模块
实验:
项目中新建一个目录m,使用下面的代码
import m # 目录
print(m)
print(type(m))
print(dir(m))
print(*m.__dict__.items(), sep='\n') # __file__为None
print('-' * 30)
import test1
print(*test1.__dict__.items(), sep='\n') # 文件模块没有__path__
竟然可以导入目录m,目录也是文件,所以可以导入,不过问题是,目录模块怎么写入代码?
为了解决这个问题,Python要求在目录下建立一个特殊文件 __init__.py ,在其中写入代码
pycharm中,创建Directory和创建Python package不同,前者是创建普通的目录,后者是创建一个带
有 __init__.py 文件的目录即包。
Python中,目录可以作为模块,这就是包,不过代码需要写在该目录下 __init__.py 中。
包的 __file__ 就指向 __init__.py 这个文件。
子模块
包目录下的py文件、子目录都是其子模块
m
|-- __init__.py
|-- m1.py
|-- m2
|-- __init__.py
|-- m21
|-- __init__.py
|-- m22.py
如上建立子模块目录和文件,所有的py文件中就写一句话 print(__name__)
#注意观察已经加载的模块、当前名词空间的名词
#import m
#import m.m1
#import m.m2.m21
#from m import m1
from m.m2 import m21
print('-'*30)
print(*filter(lambda x:x.startswith('m'), dir()))
print('-'*30)
import sys
print(sorted(filter(lambda x:x.startswith('m'), sys.modules.keys())))
删除 __init__.py
试一试,可以发现删除并不影响导入,但是这不是良好的习惯,请保留
__init__.py
文件
模块和包的总结
包能够更好的组织模块,尤其是大的模块,其代码行数很多,可以把它拆分成很多子模块,便于使用某些功能就能加载相应的子模块。
包目录中__init__.py
是在包的第一次导入的时候就会执行,内容可以为空,也可以是用于该包初始化工作的代码,最好不要删除它(低版本不可以删除__init__.py
文件)
导入子模块一定会加载父模块,但是导入父模块一定不会导入子模块
包目录之间只能使用.点号作为分隔符,表示模块及其子模块的层级关系
模块也是封装,如果类、函数,不过它能够封装变量、类、函数。
模块就是命名空间,其内部的顶层标识符,都是他的属性,可以通过__dist__
或dir(module)查看。
包也是模块,但模块不一定是包,包是特殊的模块,是一种组织方式,它包含__path__
属性
问题
from json import encode
r 之后, json.dump
函数用不了,为什么?
因为from后虽然引用了json,但是没有标识符,无法使用
原因是from json import encoder之后,当前名词空间没有json,但是json模块已经加载过了,没有
json的引用,无法使用dump函数。
import json.encoder
之后呢?json.dump
函数能用吗?
因为import后 json在sys.module中,可以直接调用
import json.encoder也加载json模块,但是当前名词空间有json,因此可以调用json.dump。
绝对导入、相对导入
绝对导入
- 在import语句或from导入模块,模块名称最前面不是以.点开头的
- 绝对导入总是去模块搜索路径中找,当然会查看一下该模块是否已经加载
相对导入
- 只能用在from语句中
- 使用.点号,表示当前目录内
- ..两点表示上一级目录
- …三点表示上上一级
- 只在包内使用,一般不要在顶层模块中使用相对导入
- 一旦一个模块中使用相对导入,就不可以作为主模块运行了
# 举例a.b.c模块,a、b是目录,c是模块c.py
# c的代码如下
from . import d # imports a.b.d
from .. import e # imports a.e
from .d import x # a.b.d.x
from ..e import x # a.e.x
使用下面的结构的包,体会相对导入的使用
m
|-- __init__.py
|-- m1.py
|-- m2
|-- __init__.py
|-- m21
|-- __init__.py
|-- m22.py
测试一下有相对导入语句的模块,能够直接运行吗?
不能了,很好理解,使用相对导入的模块就是为了内部互相的引用资源的,不是为了直接运行的,对于
包来说,正确的使用方式还是在顶级模块使用这些包及其内部资源。
相对导入,更像是目录操作。
访问控制
下划线开头的模块名
_或者__开头的模块是否能够被导入呢?
创建文件名为_xyz.py或者__xyz.py测试
都可以成功的导入,因为他们都是合法的标识符,就可以用作模块名。
模块内的标识符
# xyz.py
print(__name__) A = 5
_B = 6
__C = 7
__my__ = 8
#test.py中
import xyz
import sys
print(sorted(sys.modules.keys()))
print(dir())
print(xyz.A)
print(xyz._B)
print(xyz.__C)
print(xyz.__my__)
普通变量、保护变量、私有变量、特殊变量,都没有被隐藏,也就是说模块内没有私有的变量,在模块
中定义不做特殊处理。
# test.py中
# from语句导入
from xyz import A, _B as B, __my__, __C as C
import sys
print(sorted(sys.modules.keys()))
print(dir())
print(A, B, __my__, C)
依然可以使用from语句,访问所有变量
from … import * 和 __all__
####### 使用from … import * 导入
# xyz.py
print(__name__)
A = 5
_B = 6
__C = 7
__my__ = 8
# test.py中
from xyz import *
import sys
print(sorted(sys.modules.keys()))
print(dir())
print(locals()['A'])
A = 55
print(locals()['A']) # 思考这个A是谁的A了
xyz
['__main__', '_abc', '_bootlocale', '_codecs', '_collections', '_collections_abc', '_distutils_hack', '_frozen_importlib', '_frozen_importlib_external', '_functools', '_heapq', '_imp', '_io', '_locale', '_operator', '_signal', '_sitebuiltins', '_sre', '_stat', '_thread', '_virtualenv', '_warnings', '_weakref', 'abc', 'builtins', 'codecs', 'collections', 'contextlib', 'copyreg', 'encodings', 'encodings.aliases', 'encodings.latin_1', 'encodings.utf_8', 'enum', 'functools', 'genericpath', 'heapq', 'importlib', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib.abc', 'importlib.machinery', 'importlib.util', 'io', 'itertools', 'keyword', 'marshal', 'operator', 'os', 'os.path', 'posix', 'posixpath', 're', 'reprlib', 'site', 'sre_compile', 'sre_constants', 'sre_parse', 'stat', 'sys', 'time', 'types', 'warnings', 'xyz', 'zipimport']
['A', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
5
55
结果是只导入了A,下划线开头的都没有导入
使用__all__
__all__
是一个列表,元素是字符串,每一个元素都是一个模块内的变量名
# xyz.py
__all__ = ['_B']
print(__name__)
A = 5
_B = 6
__C = 7
__my__ = 8
from xyz import *
import sys
print(sorted(sys.modules.keys()))
print(dir())
print(locals()['_B'])
可以看到使用from xyz import *导入 __all__ 列表中的名称
包和子模块
包和模块结构如下
m
|-- __init__.py
|-- m1.py
# __init__.py中
print(__name__)
x = 1
# m1.py中
print(__name__)
y = 5
# test.py中
# 如何访问到m1.py中的变量y?
# 访问到m.m1的变量y的几种实现
import m
print(m.m1.y) # 可以吗?
# 方法1,直接导入m1模块
import m.m1
print(m.m1.y)
# 方法2,直接导入m.m1的属性y
from m.m1 import y
print(y)
# 方法3,from m import *
# print(dir())
# 该方法导入后,无法看到子模块m1,无法访问y
# 在__init__.py增加__all__ = ['x', 'm1'],使用__all__提供导出的名称
from m import *
print(m1.y)
# 方法4,不使用__all__
# 在__init__.py增加from . import m1
from m import *
print(m1.y)
# 不止以上4种,还有其他方式解决访问y,这里不再赘述
__init__.py 中有什么变量,则使用from m import *加载什么变量,这依然符合模块的访问控制
# __init__.py文件
print(__name__) x = 1
from .m1 import y as _z
print(dir())
####### 总结
一、使用from xyz import * 导入
- 如果模块没有
__all__
,from xyz import *
只导入非下划线开头的该模块的变量。如果包,子模块也不会导入,除非在__all__
中设置,或__init__.py
中导入他们 - 如果模块有
__all__
,from xyz import *
只导入__all__
列表中指定的名称,哪怕这个名词是下划线开头的,或者子模块 from xyz import *
方式导入,使用简单,但是其副作用是导入大量不需要使用的变量,甚至有可能造成名称的冲突。而__all__
可以控制被导入模块在这种导入方式下能够提供的变量名称,就是为了阻止from xyz import *
导入过多的变量,从而避免冲突。因此,编写模块时,应该尽量加入__all__
二、from module import name1, name2 导入
这种方式的导入是明确的,哪怕导入子模块,或者导入下划线开头的 名称
程序员可以有控制的导入名称和其对应的对象