代码结构
包与模块
一个普通的文件夹加上一个特殊的__init__.py
文件,就成为了Python的一个包(Package)
,一个包
可以包含多个模块(Module)
。
而一个模块
由一个或多个Python文件组成。
在com.itechthink.python
中存在test.py
文件。
# 指定通过*导入时哪些可以被导入
__all__ = ["lixingyun", "sayHello"]
lixingyun = 19
def sayHello():
print("hello python")
packages.py
可以通过几种不同的方式导入com.itechthink.python.test
中的变量和方法。
# 方式一
from com.itechthink.python import test
print(test.lixingyun)
test.sayHello()
# 方式二
from com.itechthink.python.test import lixingyun, sayHello
print(lixingyun)
sayHello()
# 方式三
from com.itechthink.python.test import *
print(lixingyun)
sayHello()
# 方式四
from com.itechthink.python.test import lixingyun as lxy, sayHello as hello
print(lxy)
hello()
# 方式五
import requests
rsp = requests.post("http://localhost:1234/json", json={
"method": "HelloService.Hello",
"params": ["lixingyun"],
"id": 0
})
print(rsp.text)
# 方式一:from model1 import varibales
# 方式二:from model1.varibales import user_list
# 这两种导入方式的区别在于:方式二在多线程环境下看不到user_list被修改后的值,而方式一可以
# 所以尽量以方式一导入外部模块中的变量和函数,然后这样使用:varibales.user_list
__init__.py
__init__.py
文件可以没有任何内容,但它的作用主要体现在两个方面。
- 一是在导入文件时,
__init__.py
中的代码会被最先自动执行,这可以用于系统模块初始化时执行某些特定的动作。例如,在__init__.py
中添加下面的内容。
# 用于指定哪些文件或模块可以被导入
__all__ = ["test"]
# 指定导入文件或模块时要预先执行的动作
a = "执行__init__.py文件"
print(a)
- 二是
__init__.py
可以作为一个公共的资源加载器,这样一来,后续所有文件在导入com.itechthink.python.test
时,也会自动导入__init__.py
中导入的模块,而不用再导一次。
# 在__init__.py中导入下面的模块
import sys
import io
......
避免循环导入
在com.itechthink.python
中存在两个文件m1.py
和m2.py
。
# m1.py中的内容
from com.itechthink.python.m2 import m2
m1 = 1
print(m1)
# m2.py中的内容
from com.itechthink.python.m1 import m1
m2 = 1
print(m2)
执行时报错:ImportError: cannot import name 'm2' from partially initialized module 'com.itechthink.python.m2' (most likely due to a circular import)
。
这就是典型的循环导入
问题。
模块内置变量
除了可以导入人为定义的模块变量,还可以调用Python内置的模块变量。
a = 1
b = "2"
c = 3.14
# 返回当前模块所有已定义的变量列表
print(dir())
# 执行后输出
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b', 'c']
也可以给dir()
指定参数。
import sys
print(dir(sys))
# 执行后输出
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', '_base_executable', '_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', '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', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'platlibdir', 'prefix', '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']
入口文件
有两个文件:com.packages.py
和com.itechthink.python.m1.py
。
com.itechthink.python.m1.py
中的内容如下。
print(__name__)
print(__package__)
# 执行结果
__main__
None
com.packages.py
中的内容如下。
from com.itechthink.python.m1 import *
print(__name__)
print(__package__)
# 执行结果
com.itechthink.python.m1
com.itechthink.python
__main__
None
所谓入口文件
就是用于启动程序的文件,当一个模块作为入口文件
被执行时,和将它作为普通模块执行时,是有差异的。
可以看到,同样的m1.py
代码,在作为模块导入到packages.py
后,两个内置变量__name__
和__package__
打印出来的值完全不同。
这就是m1.py
作为入口文件
和非入口文件
的差别。
__name__
# 判断当前模块是否是入口文件
if __name__ == '__main__':
print("作为入口文件被调用")
print("作为模块文件被调用")
当该文件作为入口文件
被调用时,两个print()
都会被执行,而作为普通模块文件
被调用时,只有后面的print()
会被调用。
相对导入
有这样几个模块和文件。
com/
|
└-- itechthink/
|
└-- module01/
| |
| └-- module04/
| |
| └-- m2.py
└--------- m1.py
|
└-- module02/
| |
| └--------- m3.py
|
└-- module03/
|
└-- main.py
m1.py
、m2.py
和main.py
内容分别如下。
# m1.py
# 当在m1.py中执行代码时,只能以它当前所在的模块module04作为顶级模块
import module04.m2
print("m1.py ==> ", __package__)
# m2.py
print("m2.py ==> ", __package__)
# main.py中代码要想成功执行,需要将 m1.py 中的 import module04.m2 改为 import module01.module04.m2
# 但这样一改,在m1.py中执行又会报错
import module01.m1
import module01.module04.m2
类似于import module01.module04.m2
这种导入形式称之为绝对导入
。
相对导入
则是指相对于当前模块而言的路径,相对导入
只能在非入口文件
中生效,而且只能以from...import
的形式使用。以m1.py
为例,其内容更改如下。
修改m1.py
、m2.py
、m3.py
和main.py
的内容。
# m1.py
# . 表示当前路径
# .. 表示上层路径
# ... 表示上上层路径
# .... 依此类推
from .module04.m2 import lixingyun
from ..module02.m3 import wanglin
print("m1.py ==>", __package__)
print(lixingyun)
print(wanglin)
# m2.py
lixingyun = 19
print("m2.py ==>", __package__)
# m3.py
wanglin = 20
print("m3.py ==>", __package__)
# main.py
import com.itechthink.module01.m1
print("main.py ==>", __package__)
main.py
的执行结果如下。
m2.py ==> com.itechthink.module01.module04
m3.py ==> com.itechthink.module02
m1.py ==> com.itechthink.module01
19
20
main.py ==> None
感谢支持
更多内容,请移步《超级个体》。