Python模块和包

模块

模块的定义和导入

  • 模块就是包含Python代码的文件,它可以作为独立的功能文件被其它程序调用。换句话说,就是将实现某些功能的函数封装在一个文件里,在另一个程序中导入/调用。

  • 为什么要使用模块

    • 程序太大,编写、维护不方便,需要拆分。
    • 模块可以增加代码重复利用率
    • 当做命名空间使用,避免命名冲突
      • 当前程序中的命名可以与模块中的命名相同
  • 如何定义模块

    • 模块是一个普通的Python文件,可以直接编写
    • 模块也有自己的规范,它包含以下内容:
      • 函数(单一功能):模块中可以包含多个函数,但每个函数只实现单一的功能。
      • 类:相似功能的组合。
      • 测试代码:一般用于测试模块是否导入成功。
  • 如何使用模块

    • 模块导入:

      • “import modelu_name”或者“form modelu_name import function_name” 的格式从程序中导入需要的模块,然后再使用其功能函数。

        • 前者:方便,直接将模块中的所有函数导入程序,使用时直接调用即可。注:需要用前缀调用功能函数,即使用模块名调用函数。
        • 后者:只调用指定的函数,其它的函数就不会被导入,减少代码冗余。好处:直接调用功能函数,无需前缀调用。
      • 对于form……import导入方式,一般写成form……import*,这样表示可以调用模块下的所有功能函数了。

      • import 模块名 as 别名

        • as:是为模块取一个别名,可以用这个别名来调用模块中的功能函数
        • 作用与前面的一样
      • if _name_ == "_main_"的使用:

        • 为了避免模块导入成功时,测试代码被动执行,可以使用此代码让测试代码不执行。
        • 建议所有程序的入口都以此代码为入口
    • 模块的使用:

      • 模块名.函数名(功能名):使用这个格式就可以调用模块文件中的某个功能函数,这种方式适合“import function_name”模式。
      • 注意:模块名(也就是文件名)的命名需符合标识符命名规范,否则导入模块时会出错。
      • 如果模块是以数字开头,那么需要借助importlib来导入这个模块文件。

模块导入举例

举例1: 我们对某值进行开方操作,就需要用到sqrt()功能函数。但sqrt()函数不能直接被调用,它是属于math模块的函数,所以需要先导入math模块。

    # 先导入math模块
    import math

    a = 144

    # 调用功能函数的格式:模块名.函数名
    print(mach.sqrt(a))

结果:

    12.0

**举例2:

我们可以自己来定义一个模块,里面包含一个功能函数:先定义一个Student类,再为它定义一个函数用来介绍自己。接着,我们在模块中重新定义一个新的函数用来说“hello”。

步骤1: 先创建一个名为“student.py”的文件,它就是要导入的模块的模块名,然后在里面定义Student类和其它需要的功能函数sayHello。

在student文件中: 这是student模块

# 定义Student类,它就相当于功能函数。
class Student ():

    def __init__ (self, name, age):
        print("我是Student模块中的构造函数!")
        self.name = name
        self.age = age
        return

    def say (self):
        print("大家好,我叫{},我今年{}岁了!".format(self.name, self.age))
        return


# 定义一个同级的函数sayHello

def sayHell ():
    print("hello, Python!")

# 测试函数,用来测试模块是否导入成功。
print("测试该模块是否导入成功!")

其中,Student和sayHello函数为同级,均由“student.”调用。最后的print函数用于测试模块是否能成功被导入,当模块被成功导入时,程序中成打印这行文字。

步骤2: 创建完模块文件后,我们再创建一个程序文件,用于导入模块,实现想要的功能。如创建一个名为“模块测试.py”的文件。

模块测试文件中:

    # 导入模块
    import student

    # 实例化对象
    # 使用模块名调用功能函数Student
    # 这一步相当于使用类Student实例化变量student
    student1 = student.Student("Tom", 25)

    # 调用Student类中的方法函数
    student1.say()

    # 调用模块中的其它功能函数
    student.sayHell()

结果:

    测试该模块是否导入成功!
    我是Student模块中的构造函数!
    大家好,我叫Tom,我今年25岁了!
    hello,Python!

其中,程序运行时,出现的第一行结果正是student模块中的测试函数,说明此时模块导入成功。

然后,第二行结果是模块中构造函数中的内容。我们知道,_init_是构造函数,同时它也是魔法函数,即当实例化对象时它就会执行,说明student1 = student.Student()是在实例化对象。

使用“模块名.函数名”的格式成功调用Student这个功能函数,然后实例化对象student1就可以调用这个类的功能函数了。

最后,我们也通过模块名调用了sayHello这个函数。

思考:通过以上我们可以发现,如果将模块student中的内容和“模块测试”这两个文件中的内容合并,也可以得到相同的效果,但为何还是要将它们分开呢?

  • 模块student中的内容是可以重复使用的部分,将它和其它程序分开,利于代码维护,当程序中的代码越来越多时,这个效果明显。
  • 模块student不仅可以被“模块测试”这个程序调用,还可以被其它程序调用。将它分开,利于被其它文件调用,这样更显得模块的作用和重要性。

关于importlib的使用

一般情况下,模块名(封装功能函数的文件名)的命名是要符合标识符命名规范的,也就是不能以数字开头。

但如果真的数字开头的模块,且我们又需要导入使用,那么就需要借助importlib,如,我们将先前定义的student模块名修改为“01”,然后在程序文件中修改代码:

    # 导入importlib
    import importlib

    # 定义一个变量,使用importlib导入模块并赋值给这个变量
    student2 = importlib.import_modelu("01")

    # 现在使用student2这个变量实例化对象
    student1 = student2.Student()

    student1.say()

当然,经过这一操作,其结果与先前的一样。

from modelu_name import function_name, class_name的使用:

    # 导入模块中的指定功能函数,其它函数不会被导入
    from student import Student 

    # 实例化对象,直接使用函数,无需前缀。
    student1 = Student()

    student1.say()

    sayHello()

可以看到,使用“from modelu_name import function_name”的模式导入模块时,调用功能函数无需使用student这个前缀,直接使用Student类名了,调用sayHello函数时,也是无需前缀。

关于“if _name_ = '_main_'”程序入口

在前面的模块student中,有一个功能函数print(),它的作用是在模块导入成功后就立刻打印执行,这个作用固然不错,但有时模块导入成功也无需特意打印执行。
所以,为了避免导入模块时被动执行该语句,可以使用“if _name_ = '_main_'”,修改方式直接在student文件中修改print()代码即可:

    if __name__ = "__main__":
        print("这是测试代码,模块导入成功后执行,现需要屏蔽!")

当执行程序文件,模块导入成功后,该语句不会被执行。

模块的搜索路径和存储

  • 什么是模块的搜索路径

    • 加载模块时,系统会在什么地方寻找模块
  • 系统默认的模块搜索路径

    • import sys:导入模块。
    • sys.path:获取所有可能的路径列表。
  • 添加模块的存储路径

    • 我们知道,sys.path是列表,那么可以使用list的append()方法自定义添加存储路径
    • sys.path.append(dir)
    • dir:表示自定义路径如:sys.path.append("C:\Users\Administrator\Desktop\Path")表示模块可以存储在桌面文件Path当中。
  • 模块的搜索顺序

    • 既然导入模块会查找模块,那么定有搜索的顺序:
      • 先搜索内存中已经加载完毕的模块
      • 搜索Python的内置模块
      • 搜索sys.path中的模块

模块存储路径的获取:

    # 导入sys模块
    import sys

    # 查看路径path的数据类型
    print(type(sys.path))

    # 获取模块可能存储的路径
    for i in sys.path:
        print(i)

结果:

    
    C:\Users\Administrator\Desktop\Work
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37\DLLs
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\win32
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\win32\lib
    C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\Pythonwin

可见,模块的存储路径是以列表的形式存在的,说明它的存储路径可能有多个。只要将自定义的模块或系统自带的模块存储到这些路径当中,其它程序就可以导入模块并调用。

返回结果中,第一行的路径名为Work,这是存储我们当前程序自定义的存储文件,如果将我们的模块放入到这个文件中即当前文件夹中,也可以访问到模块的,例如上面的student模块。

  • 包就是组织管理代码的方式,里面存放着模块。

  • 存放模块的文件夹就是包。

  • 包的结构:

      |---包
      |---|---\__init__ .py 标志文件
      |---|---模块1
      |---|---模块2
      |---|---子包(包中的包)
      |---|---|---\__init__.py 标志文件
      |---|---|---子模块1
      |---|---|---子模块2
    
  • 包的导入

    • import package_name

      • 导入一个包,这是默认使用包中的_init_.py模块文件,使用的功能函数也是属于该文件的。

      • 明确导入的包名

      • 使用方式:

          package_name.func_name()
          package_name.class_name.func_name()
        
    • import package_name as 别名

      • 该方式与前面取别名的方式相似
    • import package_name.module_name

      • 导入一个包,调用包中所有模块中的功能函数。

      • 明确导入的包名跟模块名

      • 使用方式:

          package_name.module_name.function_name()
          package_name.module.name.class_name.function_name()
          pakcage_name.module.name.class_name.var
        
      • 这种导入方式需要使用前缀调用功能函数

    • from package_name import module_name1, module_name2, ...

      • 此种方式不执行_init_.py中的内容

      • 使用方式

          module_name.function_name()
          module_name.class_name.function_name()
        
    • from package_name import *

      • 使用方式

          function_name()
          class_name.function_name()
          class_name.var
        
      • 这种导入方式无需前缀,直接调用功能函数

      • 注:这种导入包的方式,只能导入_init_.py中的内容,其它模块不能导入进来。

    • from package_name.module_name import *

      • 导入包中指定模块中的所有内容

      • 使用方式:

          function_name()
          class_name.function_name()
          class_name.var
        
      • 这种导入方式就可以使用到包中除了_init_.py之外的其它模块中的功能函数

+ \__all__的使用
    * 我们知道,from package_name import * 表示导入\__init__.py中的内容,但当\__init__.py中的内容为空,且又没有\__all__时,*导入的是空内容
    * 当\__init__.py中有\__all__时,它可以导入\__all__指定的其它模块,即导入\__init__.py文件时,也可以使用其它模块了。但不会导入\__init__.py中的其它内容,只会导入\__all__指定的内容。
    * 格式:\__all__ = ["module_name1", "module_name2", "package_name1", ...]
    * 使用方法:

            module_name1.function_name()
            module.name1.class_name.function_name()

包的使用的举例

我们要创建一个名为package的包,然后在里面创建一个名为_init_.py的文件,接着又分别创建名为packModule_1.py和packModule_2.py的两个文件。

举例1:

在_init_.py文件中写入以下代码:

    def init ():
        print("这是__init__文件中的内容!")

接着在另一个程序文件中导入package模块并调用这个功能函数:

    # 导入模块,导入模块名
    import package

    # 直接使用模块名调用功能函数,就直接调用到__init__文件中函数
    package.init()

结果:

    这是__init__文件中的内容!

可见,当以这种方式导入模块和调用功能函数时,它是默认调用_init_文件中的功能函数。但有时,我们需要调用的是包中的其它模块,所以,想要调用其它模块,可以使用“backage_name.module_name”的格式。

举例2:

现在,我们来导入package模块,并调用它里面的两个模块实现相关功能。

在模块packModule_1文件中,写入如下代码:

    # 这是包中的一个模块 packModule_1
    class Student ():

        def __init__ (self, name, age):
            print("我是Student模块中的构造函数!")
            self.name = name
            self.age = age
            return

        def say (self):
            print("大家好,我叫{},我今年{}岁了!".format(self.name, self.age))
            return


    # 测试代码
    if __name__ == "__main__":
        print("这是测试代码1")

这是如之前的例子定义了一个名为Student的类,并为它定义了方法函数。

接着,在模块packModule_2中写入如下代码:

    # 这是packModule_2

    def sayHello ():
        print("hello Python!")


    if __name__ == "__main__":
        print("这是测试代码2")

这是如之前一样定义了一个sayHello函数。

最后,我们在程序文件中导入package模块,并调用里面的模块实现功能:

    # 导入包
    # 格式:package_name.module_name
    import package.packModule_2
    import package.packModule_1


    # 调用包中的模块中的功能函数
    # 格式:package_name.module_name.function_name()
    package.packModule_2.sayHello()


    # 调用包中的模块中的类
    # 格式:package_name.module_name.class_name.function_name()
    # 以下操作是在实例化对象
    student1 = package.packModule_1.Student("Jon", 20)

    # 调用类中的方法函数
    student1.say()

运行结果:

    hello Python!
    我是Student模块中的构造函数!
    大家好,我叫Jon,我今年20岁了!

可见,用包的效果与之前直接调用模块的效果是一样的。

思考:

  • 拿模块与对象、list等比较,我们可以发现,模块名相当于对象名、列表名,它只是一个指向对象的引用而已,当调用或者访问功能函数或元素时,使用的格式“模块名.函数名”、“对象名[key]”、“列表名[索引]”。

  • 模块的访问也是相似的,如访问包中的模块中的某函数,它的格式大概为:包名.模块名.函数名()。如果访问是类,那么格式:包名.模块名.类名.函数名()。

  • _init_是特殊的标志函数,访问它直接是:包名.函数名()。

**关于from package_name import * **导入方式的特点

这种导入包的方式,只能导入_init_.py文件中的内容,也只能调用这个文件中的功能函数,其它模块是无法导入进来的。

举例:

我们将module_1模块中的“if __name == '__main'”去掉,直接修改成“print("这是测试代码!")”用来测模块是否导入成功。

    # 使用星号导入包
    from package import *

    # 调用__init__.py中的功能函数
    init()

    # 尝试调用module_1模块中的功能函数
    module_1.sayHello()

结果:

    这是init中的测试代码!
    Traceback (most recent call last):
    File "测试包的例子.py", line 13, in 
        sayHello()
    NameError: name 'sayHello' is not defined

可以看到,_init_.py中的功能函数可以直接被调用,而其它模块的根本就没有被导入进来,因为“print()”测试函数并没有提示导入成功。

**from package_name.module_name import * **的举例

针对以上导入方式,如果我们想要访问包中的其它模块,可以使用“from package_name.module_name import *”的导入方式。

举例:

现在我们将上述导入包的方式修改一番:

    # 导入包中的module_2这个模块
    from package.module_2 import *

    # 然后直接调用这个模块中的功能函数
    sayHello()

结果:

    hello Python!

可以看到,这个导入方式是无法访问到_init_.py中的内容的,这样可以调用想要调用的模块。

关于_all_的使用

我们知道,使用“from package_name import * ”这个导入方式,程序默认只会调用_init_.py文件中的内容。

但,如果我们使用这个导入方式又想调用包中的其它模块,可以使用_all_来指定导入的模块。

注: 不管_init.py文件中有没有其它内容,只要定义了_all并指定了要导入的其它模块,那么该文件下的其它内容都不会被导入,即使使用的是“from package_name import * ”的导入方式。

现在我们在包中的_init_.py文件中添加代码:

    # 在__all__中指定导入packModule_2和packModule_1这两个模块
    __all__ = ["packModule_2", "packModule_1"]

    # 定义一个功能函数,测试是否会被导入
    def __init__ ():
        print("这是init中的内容!")

然后在程序文件中书写代码:

    from package import *

    # 调用packModule_2中的功能函数
    packModule_2.sayHell()

    # 调用packModule_1中的功能函数
    student1 = packModule_1.Student("God", 18)

    # 调用packModule_1中Student的方法函数
    student1.say()

    # 调用__init__.py中的其它功能函数,测试文件中的内容能否被导入
    init()

结果:

    hello Python!
    我是Student模块中的构造函数!
    大家好,我叫God,我今年18岁了!
    Traceback (most recent call last):
     File "测试包的例子.py", line 20, in 
        init()
    NameError: name 'init' is not defined

从打印结果来看,packModule_1和packModule_2这两个模块均导入成功。而当调用_init_.py文件中的其它内容时出现了错误,说明其它内容并没有被导入进来。

命名空间

  • 用于区分不同位置****不同功能相同名称的函数或变量的一个特定前缀

  • 作用:防止命名冲突

      setName()
      Student.setName()
      Peser.setName()
    
  • 相同的函数名,属于不同的类或空间

你可能感兴趣的:(Python模块和包)