美文网首页
python 模块

python 模块

作者: 今夜秋风和 | 来源:发表于2022-12-28 22:05 被阅读0次

什么是模块

将类,函数,变量等放在一个.py 文件中的形式称为模块,模块使得我们可以更好组织代码,增加代码可读性,易维护;
当模块变得越来越多时,使用包(package) 来组织模块,通过在 文件夹下创建init.py 文件 ,python 中将具有init_.py 的文件夹形式称为包,一个包下可以继续划分成子包的形式, 子包 下存放对应的模块,项目中通过这样的结构,来使得业务模块之间相互解耦;

模块导入

  • 使用import 语句来引入模块
tests_1
  __init__.py
  test_a.py
  test_b.py
# test_a 模块导入test_b 模块 
# test_a.py 例子:
import sys
import  test_b
test_b.test_b_function()
  • 使用from ... import 语句来引入模块
    from 语句让你从模块中导入一个指定的部分到当前命名空间中
    例如在上面的例子中 test_a 模块导入test_b 模块 test_a.py:
import sys
from tests_1 import  test_b
test_b.test_b_function()

这样把test_b 模块整个导入当前test_a 的命名空间中,当使用test_b 模块的函数,变量按照testb.xx 这样调用即可; 或者也可以只导入需要使用的函数 例如: from tests_1.test_b import test_b_function ,调用test_b_function 函数通过test_b_function() 来进行调用;

  • 使用from .. import * 语句来引入模块
    把一个模块的所有内容全都导入到当前的命名空间也是可行的
    在上面的例子中,test_a 模块导入test_b 模块 test_a.py:
from tests_1 import * // 把tests_1 包下的所有模块导入了test_a 的命名空间
from tests_1.test_b  import *  // 把tests_1 包下的test_b模块除了_ 开头的所有类,函数,变量 都导入了test_a 的命名空间

# 模块搜索过程

执行python 脚本,当遇到import 时,python 会进行模块的搜索并加载:
1.搜索sys.modules(当模块被第一次加载了后,会被保存在sys.modules 字典中,下次加载优先从sys.modules 字典进行加载)

>>> import sys
>>> sys.modules
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, 'zipimport': <module 'zipimport' (built-in)>, '_frozen_importlib_external': <module 'importlib._bootstrap_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'posix': <module 'posix' (built-in)>, 'encodings': <module 'encodings' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/encodings/__init__.py'>, 'codecs': <module 'codecs' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/codecs.py'>, '_codecs': <module '_codecs' (built-in)>, 'encodings.aliases': <module 'encodings.aliases' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/encodings/aliases.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/encodings/utf_8.py'>, '_signal': <module '_signal' (built-in)>, '__main__': <module '__main__' (built-in)>, 'encodings.latin_1': <module 'encodings.latin_1' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/encodings/latin_1.py'>, 'io': <module 'io' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/io.py'>, 'abc': <module 'abc' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/abc.py'>, '_abc': <module '_abc' (built-in)>, 'site': <module 'site' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site.py'>, 'os': <module 'os' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/os.py'>, 'stat': <module 'stat' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/stat.py'>, '_stat': <module '_stat' (built-in)>, '_collections_abc': <module '_collections_abc' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/_collections_abc.py'>, 'posixpath': <module 'posixpath' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py'>, 'genericpath': <module 'genericpath' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/genericpath.py'>, 'os.path': <module 'posixpath' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py'>, '_sitebuiltins': <module '_sitebuiltins' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/_sitebuiltins.py'>, '_bootlocale': <module '_bootlocale' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/_bootlocale.py'>, '_locale': <module '_locale' (built-in)>, 'types': <module 'types' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/types.py'>, 'importlib': <module 'importlib' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py'>, 'importlib._bootstrap': <module 'importlib._bootstrap' (frozen)>, 'importlib._bootstrap_external': <module 'importlib._bootstrap_external' (frozen)>, 'warnings': <module 'warnings' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/warnings.py'>, 'importlib.util': <module 'importlib.util' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/util.py'>, 'importlib.abc': <module 'importlib.abc' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/abc.py'>, 'importlib.machinery': <module 'importlib.machinery' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/machinery.py'>, 'contextlib': <module 'contextlib' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/contextlib.py'>, 'collections': <module 'collections' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/collections/__init__.py'>, 'operator': <module 'operator' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/operator.py'>, '_operator': <module '_operator' (built-in)>, 'keyword': <module 'keyword' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/keyword.py'>, 'heapq': <module 'heapq' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/heapq.py'>, '_heapq': <module '_heapq' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/_heapq.cpython-37m-darwin.so'>, 'itertools': <module 'itertools' (built-in)>, 'reprlib': <module 'reprlib' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'functools': <module 'functools' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/functools.py'>, '_functools': <module '_functools' (built-in)>, 'google': <module 'google' (namespace)>, 'google.logging': <module 'google.logging' (namespace)>, 'readline': <module 'readline' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/readline.cpython-37m-darwin.so'>, 'atexit': <module 'atexit' (built-in)>, 'rlcompleter': <module 'rlcompleter' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/rlcompleter.py'>}

2.搜索sys.meta_path
在解释器环境下查看sys.meta_path 所包含的类:

>>> sys.meta_path
[<class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib_external.PathFinder'>]

meta_path = [
    __loader__,
    None, # (!) real value is "<class '_frozen_importlib.FrozenImporter'>" # Meta path import for frozen modules.
    None, # (!) real value is "<class '_frozen_importlib_external.PathFinder'>" # Meta path finder for sys.path and package __path__ attributes
]

BuiltinImporter(代表python内置模块importer 对象,用来查找内置模块)
FrozenImporter(编译为可执行文件模块的importer 对象, 查找可执行文件模块)
PathFinder(代表路径查找模块的对象,查找sys.path)
当在sys.modules 查找不到模块时,依次查找内置模块,可执行文件模块,sys.path,如果这些中都没有找到模块,则会抛出ModuleNotFoundError 错误;
查找的sys.path 为一个列表:
在解释器环境下查看sys.path 路径:

>>> import sys
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']

1.sys.path[0] 为执行当前模块所在的目录位置,按照sys.path 路径的优先级会优先被搜索
2.第二个顺序时系统安装python 环境时内置进去的“ '/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload' ''
3.第三个顺序是我们安装第三方模块比如redis,flask等, 这些模块统一放在当前这个路径下 /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages

python 2.x 和python3.x import 差异

在之前的模块导入过程中测试了一些案例,整理并记录下一些特殊的地方,比如,在以下例子中:

test_import
     test_sub_import
        __init__.py
          test3.py
          test4.py
     test1.py
     test2.py
    __init__.py

在上述的模块test1 导入test3

test1.py 
import os
from test_sub_import import  test3
tests3.print_test3()

test3 模块导入test4模块:

test3.py 
import  test4
def print_test3():
    print('test3')

test4.py 模块内容:

def print_test4():
    print('test4')

在test_import 目录下执行python2 test1.py

截屏2022-12-25 下午5.01.52.png
然后使用python3 test1.py 运行,发现找不到test4 模块
截屏2022-12-25 下午5.04.05.png
查找了很多资料,官网的介绍,经过梳理整理出来执行过程的区别:
这里引出了相对导入和绝对导入的区别:
官方说明:
Relative imports use a module's name attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to 'main') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
大致意思是: 相对导入使用模块属性name 名称来决定模块在包中的位置, 如果这个模块的名字没有包含任何包的信息,它的name 属性被设置为main,相对导入的被导入模块如果是顶层模块就会被解析;

上面python2 test1.py :
test1.py 直接作为脚本执行,那么它的name 就是main,当遇到from test_sub_import import test3,解释器按照sys.modules->sys.meta_path->sys.path 顺序搜索,最终从sys.path0在这个路径下找到test_sub_import ,然后找到test3 模块加载进来, test3 模块的名称为(test_sub_import.test3),当在test3 模块加载test4 模块时,会基于test_sub_import 包下查找test4模块进行加载.通过打印name属性,观察每个加载的每个模块名称;

截屏2022-12-25 下午6.07.03.png
python3 test1.py
在python3 环境下使用绝对导入,在sys.path[0] 下没有test4,所以会报错,通过修改sys.path, 增加查找的test_sub_import路径,sys.path.append('/Users/xiaolin.zhang/Documents/myproject/python_test_project/test_import/test_sub_import'),在执行 python3 test1.py, 正常,通过打印日志,这里test4 name 属性名为test4;
截屏2022-12-25 下午6.11.07.png

上面导入方式中,我们可以使用from . import test4 显示相对导入统一支持python2 和python3 环境:

test3.py
import sys
print(sys.path)
from . import  test4
print('test3-name',__name__)
def print_test3():
    print('test3')

这样test4 模块名称统一为:test_sub_import.test4
如果要在test1 入口脚本处相对导入test3 模块:

test1.py
import os
from .test_sub_import import  test3
test3.print_test3()
print('test1-name',__name__)

直接使用python3 test1.py 运行提示报错:

localhost:test_import xiaolin.zhang$ python3 test1.py
Traceback (most recent call last):
  File "/Users/xiaolin.zhang/Documents/myproject/python_test_project/test_import/test1.py", line 2, in <module>
    from .test_sub_import import  test3
ImportError: attempted relative import with no known parent package

这个是因为test1的名称是main,没有包的任何信息, 没法找到test_sub_import 包的位置,python 中通过-m 选项将test1 当作被导入的模块来执行;
在test_import 的上层目录下,执行python3 -m test_import.test1, 这样方式下test1的属性名称变为test_import.test1, 遇到 from .test_sub_import import test3时,通过从test_import包下会找到test_sub_import 这个包的位置信息;

相对导入的其他常见方式:
from .package_name import module_name 导入和当前自己同目录的包的其他模块
from ..package_name import module_name 导入当前自己父目录下的包的模块
from .. import module_name 导入当前自己父目录的模块

模块文件编译

当遇到import 时,python 会将导入的.py 文件编译为.pyc ,python2 下直接跟模块放在一个目录下,python3 统一当前pycache 目录下,当使用-O 选项时, 例如: python2 -O test2.py (python2 会存在.pyo 文件), .pyc 和.pyo文件其运行速度并不快于从.py文件读取时更快,只是加载速度会快一点而已;

localhost:test_sub_import xiaolin.zhang$ ls
__init__.py     __init__.pyc    __pycache__     test3.py        test3.pyc       test4.py        test4.pyc

localhost:test_sub_import xiaolin.zhang$ cd __pycache__/
localhost:__pycache__ xiaolin.zhang$ ls
__init__.cpython-39.pyc test3.cpython-39.pyc    test4.cpython-39.pyc

模块导入常见问题

  • xx object has no attribute 'xx'
    如果是自己写的模块,检查导入的模块是否缺少xx 这样的属性或函数
    检查是否和第三方的模块重名,比如也编写了一个redis.py 模块,导入模块用的是我们自己编写的,使用第三方库的一些函数就会带来问题,改成不一样的模块名即可
    有个隐藏的小问题是编写了一个redis.py,被其他模块导入,已经生成了.pyc 文件,发现重名了后将原有的redis.py 删除了,但是未删除redis.pyc 文件,使用redis 的函数在python2 的环境下也会出现问题

  • module xx has not found
    导入了一个错误的模块,导致不存在
    模块的位置不正确,也会出现找不到
    如果使用相对导入的话,理解这个相对导入的过程即可

  • attempted relative import with no known parent package
    在最入口脚本进行相对导入时,通过使用python -m xx 将脚本作为模块来加载,这样就可以找到要导入的包的位置;

  • can not from xx import xx
    看是否相互引用问题,比较a.py 引入b.py 中的一个函数,b.py 又引入a.py 的函数, 从模块上考虑这样划分是否合理;
    在flask 应用中,针对同一个变量的相互引用问题:

manager.py 文件:
app = create_app(config_name = os.getenv('FLASK_CONFIG') or 'development')
#create_app 过程
def create_app(config_name=None,app = None):
    if config_name is None:
        config_name = os.getenv('FLASK_CONFIG', 'development')
     app = Flask(__name__)
    app.config.from_object(config[config_name])
    app.after_request(after_request)
    register_logging(app)
    register_extensions(app)
    register_blueprints(app)
    register_errors(app)
    register_redis(app)
    return app
 在注册蓝本中引入app:
from manager import app
class TaskDailyBuild():

    def __init__(self):
        pass

解决方式:
把创建app 放在另一个extensions.py 文件中
un_instance_app = Flask(name)

manager.py 文件:
from KPAP.extensions import db, un_instance_app
app = create_app(config_name = os.getenv('FLASK_CONFIG') or 'development',app = un_instance_app)

蓝本中引入:

from KPAP.extensions import db, un_instance_app
from KPAP.models.table_models import Task
from KPAP.util.LogUtil import log
class TaskDailyBuild():
    def __init__(self):
        pass

相关文章

网友评论

      本文标题:python 模块

      本文链接:https://www.haomeiwen.com/subject/emhozrtx.html