zoukankan      html  css  js  c++  java
  • 模块与包

    模块

    什么是模块
    一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。
    为什么要使用模块
    如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用
    首次导入模块做的三件事
    1.开辟名称空间,用来存放spam.py中定义的名字
    2.基于刚创建的名称空间,将模块执行一遍
    3.创建名字spam指向该名称空间,spam.名字的操作,都是以spam.py为准
    spam.py

    # 模块spam.py
    print("from th spam.py")
    money=1000
    
    def read1():
        print('spam->read1->money', money)
    
    def read2():
        print('spam->read2 calling read')
        read1()
    
    def change():
        global money
        money = 10
    

    test.py

    # 文件test.py
    import spam
    import sys
    # print(sys.modules) # 查看当前已加载的模块
    
    money = 1231213  # 测试文件money与spam.money不冲突
    print(spam.money)  # 1000
    spam.read1()       # spam->read1->money 1000
    def read1():
        print("from test read1")
    
    spam.read2()      # spam->read2 calling read spam->read1->money 1000
    spam.change()     
    print(spam.money) # 10
    

    为模块名起别名

    import spam as sm
    print(sm.money) # 1000
    

    为已经导入的模块起别名的方式对编写可扩展的代码很有用,假设有两个模块xmlreader.py和csvreader.py,它们都定义了函数read_data(filename):用来从文件中读取一些数据,但采用不同的输入格式。可以编写代码来选择性地挑选读取模块,例如

    1 if file_format == 'xml':
    2     import xmlreader as reader
    3 elif file_format == 'csv':
    4     import csvreader as reader
    5 data=reader.read_date(filename)
    

    from ... import...
    import spam,会将源文件的名称空间'spam'带到当前名称空间中,使用时必须是spam.名字的方式

    而from 语句相当于import,也会创建新的名称空间,但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了.

    # test.py文件
    from spam import money,read1,read2
    print(money)    # 1000
    money = 123
    print(money)    # 123
    read1()         # spam->read1->money 1000 此时read1中的money仍以spam中的为准
    read2()         # spam->read2 calling read      spam->read1->money 1000
    

    from spam import*
    from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。

    from spam import * #将模块spam中所有的名字都导入到当前名称空间
    print(money)
    print(read1)
    print(read2)
    print(change)
    
    '''
    执行结果:
    from the spam.py
    1000
    <function read1 at 0x1012e8158>
    <function read2 at 0x1012e81e0>
    <function change at 0x1012e8268>
    '''
    

    if name == 'main':用来控制.py文件在不同的应用场景下执行不同的逻辑
    模块搜索路径
    内存中已经加载的模块->内置模块->sys.path路径中包含的模块
    我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错.
    在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。
    .pyc文件
    为了提高模块的加载速度,Python缓存编译的版本,每个模块在__pycache__目录的以module.version.pyc的形式命名,通常包含了python的版本号,如在CPython版本3.3,关于spam.py的编译版本将被缓存成__pycache__/spam.cpython-33.pyc,这种命名约定允许不同的版本,不同版本的Python编写模块共存。

    Python检查源文件的修改时间与编译的版本进行对比,如果过期就需要重新编译。这是完全自动的过程。并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享,即pyc使一种跨平台的字节码,类似于JAVA火.NET,是由python虚拟机来执行的,但是pyc的内容跟python的版本相关,不同的版本编译后的pyc文件不同,2.5编译的pyc文件不能到3.5上执行,并且pyc文件是可以反编译的,因而它的出现仅仅是用来提升模块的加载速度的。
    sys.path.append修改模块找寻路径

    # 修改sys.path来改变,模块找寻路径
    possible_topdir = os.path.normpath(os.path.join(
        os.path.abspath(__file__), # 当前文件的路径
        os.pardir, #上一级
        os.pardir, # 上一级
        os.pardir  # C:UsersouPycharmProjectspy_fullstack_s4day35os模块.py......
    ))
    print("---------------")
    print(possible_topdir)  # C:UsersouPycharmProjects
    
    sys.path.insert(0,possible_topdir) # 模块第一查找路径
    

    包的本质就是一个包含__init__.py文件的目录。

    # 有如下glance包
    glance/                   #Top-level package
    
    ├── __init__.py      #Initialize the glance package
    
    ├── api                  #Subpackage for api
    
    │   ├── __init__.py
    
    │   ├── policy.py
    
    │   └── versions.py
    
    ├── cmd                #Subpackage for cmd
    
    │   ├── __init__.py
    
    │   └── manage.py
    
    └── db                  #Subpackage for db
    
        ├── __init__.py
    
        └── models.py
    

    无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法

    # glance的同级测试
    import glance.db.models
    glance.db.models.register_models("mysql") # from models.py:  mysql
    
    # 需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法
    from glance.db import models
    models.register_models("msql") # from models.py:  mysql
    
    
    from glance.db.models import register_models
    register_models("MTSQL") # from models.py:  MTSQL
    

    __init__文件
    不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码
    **from glance.api import ***
    在讲模块时,我们已经讨论过了从一个模块内导入所有,此处我们研究从一个包导入所有

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

    # api的__init__
    print("from api __init__")
    x = 10
    def foo():
        print("from api.__init__.py")
    
    __all__ = ["x", "foo", "policy", "versions"]
    

    此时我们在于glance同级的文件中执行from glance.api import *就导入__all__中的内容,导入了policy和version模块。
    绝对导入和相对导入
    绝对导入时,包的名字改变时,就会出错,推荐使用相对导入

    # 相对导入,推荐使用
    # glance下的__init__
    print("from glance __init__")
    x = 10
    def foo():
        print("from glan.__init__.py")
    
    # all = ["x", "foo", "api"]
    from .api.policy import get
    from .api.versions import create
    from .cmd.manage import main
    from .db.models import register_models
    
    # test.py
    import glance
    glance.get()                   # from policy.py
    glance.main()                  # from manage.py
    glance.create("ww")            # from version.py:  ww
    glance.register_models("www")  # from models.py:  www
    
  • 相关阅读:
    串口调试助手
    自己动手编写俄罗斯方块
    ASP.NET Core log4net
    ASP.NET Core读取配置文件
    ASP.NETCore3 MVC
    ASP.NETCore2C#7.0新语法
    ASP.NETCore1C#6.0新语法
    C#加密解密
    前端通用的滚动条样式
    C# 106 短信发送
  • 原文地址:https://www.cnblogs.com/zouruncheng/p/6798178.html
Copyright © 2011-2022 走看看