zoukankan      html  css  js  c++  java
  • Python 核心概念

    包、模块等核心概念 

    什么是模块

    当我们新建一个python file,这个时候形成一个.py后缀的文件,这个文件就称之为模块 

    什么是包?包跟普通的目录有什么区别?

    pycharm中,我们右键可以创建一个目录,也可以创建一个包,两者看起来差不多,唯一的区别在于,创建包的时候,包下面会有一个__init__.py的文件,这也是python为了区分目录跟包所作出的界定

    包与子包

    包下面,还能新建包,称之为子包 

    命名空间 

    什么是命名空间

    命名空间是变量到对象的映射集合。一般都是通过字典来实现的。主要可以分为三类:

    1. 每个函数都有着自已的命名空间,叫做局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。
    2. 每个模块拥有它自已的命名空间,叫做全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
    3. 还有就是内置命名空间,任何模块均可访问它,它存放着内置的函数和异常。

    通俗点讲:命名空间就是为了确定全局唯一,当模块A中有变量c,模块B中也有一个变量c,此时,通过A.c来确定引用A中变量c.123 

    比如在class2模块中要引用class1中的变量a,在导入class1模块之后,可以使用class1.a访问class1的变量 

    命名空间的查找顺序

    当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序: 

    1. 局部命名空间:特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数xPython 将使用它,然后停止搜索。
    2. 全局命名空间:特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python将使用它然后停止搜索。
    3. 内置命名空间:对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。
    4. 如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如:NameError: name 'xxx' is not defined。 

    当函数嵌套时的查找规则

    1. 先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
    2. 然后是在父函数的命名空间中搜索
    3. 接着是模块命名空间中搜索
    4. 最后在内置命名空间中搜索 
    msg = "msg"
    
    def my_func():
        name = " wiggin "
        def func_son():
            name = "xdclass " # 此处的name变量,覆盖了父函数的name变量
            print(name)
        # 调用内部函数
        func_son()
        print(name)
    
    my_func()

    命名空间的生命周期

    1. 内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。
    2. 模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。
    3. 当函数被调用时创建一个局部命名空间,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间。 
    a = 1
    
    def my_func(str):
        if a == 1:
            print(str)
            a = 24
    
    my_func("file")

    上面的程序会在报错,UnboundLocalError: local variable 'a' referenced before assignment, 错误的意思就是a这个变量在引用前还没有定义,这上面不是定义了么? 

    • python的函数中和全局同名的变量,如果你有修改变量的值就会变成局部变量,在修改之前对该变量的引用自然就会出现没定义这样的错误了,如果确定要引用全局变量,并且要对它修改,必须加上global关键字。 

    对上面的错误进行修改,如下

    a = 1
    
    def my_func(str):
        global a
        if a == 1:
            print(str)
            a = 24
    
    my_func("file")
    print(a)
    
    输出结果:
    file
    24

    命名空间的访问

    局部命名空间可以 locals() 来访问 

    def my_func():
        a = 1
        b = 2
        print(locals())
    
    my_func()
    
    输出结果:
    {'a': 1, 'b': 2}

    locals 返回一个名字/值对的 dictionary。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。 

    全局 (模块级别)命名空间可以通过 globals() 来访问

    a = 1
    b = 2
    print(globals())
    
    输出结果:
    {
    '__name__': '__main__',
    '__doc__': None,
    '__package__': None,
    '__loader__': <_frozen_importlib_external.SourceFileLoader object at
    0x00000255B062F548>,
    '__spec__': None,
    '__annotations__': {},
    '__builtins__': <module 'builtins' (built-in)>,
    '__file__': 'C:/Users/wiggin/Desktop/xdclass/chapter12/class2.py',
    '__cached__': None,
    'a': 1, 'b': 2
    }

    locals globals 之间的区别 

    • locals 是只读的,但globals是可读写的 
    def my_func():
        x = 123
        print(locals())
        locals()["x"] = 456
        print("x=", x)
    
    y = 123
    my_func()
    globals()["y"] = 111
    print("y=", y)

    导入模块 

    python中,可以使用import 关键字进行模块的导入,语法如下:import module_name

    例如,在模块class2中要引用同目录下class1中的变量a,此时可以如下 

    import class2
    
    print(class2.a)

    这个时候,需要使用命名空间来访问相应的变量

    这样导入似乎很轻松,但是如果模块名长了,代码写起来就有点别扭了,比如: 

    import xdclass_python_chapter12_class3
    
    print(xdclass_python_chapter12_class3.name)

    这样的代码看起来非常拖沓,这个时候我们想让代码看起来简短些,我们可以使用别名,具体语法如下:import module_name as alias 

    # 导入模块并重命名为xd
    import xdclass_python_chapter12_class3 as xd
    
    print(xd.name)

    import导入时,究竟做了什么事? 

    1. 查找一个模块,如果有必要还会加载并初始化模块。
    2. 在局部命名空间中为 import 语句发生位置所处的作用域定义一个或多个名称。 

    当一个模块首次被导入时,Python 会搜索该模块,如果找到就创建一个 module 对象并初始化它。 如果指定名称的模块未找到,则会引发 ModuleNotFoundError。 当发起调用导入机制时,Python 会实现多种策略来搜索指定名称的模块。 

    注意:当模块首次被导入时,会执行模块里面的代码 

    我们修改下代码,xdcass_python_chapter12_class3模块里的内容改成: 

    print("hello world")

    此时,我们仅仅导入模块,之后运行

    import xdclass_python_chapter12_class3 as xd
    
    输出结果:
    hello world

    还有另外的方法可以导入模块:

    使用importlib模块进行模块的导入,基本语法如下

    import importlib
    importlib.import_module("module_name")

    例如:

    import importlib
    module = importlib.import_module("xdclass_python_chapter12_class3")

    如果想导入另一个包中的模块,可以使用如下语法:

    from package import module

    如果想导入多层包中的模块,可以使用如下语法:

    from package.son import module

    导入变量 

    能否像导入一个模块那样单独导入一个模块里的某个变量?带着疑问先动手 

    import class3.a as a
    
    print(a)
    
    运行结果:
    Traceback (most recent call last):
    File "C:/Users/wiggin/Desktop/xdclass/chapter12/class4.py", line 1, in
    <module>
    import class3.a as a
    ModuleNotFoundError: No module named 'class3.a'; 'class3' is not a package

    显然,此路不通,那么如何导入一个变量呢?我们是以使用如下语法

    from module import variable

    其语义也非常好理解,字面上的意思就是从xxx导入xxx 

    from class3 import a
    print(a)

    这个时候再去运行就会发现不会报错,能正常运行

    同样注意:当模块首次被导入时,会执行模块里面的代码

    我们修改class3的内容:

    a = 1111
    print("hello world")

    再次运行class4文件,就会发现,同样也会先输出hello world,再打印a的值。

    如果要导入多个变量,可以使用逗号分隔

    from class3 import a, b
    
    print(a)
    print(b)

    当要导入的变量非常多的时候,可以使用*进行导入 

    class3.py 

    a = 1
    b = 2
    c = 3
    from class3 import *
    print(a)
    print(b)
    print(c)

    虽然支持*通配符进行导入,但是不建议过多使用,因为使用*导入,阅读代码这难以理清其语义

    导包机制 

    • 导入期间,会在 sys.modules 查找模块名称,如存在则其关联的值就是需要导入的模块,导入过程完成。 然而,如果值为 None ,则会引发 ModuleNotFoundError。 如果找不到指定模块名称,Python 将继续搜索该模块。
    • 如果指定名称的模块在 sys.modules找不到,则将发起调用 Python 的导入协议以查找和加载该模块。 此协议由两个概念性模块构成,即 查找器和 加载器。 查找器的任务是确定是否能使用其所知的策略找到该名称的模块。 同时实现这两种接口的对象称为 导入器——它们在确定能加载所需的模块时会返回其自身。 

    大白话理解导入机制 

    对于 from class5_import import a 

    • sys.modules中查找符号"class5_import"
    • 如果符号存在,则获得符号class5_import对应的module对象<moduleclass5_import>
      • <module class5_import>dict中获得符号"a"对应的对象,如果"a"不存在,则抛出异常
    • 如果符号class5_import不存在,则创建一个新的module对象<moduleclass5_import>,注意,这时,module对象的dict为空
    • 执行class5_import.py中的表达式,填充<module class5_import>dict
    • <module class5_import>dict中获得"a"对应的对象,如果"a"不存在,则抛出异常 

    以下两模块,能否正常导入 

    模块 class5.py 

    from class5_import import a
    
    b = 11
    print(a)
    
    from class5_import import ab = 11print(a)

    模块 class5_import.py

    from class5 import b
    
    a = 1

    执行过程如下: 

    1、执行class5.py中的from class5_import import a

    • 由于是执行的python class5.py,所以在sys.modules中并没有<module class5_import>在,首先为B.py创建一个module对象(<module class5_import>)注意,这时创建的这个module对象是空的,里边啥也没有,Python内部创建了这个module对象之后,就会解析执行class5_import.py,其目的是填充<module class5_import>这个dict

    2、执行class5_import.py中的 from class5 import b

    • 在执行class5_import.py的过程中,会碰到这一句,首先检查sys.modules这个module缓存中是否已经存在<module class5>了,由于这时缓存还没有缓存<module class5>所以类似的,Python内部会为class5.py创建一个module对象(<module class5>)然后,同样地,执行class5.py中的语句

    3、再次执行class5.py中的from class5_import import a

    • 这时,由于在第1步时,创建的<module class5_import>对象已经缓存在了sys.modules中,所以直接就得到了<module class5_import>但是,注意,从整个过程来看,我们知道,这时<module class5_import>还是一个空的对象,里面啥也没有,所以从这个module中获得符号"a"的操作就会抛出异常。
    • 如果这里只是import class5_import,由于"class5_import"这个符号在sys.modules中已经存在,所以是不会抛出异常的。 

    流程图如下

    __init__.py的作用及用法

    __init__.py的作用

    • 标志所在目录是一个模块包
    • 本身也是一个模块
    • 可用于定义模糊导入时要导入的内容 

    在前面的课程中,我们使用from package import * 会报错误,如果想使用该语法不报错,可以__init__.py中定义要导入的模块

    我们可以在__init__.py文件中,使用__all__ = ['module_name1','module_name2']定义*号匹配时要导入的模块,之后再导入的时候,就可以使用*通配符进行模糊导入 

    • 导入一个包的时候,包下的__init__.py中的代码会自动执行
    • 用于批量导入模块 

    当我们的许多模块中,都需要导入某些公共的模块,此时,可以在__init__.py中进行导入,之后直接导入该包即可 

     

    __all____name__的作用及其用法 

    __all__的作用及其用法

    • 在普通模块中使用时,表示一个模块中允许哪些属性可以被导入到别的模块中
    • 在包下的__init__.py,可用于标识模糊导入时的模块

    __name__的作用及其用法

    • __name__这个系统变量显示了当前模块执行过程中的名称,如果当前程序运行在这个模块中,__name__ 的名称就是__main__如果不是,则为这个模块的名称。
    • __main__一般作为函数的入口,类似于C语言,尤其在大型工程中,常常有if __name__ =="__main__":来表明整个工程开始运行的入口。 
    def my_fun():
        if __name__ == "__main__":
        print("this is main")
    
    my_fun()
  • 相关阅读:
    2013-9-29 通信原理学习笔记
    《大数据时代》阅读笔记
    《人人都是产品经理》阅读笔记一
    2013-8-13 信道接入技术研究学习
    2013-8-6 ubuntu基本操作
    2013-7-30 802.1X企业级加密
    2013-7-29 杂记
    2013-7-28 802.11n帧聚合
    2013-7-27 802.1X学习
    vue+node+mongoDB前后端分离个人博客(入门向)
  • 原文地址:https://www.cnblogs.com/jwen1994/p/13127377.html
Copyright © 2011-2022 走看看