美文网首页
Python中模块与包

Python中模块与包

作者: Techml | 来源:发表于2017-05-02 16:44 被阅读0次

    一. 模块

    1. 概念

    为了便于代码维护,一般很多函数分组,分别放到不同的文件里,每个文件包含的代码就相对较少,维护也更方便。在Python中,一个.py文件就称之为一个模块(Module)。
    Python中模块分为三种: Python标准库第三方模块应用程序自定义模块
    使用模块可以帮助避免函数与其他模块函数重名,函数名与变量名重名的问题。
    此外,要尽量避免定义的模块名与内置函数(build in function)重名。

    2. 模块引入方法

    模块的引入使用import相关语句。
    在模块中可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行。
    import语句是可以在程序中的任意位置使用,同一个模块可以被多次引用
    为了防止在同一个文件中重复引入,python使用了一下优化方法:第一次导入后将模块名加载到内存,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句

    import 语句

    import module1[, module2[, module3[, ...moduleN]]]
    Python解释器通过import 语句在搜索路径中搜索相应的模块,
    模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
    若在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉

    from ... import 语句

    from modulename import name1[, name2[, ... nameN]]
    这个声明不会把整个modulename模块导入到当前的命名空间中,只会将它里面的name1name2单个引入到执行这个声明的模块的全局符号表

    from ... import *

    from modulename import *
    通过使用*提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。

    3. 为模块名起别名

    别名的使用可以简化编程
    两个模块xmlreader.pycsvreader.py,它们都定义了函数read_data(filename),用来从文件中读取一些数据,但采用不同的输入格式。可以通过为模块起别名实现使用同样的代码实现不同的功能,例如

    if file_format == 'xml':
        import xmlreader as reader
    elif file_format == 'csv':
        import csvreader as reader
    data=reader.read_date(filename)
    

    二. 包

    为了避免模块名冲突,Python引入了按目录来组织模块的方法,称为包(Package)。
    无论是import形式还是from...import,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法,点的左边都必须是一个包。
    包的本质就是一个包含__init__.py文件的目录。
    需要注意的是from后import导入的模块不能带点,否则会有语法错误,如:from a import b.c是错误语法

    1. __init__.py

    无论使用import还是from import,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件。

    2. from modulename import *

    Foo                      #Top-level package
    |__ __init__.py          #Initialize the glance package
    |
    |__ api                  #Subpackage for api
    |     |__ __init__.py
    |     |__ read.py
    |     |__ write.py
    |
    |__ cmd                  #Subpackage for cmd
    |     |__ __init__.py
    |     |__ control.py
    |
    |__ db                   #Subpackage for db
          |__ __init__.py
          |__ data.py
    

    此处是想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all___:

    #在__init__.py中定义
    x=10
    
    def func():
        print('from api.__init.py')
    
    __all__=['x','read', 'func']
    

    此时在与Foo同级的文件中执行from Foo.api import *就导入__all__中的内容,write仍然不能导入。

    3. 绝对导入和相对导入

    上述例子中最顶级包Foo是写给别人用的,然后在Foo包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
    绝对导入:以Foo作为起始
    相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)

    例如:我们在Foo/api/write.py中想要导入Foo/cmd/control.py

    在Foo/api/write.py
    
    #绝对导入
    from Foo.cmd import control
    control.ctrl()
    
    #相对导入
    from ..cmd import control
    control.ctrl()
    

    特别需要注意的是:可以用import导入内置或者第三方模块,但是要绝对避免使用import来导入自定义包的子模块,应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。

    __all__是用于控制from...import * ,不能用于使用import单独导入包

    4. 多层目录直接调用内部函数

    创建如下目录结构

    Foo
     |---test.py
     |---keystone
         |---auth
         |   |---plugins
         |   |   |---core.py
         |   |   |---__init__.py
         |   |
         |   |---__init__.py
         |
         |---__init__.py
    

    core.py内容为:

    def create(cls=None, auth_payload=None, method_name=None):
        print('function create')
        pass
    
    class UserAuthInfo:
        def __init__(self):
            self.password = None
        def foo(self):
            print('cls UserAuthInfo')
    

    要求:import keystone,然后就可以直接调用keystone.create和keystone.UserAuthInfo

    在keystone目录下的__init__.py中输入如下代码

    from .auth.plugins.core import create
    from .auth.plugins.core import UserAuthInfo
    

    在test.py中调用core.py中的函数

    import keystone
    keystone.create()
    a = keystone.UserAuthInfo()
    a.foo()
    
    >>function create
    >>cls UserAuthInfo
    

    5. 包与包之间模块导入

    目录结构及调用关系如下所示

    Foo
     |---dir1
     |   |---hello.py
     |---dir2
         |---main.py
         
    其中,hello.py:
    def add(x,y)
        return x+y
    
    main.py如何能调用到hello.py中的add函数。
    

    解决方法

    import os
    import sys
    BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 当前project的目录
    print(BASE_DIR)
    sys.path.append(BASE_DIR) # 添加当前project目录到环境变量
    
    from dir1 import hello
    
    print(hello.add(1, 2))
    

    相关文章

      网友评论

          本文标题:Python中模块与包

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