python 模块定义、导入、优化

发布时间:2018-03-01 14:45:55编辑:admin阅读(3318)

    模块:

    在Python中,一个.py文件就称之为一个模块(Module)。

    它是实现了某个功能的代码集合。


    模块分为三种:


    1.自定义模块

    2.内置标准模块(又称标准库)

    3.开源模块


    模块导入方法:

    1. import module_name (导入一个模块)

    2. import module_name,module2_name (导入多个模块)

    3. from module_name import * (不推荐使用)

    4. from module_name import m1 as name_m1 (导入指定的函数或变量并重命名)

    5. from module_name import m1,m2,m3 (导入模块的多个函数或变量)


    第3种办法,不推荐使用。因为这一段话,相当于把module_name.py的所有代码复制到当前代码中,调用的时候,直接写方法名调用,不需要module_name.方法名()这种方式调用。如果发现方法名或者变量冲突,以本代码为准。


    import的本质

    把导入模块的所有代码解释一遍,统一赋值给一个变量,这个变量名叫模块名。

    调用变量,使用 module_name.变量名

    调用函数,使用 module_name.函数名()


    from的本质

    把导入模块的指定函数或者变量,放到当前文件的位置,执行一遍。

    调用变量或者函数的时候,直接使用即可,不需要在前面加模块名。


    自定义一个模块

    新建文件hello.py

    name = "zhang"
    def say_hello():
        print("hello,",name)

    新建文件test.py,导入模块。注意,模块名是去除.py之后的名字

    import hello
    #输出变量,执行方法
    print(hello.name)
    hello.say_hello()

    执行输出

    zhang

    hello, zhang


    包:

    用来从逻辑上组织模块,本质就是一个目录(必须带有一个__init__.py文件)


    导入包的本质:

    去执行包目录下的__init__.py文件


    使用pycharm编辑器,新建一个包package_test

    编辑__init__.py文件

    print('from the package package_test')

    新建一个文件p_test.py

    import package_test

    目录结构如下:

    .
    ├── p_test.py
    └── package_test
        └── __init__.py

    执行p_test.py,输出

    from the package package_test


    上面介绍的模块导入,都是基于同级目录的。那么module在不同目录下,该如何导入呢?

    目录结构如下:

    .
    ├── hello.py
    └── package_test
        ├── __init__.py
        └── main.py

    main.py需要导入hello模块


    先来分析一个导入模块的执行步骤

    import module_name->找到module_name.py文件->找到module_name.py的路径->sys.path(搜索路径)

    编辑main.py,打印sys.path

    import sys
    print(sys.path)

    使用cmd执行,执行输出

    ['E:\\python_script\\day5\\package_test', 'C:\\Program Files\\Python36\\python36.zip', 'C:\\Program Files\\Python36\\DLLs', 'C:\\Program Files\\Python36\\lib', 'C:\

    \Program Files\\Python36', 'C:\\Program Files\\Python36\\lib\\site-packages']

    第一个元素,就是当前路径

    如果一个元素找不到,就会找下一个元素,直到所有的目录都还找不到的话,就报错

    ModuleNotFoundError: No module named 'xxx'


    为了解决这个问题,需要加一个路径,必须要包含需要导入模块的路径。这个路径,一般为项目根目录。

    编辑main.py,先来打印当前文件的绝对路径

    import sys,os
    print(os.path.abspath(__file__))

    执行输出

    E:\python_script\day5\package_test\main.py


    然后获取当前目录

    import sys,os
    print(os.path.dirname(os.path.abspath(__file__)))

    执行输出

    E:\python_script\day5\package_test


    最后获取上一层目录,定义变量

    import sys,os
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    print(BASE_DIR)

    执行输出

    E:\python_script\day5

    这个目录,就可以找到package_test路径了!


    将项目根目录添加到系统环境变量中

    import sys,os
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)

    最后导入模块,执行函数

    import sys,os
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    
    import hello
    
    hello.say_hello()

    执行输出

    hello, zhang


    注意:

    sys.path.append(BASE_DIR) 是将项目根目录追加到最后一个列表元素。如果中间某一个元素,正好找到了指定文件,比如hello.py,那么就不会再查找后面的路径了,直接加载。

    如果出现了这种情况,必须要把BASE_DIR插入到第一个元素位置,使用insert方法


    sys.path.insert(0,BASE_DIR)
    print(sys.path)

    执行输出

    ['E:\\python_script\\day5', 'E:\\python_script\\day5\\package_test', 'E:\\python_script', 'C:\\Program Files\\Python36\\python36.zip', 'C:\\Program Files\\Python36\\DLLs', 'C:\\Program Files\\Python36\\lib', 'C:\\Program Files\\Python36', 'C:\\Program Files\\Python36\\lib\\site-packages']


    举另外一个例子

    main.py需要调用test1.py里面的函数,目录结果如下:

    .
    ├── module_test
    │   └── main.py
    └── package_test
        ├── __init__.py
        └── test1.py

    test1.py内容如下:

    def test():
        print('in the test1')


    main.py直接引用package_test包,是否可以执行呢?

    import sys,os
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    
    import package_test
    
    package_test.test1.test()

    执行报错

    ModuleNotFoundError: No module named 'test1'


    因为import package_test 这一步是执行package_test目录下的__init__.py文件,而这个文件是空的

    修改__init__.py文件,从当前目录下,导入test1模块

    这个 点 表示相对于__init__.py文件的路径。

    from . import test1

    再次执行main.py

    执行输出

    in the test1


    再深层次的目录调用,目录结果如下:

    python_script/
    ├── day4
    │   └── test.py
    └── day5
        └── package_test
            ├── __init__.py
            └── test1.py

    day5的代码,同上,没改动

    test.py需要调用test1.py,那么需要把python_script目录加到环境变量即可


    修改day4下的test.py文件

    import sys,os
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    print(BASE_DIR)

    执行输出

    E:\python_script


    加载到环境变量

    import sys,os
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    
    from day5 import package_test
    package_test.test1.test()

    执行输出

    in the test1


    注意下面这行代码

    from day5 import package_test

    day5表示python_script目录下的day5


    导入优化

    语法:

    from 模块名 import 函数或变量名


    举个例子,目录结构如下:

    test4/
    ├── module_test.py
    └── test.py

    module_test.py内容如下:

    def test():
        print('in the test')

    test.py内容如下:

    import module_test
    
    def logger():
        module_test.test()
        print('in the logger')
    
    def search():
        module_test.test()
        print('in the search')
    
    #假设有N个函数在调用module_test.test()
    #...
    logger()

    执行输出

    in the test

    in the logger


    module_test.test()在执行的时候,先找到module_test模块,再从里面检索test函数是否存在。

    那么每执行一次,就要先找模块,再找函数,重复劳动,效率很低。


    为了解决这个问题,就是在导入模块的时候,不让它找就可以了

    import module_test

    改成

    from module_test import test

    就可以了。


    下面的执行方法module_test.test()改成test() 就可以了,完整代码如下

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    from module_test import test
    
    def logger():
        test()
        print('in the logger')
    
    def search():
        test()
        print('in the search')
    
    #....
    
    logger()

    执行输出

    in the test

    in the logger


    这种方法,可以使代码执行的更快

    为了避免函数重名,可以在导入的时候,起别名

    from module_test import test as mod_test


关键字