• 第六篇 模块和包


      首先,先介绍两个概念:

    • 模块:一个python文件(xxx.py)就是一个模块。我们可以在一个模块里定义变量、方法、类。
    • 包:模块通过包来组织和管理。从实现的方式来看,包是一个包含init.py的文件夹。

    1.1模块的分类

    • Python标准模块(Python内置模块、Python标准库)
    • 第三方模块/库(pypi.org)
    • 自定义模块

    1.2导入包的几种方式

    - import item.subitem.subsubitem
    - from package import item (推荐使用的方式)
    - from package import *

      现在假设我们有下面的文件结构,michasel.py、james.py和amanda.py的代码是类似的,除了name的值不一样:

    # 文件结构
    school
       boys 
           michael.py
           james.py
           __init__.py
       girls
           amanda.py
           __init__.py
       __init__.py
       
    # michael.py 示例代码
    name = 'Michael'
    
    def say_hello():
        print('hi, this is %s.' % (name, ))
    
    class Info(object):
        def __init__(self):
            pass
    
        def info(self):
            say_hello()

      默认情况下,我们不需要在init.py下添加任何代码就可以使用上述的前两种方式导入。

    • import item.subitem.subsubitem:使用这种方法,可以导入包或者模块,但是不能导入一个模块中定义的内容,比如模块中的类,方法或者变量等。并且引用包中的内容的时候,必须使用完整的名字(这里就是item.subitem.subsubitem)。一般使用这种方式的时候,我们可以给导入的包或者模块起别名,这样就可以避免使用很长的完整名。
    import school.boys.michael
    school.boys.michael.say_hello()
    michael.say_hello() # NameError: name 'michael' is not defined
    
    # 取别名
    import school.boys.james as james
    james.say_hello()
    • from package import item:从字面理解,就是从某个包中导入子包或者模块。除此之外,还可以导入某个模块中定义的类,方法或者变量等。同时,与上面那种方法相比,这种方法可以通过import后面的名字直接使用(而不需要使用完整名)。
    from school.boys import james
    james.say_hello()
    
    from school.boys.michael import name
    print(name)
    • from package import *:执行from package import * 的时候,本质是读取模块里的__all__属性,看这个属性里面定义了哪些变量和函数,如果模块里面没有定义__all__,才会导入所有不以_开头的变量和函数。
    # 没有定义__all__
    from school.boys import *
    james.say_hello() # NameError: name 'james' is not defined
    
    # 设置__all__ = __all__ = ['james', 'michael']
    from school.boys import *
    james.say_hello() # Correct 
    michael.say_hello() # Correct
    
    # 设置__all__ = [](空列表)
    from school.boys import *
    james.say_hello() # NameError: name 'james' is not defined

      __all__是一个列表,用于表示本模块可以被外界使用的成员,只对from xxx import * 这种导入方式生效,其余的方式都无效。

    1.3包内引用

      一个模块可以导入本包内的模块,也可以从邻包导入。导入的时候,可以使用相对位置或者绝对位置导入。需要注意的是,在主模块中导入不论本包或者邻包的模块的时候,总是要使用绝对位置,下面通过具体的示例来说明。

    ############## 引用相同包中的模块 ##############
    # 在james.py中引用michael.py中定义的name
    # 使用相对位置
    from .michael import name as michael_name
    print(michael_name)
    
    # 使用绝对位置
    from school.boys.michael import name as michael_name
    ################################################
    
    
    ############## 引用邻包中的模块 ################
    # 在james.py中引用amanda.py中定义的name
    # 使用相对位置
    from ..girls.amanda import name as amanda_name
    print(amanda_name)
    
    # 使用绝对位置
    from school.girls.amanda import name as amanda_name
    ################################################
    
    
    ############## 主模块(例如把james.py作为主模块来运行)中,必须使用绝对位置 ##############
    # 例如,在james.py中以相对位置导入michael.py,并且把james.py作为主模块运行,会出现一下错误
    # ImportError: attempted relative import with no known parent packagefrom .michael import name as michael_name
    print(michael_name)
    #####################################################################################

    1.4自定义init.py

    • 自动加载子模块:还是以本文开始的文件结构为例。现在,我们想在导入school.boys包的时候,自动就导入james.py和michael.py这两个模块。只需要在boys下的init.py中添加下面的代码
    # school/boys/__init__.py
    from . import james
    from . import michael
    
    # 在主模块中导入
    import school.boys
    school.boys.michael.say_hello()
    • 合并子模块为单一的逻辑模块:例如james.py下有james_greeting方法,michael.py下有michael_greeting方法,希望在主模块中调用的时候,看起来这两个方法属于同一个模块,比如属于boys这个包,这样就不需要导入michael.py、james.py
    # school/boys/__init__.py
    from .james import james_greeting
    from .michael import michael_greeting
    
    # 在主模块中导入
    import school.boys
    school.boys.michael_greeting()
    school.boys.james_greeting()
    • 包命名空间:文章一开始的部分就提到,我们可以在一个模块中导入一个包,一个包下面包含init.py文件。如果你好奇,或许你已经试过了 - 如果我们删除了包下面的init.py,我们依然可以在模块中导入这个包,这个神奇的魔法是通过“包命名空间”实现。当一个文件夹下不包含init.py的时候,那么解释器将把这个文件夹理解为包命名空间。这种用法,可能会用在一些开发框架中,对于框架的使用者而言,可以创建相同名称的文件夹并创建自己的模块,相当于把自己的模块整合到该开发框架中。我们可以输出某个包的str,通过判断是否包含"namespace"来判断是否是包命名空间。
    import school.boys
    print(school.boys.__path__)
    print(school.boys)
    
    # output
    
    # 包
    ['D:\Workspace\Python\PackageDemo\school\boys']
    <module 'school.boys' from 'D:\Workspace\Python\PackageDemo\school\boys\__init__.py'>
    
    # 包命名空间
    _NamespacePath(['D:\Workspace\Python\PackageDemo\school\boys'])
    <module 'school.boys' (namespace)>
    • 添加本地的包到项目中:有一种情况是,在公司的项目中,我们需要使用到一些自家的通用的包,这个包无法通过网络获取,但是在我们的开发环境中可以找到,那在我们开发的项目中,如何去导入呢?一种简单粗暴的方法是,将包所在的路径添加的系统变量PATHONPATH中(其实还可以pip install --user packagename 来安装私有包);另一个种是我们在项目中,修改sys.path,这样去告诉解释器去哪里找这个包。例如,现在我们把这个自家是包复制到项目是packages目录中:
    import sys
    from os.path import abspath, join, dirname
    sys.path.insert(0, join(abspath(dirname(__file__)), 'src'))

    1.5模块的搜索顺序

      Python中引用模块是按照一定的规则以及顺序去寻找的,这个查询顺序为:先从内存中已经加载的模块(当前包)进行寻找,找不到再从内置模块(安装路径下:Lib)中寻找,内置模块如果也没有,最后去sys.path中路径包含的模块中寻找。它只会按照这个顺序从这些指定的地方去寻找,如果最终都没有找到,那么就会报错。

      对于PYTHONPATH,一般不用,sys.path是最常用的。

      动态修改sys.path。

      os.path.dirname():获取某个路径的父路径。通常用于获取当前模块的相对路径。

    print(__file__) # 当前文件的绝对路径
    #D:/python_22/P16/123.py
    
    # 使用os模块获取一个路径的父路径
    print(os.path.dirname(__file__))
    # D:/python_22/P16
    
    print(os.path.dirname(__file__)+'/aa')
    # D:/python_22/P16/aa

    1.6模块属性

    • dir:列出对象的所有属性及方法
    • help:查看类,方法的帮助信息
    • __name__:模块的名称
    • __file__:文件全路径

    1.7每一个文件都应该是可以被导入的

      在实际开发中,每一个模块都是独立开发,开发人员通常会在模块下方增加一些测试代码,仅在模块内使用,而被导入到其他文件中时不需要执行。

      __name__属性可以做到测试模块的代码只在测试情况下被运行,而在被导入时不会被执行。

      __name__是Python的一个内置属性,记录着一个字符串,当被其他文件导入的时候,__name__就是模块名;但是当前执行的程序时,__name__就是__main__。

    # 测试一个模块的代码可以用
    if __name__ == '__main__':
        '''很多的测试代码'''
    # 这种方式来存储,这样在别的模块导入该模块时不会触发这些测试代码
    
    # 经典测试代码版本
    def main():
        '''很多的测试代码'''
    if __name__ == '__main__':
        main()

    1.8发布模块

    1.  创建setup.py
     1 # -*- coding=utf-8 -*-
     2 from distutils.core import setup
     3 
     4 setup(name="package_test",  # 包名
     5       version="1.0",  # 版本
     6       description="博小园的测试模块",  # 描述信息
     7       long_description="完整的博小园的测试模块",  # 完整的描述信息
     8       author="博小园",  # 作者
     9       author_email="admin@admin.com",  # 作者邮箱
    10       url="www.ceshi.com",  # 主页
    11       py_modules=["package_test.testa",  # Python模块列表
    12                   "package_test.testb"])

            2. 构建模块

    1 python3 setup.py build

            3.生成发布压缩包

    1 python3 setup.py sdist

            4.解压并安装模块

    1 python3 setup.py install

     说明:如果想要卸载模块,只在从安装目录下,把安装模块的目录删除即可。

    1.9pip安装第三方模块

    第三方模块是由知名的第三方团队开发的并且被程序员广泛使用的Python包/模块,pip是一个现代的,通用的Python包管理工具,提供了对Python包的查找,下载,安装,卸载等功能。

    1 # 安装和卸载pygame模块
    2 pip3 install pygame
    3 pip3 uninstall pygame
    4 
    5 # 安装iPython模块
    6 pip3 install ipython
  • 相关阅读:
    Pycharm
    Python
    navicat连接MySQL8.0出现2059错误
    MySQL Community Server 8.0.11下载与安装配置
    pip升级以及导入模块
    pycharm安装
    python环境安装
    js 超级玛丽(未完成)
    js 点名
    js 获取鼠标位置坐标
  • 原文地址:https://www.cnblogs.com/zhuzhaoli/p/10347738.html
Copyright © 2020-2023  润新知