zoukankan      html  css  js  c++  java
  • Python学习笔记:模块和包

    第九章 模块和包

    模块化编程

    导入模块的语法

    使用import导入模块,主要有两种用法:

    • import 模块名1[as 别名1],模块名2[as 别名2],... : 导入整个模块
    • from 模块名 import 成员名1[as 别名1], 成员名2[as 别名2],...,:导入模块中指定成员

    上面两种import语句的区别主要有两点:

    • 第一种import语句导入模块内的所有成员(包括变量、函数、类等);第二种import语句只导入模块内的指定成员(除非使用 from 模块名 import *)
    • 当使用第一种import语句导入模块中的成员时,必须添加模块名或模块别名作为前缀;当使用第二种import语句导入模块中的成员时,无须使用任何前缀,直接使用成员名或成员别名即可


    使用import导入模块介绍

    # 导入sys整个模块
    import sys
    # 使用sys模块名作为前缀来访问模块中的成员
    print(sys.argv[0])
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    

    为模块指定别名,示例:

    # 导入sys整个模块,并指定别名为s
    import sys as s
    # 使用s模块别名作为前缀来访问模块中的成员
    print(s.argv[0])
    

    当指定了别名,就必须使用别名作为前缀,如果仍旧使用模块名,则会报错:

    # 导入sys整个模块,并指定别名为s
    import sys as s
    # 使用s模块别名作为前缀来访问模块中的成员
    print(s.argv[0])
    
    print("------------")
    # 仍旧使用模块名作为前缀,则会报错
    print(sys.argv[0])
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    ------------
    Traceback (most recent call last):
    
      File "C:Userszz.spyder-py3	emp.py", line 8, in <module>
        print(sys.argv[0])
    
    NameError: name 'sys' is not defined
    

    一次导入多个模块,多个模块之间用逗号隔开,示例:

    # 导入sys、os两个模块
    import sys,os
    # 使用模块名作为前缀来访问模块中的成员
    print(sys.argv[0])
    # os模块的sep变量代表平台上的路径分隔符
    print(os.sep)
    
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    
    

    为多个模块指定别名,示例:

    # 导入sys、os两个模块,并为sys指定别名s,为os指定别名o
    import sys as s,os as o
    # 使用模块别名作为前缀来访问模块中的成员
    print(s.argv[0])
    print(o.sep)
    

    使用from ... import 导入模块内指定成员的用法介绍

    # 导入sys模块的argv成员
    from sys import argv
    # 使用导入成员的语法,直接使用成员名访问,不能添加前缀
    print(argv[0])
    

    如果为成员名添加模块名前缀,则会报错,示例:

    # 导入sys模块的argv成员
    from sys import argv
    # 使用导入成员的语法,直接使用成员名访问
    print(argv[0])
    
    print("--------------------")
    
    # 如果为成员名添加模块名前缀,则会报错
    print(sys.argv[0])
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    --------------------
    Traceback (most recent call last):
    
      File "C:Userszz.spyder-py3	emp.py", line 9, in <module>
        print(sys.argv[0])
    
    NameError: name 'sys' is not defined
    

    为成员名指定别名,示例:

    # 导入sys模块的argv成员,并为其指定别名v
    from sys import argv as v
    # 使用导入成员(并指定别名)的语法,直接使用成员的别名访问
    print(v[0])
    
    # 如果指定了别名,就必须使用别名,若仍使用成员名,则会报错
    print(argv[0])
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    Traceback (most recent call last):
    
      File "C:Userszz.spyder-py3	emp.py", line 7, in <module>
        print(argv[0])
    
    NameError: name 'argv' is not defined
    

    导入多个模块,示例:

    # 导入sys模块的argv,winver成员
    from sys import argv, winver
    # 使用导入成员的语法,直接使用成员名访问
    print(argv[0])
    print(winver)
    

    为多个模块指定别名,示例:

    # 导入sys模块的argv,winver成员,并为其指定别名v、wv
    from sys import argv as v, winver as wv
    # 使用导入成员(并指定别名)的语法,直接使用成员的别名访问
    print(v[0])
    print(wv)
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    3.7
    

    小结:

    • 一旦为模块名或成员名指定了别名,则在程序中必须使用别名,若仍旧使用模块名作为前缀则会报错、若仍旧使用成员名也会报错
    • 使用成员名访问时,不能添加模块名作为前缀,若强制添加模块名作为前缀则会报错

    使用from ... import语法时也可一次导入指定模块内的所有成员,程序即可使用成员名来使用该模块内的所有成员。(不推荐使用这种语法)示例:

    # 导入sys模块的所有成员
    from sys import *
    # 使用导入成员的语法,直接使用成员的别名访问
    print(argv[0])
    print(winver)
    
    输出结果:
    C:Userszz.spyder-py3	emp.py
    3.7
    

    定义模块

    模块到底是什么?模块就是python程序,任何python程序都可作为模块导入。对于任何程序,只要导入了模块,即可使用该模块内的所有成员。
    module1.py代码:

    '''
    这是我们编写的第一个模块,该模块包含以下内容:
    my_book:字符串变量
    say_hi:简单的函数
    User:代表用户的类
    '''
    print('这是module 1')
    my_book = '疯狂Python讲义'
    def say_hi(user):
        print('%s,您好,欢迎学习Python' % user)
    class User:
        def __init__(self, name):
            self.name = name
        def walk(self):
            print('%s正在慢慢地走路' % self.name)
        def __repr__(self):
            return 'User[name=%s]' % self.name
    

    使用模块的好处在于:如果将程序需要使用的程序单元(比如modulel.py定义的say_hi()函数、User类)定义在模块中,后面不管哪个程序,只要导入该模块,该程序即可使用该模块所包含的程序单元,这样就可以提供很好的复用一一导入模块,使用模块,从而避免每个程序都需要重新定义这些程序单元。
    模块文件的文件名就是它的模块名,比如module1.py的模块名就是module1。

    为模块编写说明文档

    在实际开发中也应该为模块编写说明文档,否则,其他开发者都不知道该模块有什么作用,以及包含哪些功能
    为模块编写说明文档很简单,只要在模块开始处定义一个字符串直接量即可,可通过模块的__doc__属性访问文档。module1.py的第一行代码之前添加如下内容,这段内容将作为该模块的说明文档。

    '''
    这是我们编写的第一个模块,该模块包含以下内容:
    my_book:字符串变量
    say_hi:简单的函数
    User:代表用户的类
    '''
    

    为模块编写测试代码

    当模块编写完成之后,可能还需要为模块编写一些测试代码,用于测试模块中的每一个测试单元是否都能正常运行。
    希望实现的效果:如果直接使用python命令运行该模块(相当于测试),程序应该执行该模块的测试函数;如果是其他程序导入该模块,程序不应该执行该模块的测试函数。此时可借助于所有模块内置的__name__变量进行区分,如果直接使用Python命令来运行一个模块,name__变量的值为__main;如果该模块导入被导入其他程序中,__name__变量的值就是模块名。因此,如果希望测试函数只有在使用python命令直接运行时才执行,则可在调用测试函数
    时增加判断:只有当__name__属性为__main__时才调用测试函数。为模块增加如下代码即可

    '''
    这是我们编写的第一个模块,该模块包含以下内容:
    my_book:字符串变量
    say_hi:简单的函数
    User:代表用户的类
    '''
    print('这是module 1')
    my_book = '疯狂Python讲义'
    def say_hi(user):
        print('%s,您好,欢迎学习Python' % user)
    class User:
        def __init__(self, name):
            self.name = name
        def walk(self):
            print('%s正在慢慢地走路' % self.name)
        def __repr__(self):
            return 'User[name=%s]' % self.name
              
            
    # ===以下部分是测试代码===
    def test_my_book ():
        print(my_book)
    def test_say_hi():
        say_hi('孙悟空')
        say_hi(User('Charlie'))
    def test_User():
        u = User('白骨精')
        u.walk()
        print(u)
    # 当__name__为'__main__'(直接使用python运行该模块)时执行如下代码
    if __name__ == '__main__':
        test_my_book()
        test_say_hi()
        test_User()
    

    使用python module1.py命令来运行该模块,输出结果:

    这是module 1
    疯狂Python讲义
    孙悟空,您好,欢迎学习Python
    User[name=Charlie],您好,欢迎学习Python
    白骨精正在慢慢地走路
    User[name=白骨精]
    

    加载模块

    在编译一个python模块之后,如果直接用import或from ... import来导入该模块,python通常并不能加载该模块。道理很简单:python怎么知道到哪里去找这个模块呢?
    为了让python能找到我们编写(或第三方提供)的模块,可以使用两种方式来告诉它:

    • 使用环境变量
    • 将模块放在默认的模块加载路径之下

    使用环境变量

    Python将会根据PYTHONPATH环境变量的值来确定到哪里去加载模块。PYTHONPATH环境变量的值是多个路径的集合,这样Python就会依次搜索PYTHONPATH环境变量所指定的多个路径,试图从中找到程序想要加载的模块。


    在windows平台上设置环境变量
    一般建议设置“用户变量”即可,因为用户变量只对当前用户有效,而系统变量对所有用户有效。为了减少自己所做的修改对其他人的影响,故设置用户变量。对于当前用户而言,设置用户变量和系统变量的效果大致相同,不过系统变量的路径排在用户变量的路径之前。
    在“变量名”文本框内输入PYTHONPATH,表明将要建立名为PYTHONPATH的环境变量:在“变量值”文本框内输入 .;d:python_module,这就是该环境变量的值,该值其实包含了两条路径(分号为分隔符),第一条路径为一个点(.),这个点代表当前路径,表明当运行Python程序时,Python总能从当前路径加载模块:第二条路径为d:python_module,表明当运行Python程序时,Python总能从d:python_module加载模块。
    在成功设置了上面的环境变量之后,接下来只要把前面定义的模块(Python程序)放在与当前所运行Python程序相同的路径中(或放在d:python_module路径下),该模块就能被成功加载了。
    提示:设置完环境变量后,需要重启 spyder 编辑器,让spyder重新加载环境变量的值。

    # 导入module1,并指定其别名为md
    import module1 as md
    print(md.my_book)
    md.say_hi('Charlie')
    user = md.User('孙悟空')
    print(user)
    user.walk()
    
    输出结果:
    这是module 1
    疯狂Python讲义
    Charlie,您好,欢迎学习Python
    User[name=孙悟空]
    孙悟空正在慢慢地走路
    

    默认的模块加载路径

    python默认的模块加载路径由sys.path变量代表,可通过在交互式解释器中输入如下命令来查看:

    import sys,pprint
    
    pprint.pprint(sys.path)
    ['C:\Users\zz',
     'D:\Python\python_module',
     'D:\6.CommonTools\anaconda2\envs\python37\python37.zip',
     'D:\6.CommonTools\anaconda2\envs\python37\DLLs',
     'D:\6.CommonTools\anaconda2\envs\python37\lib',
     'D:\6.CommonTools\anaconda2\envs\python37',
     '',
     'D:\6.CommonTools\anaconda2\envs\python37\lib\site-packages',
     'D:\6.CommonTools\anaconda2\envs\python37\lib\site-packages\win32',
     'D:\6.CommonTools\anaconda2\envs\python37\lib\site-packages\win32\lib',
     'D:\6.CommonTools\anaconda2\envs\python37\lib\site-packages\Pythonwin',
     'D:\6.CommonTools\anaconda2\envs\python37\lib\site-packages\IPython\extensions',
     'C:\Users\zz\.ipython']
    

    如果要打印的内容很多,使用pprint可以显示更友好的打印结果。
    通常来说,我们应该将python的扩展模块添加在 libsite-packages 路径下,它专门用于存放python的扩展模块和包。
    下面编写一个python模块文件(print_shape.py),并将该文件复制到 libsite-packages 目录下面,就相当于为python扩展了一个 print_shape 模块,这样任何python程序都可使用该模块,示例:

    # coding: utf-8
    '''
    简单的模块,该模块包含以下内容
    my_list:保存列表的变量
    print_triangle: 使用星号打印三角形的函数
    '''
    my_list = ['Python', 'Kotlin', 'Swift']
    def print_triangle(n):
        '''使用星号打印一个三角形'''
        if n <= 0:
            raise ValueError('n必须大于0')
        for i in range(n):
            print(' ' * (n - i - 1), end='')
            print('*' * (2 * i + 1), end='')
            print('')
    
    # ====以下是测试代码====
    def test_print_triangle():
        print_triangle(3)
        print_triangle(4)
        print_triangle(7)
    if __name__ == '__main__': test_print_triangle()
    

    交互式解释器中测试该模块:

    (base) C:Userszz>conda activate python37
    
    (python37) C:Userszz>python
    Python 3.7.9 (default, Aug 31 2020, 17:10:11) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import print_shape
    >>> print(print_shape.__doc__)
    
    简单的模块,该模块包含以下内容
    my_list:保存列表的变量
    print_triangle: 使用星号打印三角形的函数
    
    >>> print_shape.print_triangle.__doc__
    '使用星号打印一个三角形'
    >>> print_shape.my_list[1]
    'Kotlin'
    >>> print_shape.print_triangle(5)
        *
       ***
      *****
     *******
    *********
    >>> 
    

    从上面可以看到,程序通过模块名前缀访问my_list变量,输出了该变量的第二个元素。

    导入模块的本质

    定义一个新的模块(fk_module.py),该模块比较简单,示例:

    '一个简单的测试模块: fkmodule'
    print("this is fk_module")
    name = 'fkit'
    
    def hello():
        print("Hello, Python")
    
    

    接下来,在相同路径下定义如下程序来使用该模块,示例:

    import fk_module
    
    print("================")
    # 打印fk_module的类型
    print(type(fk_module))
    print(fk_module)
    print(fk_module.name)
    print(fk_module.hello)
    

    输出结果:

    this is fk_module
    ================
    <class 'module'>
    <module 'fk_module' from 'C:\Users\zz\.spyder-py3\fk_module.py'>
    fkit
    <function hello at 0x000001577127FC18>
    

    使用“import fk_module”导入模块的本质就是:将fk_module.py中的全部代码加载到内存井执行,然后将整个模块内容赋值给与模块同名的变量(fk_module),该变量的类型是module,而在该模块中定义的所有程序单元都相当于该module对象的成员。


    再试试使用from ... import 语句来执行导入,示例:

    from fk_module import name, hello
    
    print("================")
    print(name)
    print(hello)
    # 打印fk_module
    print(fk_module)
    

    输出结果:

    this is fk_module
    ================
    fkit
    <function hello at 0x00000157712CE678>
    Traceback (most recent call last):
    
      File "C:Userszz.spyder-py3	emp.py", line 7, in <module>
        print(fk_module)
    
    NameError: name 'fk_module' is not defined
    

    使用 "from fk_module import name, hello" 导入模块中成员的本质是:将fk_module.py中的全部代码加载到内存并执行,然后只导入指定变量、函数等成员单元,并不会将整个模块导入,因此上面程序在输出fk_module时将看到错误提示:name 'fk_module' is not defined


    在导入模块后,可以在模块文件所在目录下看到一个名为"pycache"的文件夹,打开该文件夹,可以看到Python为每个模块都生成一个*.cpython-36.pyc文件,比如Python为fk_module模块生成一个fk_module.cpython-36.pyc文件,该文件其实是Python为模块编译生成的字节码,用于提升该模块的运行效率。

    模块的__all__变量

    在默认情况下,如果使用“from 模块名 import *”这样的语句来导入模块,程序会导入该模块中所有不以下画线开头的程序单元,这是很容易想到的结果。
    有时候模块中虽然包含很多成员,但并不希望每个成员都被暴露出来供外界使用,此时可借助于模块的__all__变量,将变量的值设置成一个列表,只有该列表中的程序单元才会被暴露出来。
    定义一个包含__all__变量的模块(all_module.py)

    '测试__all__变量的模块'
    
    def hello():
        print("Hello, Python")
    def world():
        print("Pyhton World is funny")
    def test():
        print('--test--')
    
    # 定义__all__变量,指定默认只导入hello和world两个程序单元
    __all__ = ['hello', 'world']
    

    示范模块中__all__变量的用处

    # 导入all_module模块内所有成员
    from all_module import *
    hello()
    world()
    test() # 会提示找不到test()函数
    
    输出结果:
    Hello, Python
    Pyhton World is funny
    Traceback (most recent call last):
    
      File "C:Userszz.spyder-py3	emp.py", line 5, in <module>
        test() # 会提示找不到test()函数
    
    NameError: name 'test' is not defined
    

    上面程序显示,只导入了__all__变量所列的程序单元,函数test()默认没有被导入。


    __all__变量的意义在于为模块定义了一个开放的公共接口。通常来说,只有__all__变量列出的程序单元,才是希望该模块被外界使用的程序单元。因此,为模块设置__all__变量还是比较有用的。比如一个实际的大模块可能包含了大量其他程序不需要使用的变量、函数和类,那么通过__all__变量即可把它们自动过滤掉,这还是非常酷的。


    如果确实希望程序使用模块内__all__列表之外的程序单元,有两种解决方法:

    • 第一种是使用“import 模块名”来导入模块。在通过这种方式导入模块之后,总可以通过模块名前缀(如果为模块指定了别名,则使用模块的别名作为前缀)来调用模块内的成员。
    • 第二种是使用"from 模块名 import 程序单元”来导入指定程序单元。在这种方式下,即使想导入的程序单元没有位于all列表中,也依然可以导入。

    使用包

    对于一个需要实际应用的模块而言,往往会具有很多程序单元,包括变量、函数和类等,如果将整个模块的所有内容都定义在同一个Python源文件中,这个文件将会变得非常庞大,显然并不利于模块化开发。

    什么是包

    为了更好地管理多个模块源文件,python提供了包的概念。那么什么是包呢?

    • 从物理上看,包就是一个文件夹,在该文件夹下包含了一个__init__.py文件,该文件夹可用于包含多个模块源文件
    • 从逻辑上看,包的本质依然是模块

    包的作用是包含多个模块,但包的本质依然是模块,包可用于包含包。

    定义包

    定义包很简单,主要有两步:

    1. 创建一个文件夹,该文件夹的名字就是该包的包名
    2. 在该文件夹内添加一个 init.py文件即可

    下面定义一个非常简单的包。先创建一个 first_package 文件夹,在该文件夹中添加一个 init.py 文件,该文件内容如下:

    '''
    这是学习包的第一个示例
    '''
    print('this is first_package')
    

    该文件开始部分的字符串是该包的说明文档。通过如下程序使用该包,示例:

    # 导入first_package包(模块)
    import first_package
    
    print('==========')
    print(first_package.__doc__)
    print(type(first_package))
    print(first_package)
    
    
    
    输出结果:
    this is first_package
    ==========
    
    这是学习包的第一个示例
    
    <class 'module'>
    <module 'first_package' from 'C:\Users\zz\.spyder-py3\first_package\__init__.py'>
    

    再次强调,包的本质就是模块,因此导入包和导入模块的语法完全相同。
    从上面的输出结果可以看出,在导入first_package包时,程序执行了该包所对应的文件夹下的__init__.py;从倒数第二行输出可以看到,包的本质就是模块;从最后一行输出可以看到,使用"import first_package"导入包的本质就是加载并执行该包下的__init__.py文件,然后将整个文件内容赋值给与包同名的变量,该变量的类型是module。


    与模块类似的是,包被导入之后,会在包目录下生成一个__pycache__一文件夹,并在该文件夹内为包生成一个__init__.cpython-37.pyc文件。


    由于导入包就相当于导入该包下的__init__.py文件,因此我们完全可以在__init__.py文件中定义变量、函数、类等程序单元,但实际上往往并不会这么做。想一想原因是什么?包的主要作用是包含多个模块,因此__init__.py文件的主要作用就是导入该包内的其他模块。下面再定义一个更加复杂的包,在该包下将会包含多个模块,并使用__init__.py文件来加载这些模块。


    新建一个 fk_package 包,包含三个模块文件:

    • arithmetic_chart.py
    def print_multiple_chart(n):
        '打印乘法口角表的函数'
        for i in range(n):
            for j in range(i + 1):
                print('%d * %d = %2d' % ((j + 1) , (i + 1) , (j + 1)* (i + 1)), end='  ')
            print('')
    
    
    • billing.py
    class Item:
        '定义代表商品的Item类'
        def __init__(self, price):
            self.price = price
        def __repr__(self):
            return 'Item[price=%g]' % self.price
    
    • print_shape.py
    def print_blank_triangle(n):
        '使用星号打印一个空心的三角形'
        if n <= 0:
            raise ValueError('n必须大于0')
        for i in range(n):
            print(' ' * (n - i - 1), end='')
            print('*', end='')
            if i != n - 1:
                print(' ' * (2 * i - 1), end='')
            else:
                print('*' * (2 * i - 1), end='')
            if i != 0: 
                print('*')
            else:
                print('')
    

    init.py 文件暂时为空,不用编写任何内容。
    这意味着:fk_package 包(也是模块)总共包含这三个模块。在这种情况下,这三个模块就相当于fk_package 包的成员。

    导入包内成员

    # 导入fk_package包,实际上就是导入包下__init__.py文件
    import fk_package
    # 导入fk_package包下的print_shape模块,
    # 实际上就是导入fk_package目录下的print_shape.py
    import fk_package.print_shape
    # 实际上就是导入fk_package包(模块)导入print_shape模块
    from fk_package import billing
    # 导入fk_package包下的arithmetic_chart模块,
    # 实际上就是导入fk_package目录下的arithmetic_chart.py
    import fk_package.arithmetic_chart
    
    fk_package.print_shape.print_blank_triangle(5)
    im = billing.Item(4.5)
    print(im)
    fk_package.arithmetic_chart.print_multiple_chart(5)
    
    
    输出结果:
        *
       * *
      *   *
     *     *
    *********
    Item[price=4.5]
    1 * 1 =  1  
    1 * 2 =  2  2 * 2 =  4  
    1 * 3 =  3  2 * 3 =  6  3 * 3 =  9  
    1 * 4 =  4  2 * 4 =  8  3 * 4 = 12  4 * 4 = 16  
    1 * 5 =  5  2 * 5 = 10  3 * 5 = 15  4 * 5 = 20  5 * 5 = 25  
    

    image.png
    image.png

    # 从当前包导入print_shape模块
    from . import print_shape
    # 从.print_shape导入所有程序单元到fk_package中
    from .print_shape import *
    # 从当前包导入billing模块
    from . import billing
    # 从.billing导入所有程序单元到fk_package中
    from .billing import *
    # 从当前包导入arithmetic_chart模块
    from . import arithmetic_chart
    # 从.arithmetic_chart导入所有程序单元到fk_package中
    from .arithmetic_chart import *
    

    image.png

    # 导入fk_package包,实际上就是导入包下__init__.py文件
    import fk_package
    
    # 直接使用fk_package前缀即可调用它所包含的模块内的程序单元。
    fk_package.print_blank_triangle(5)
    im = fk_package.Item(4.5)
    print(im)
    fk_package.print_multiple_chart(5)
    

    输出结果:

        *
       * *
      *   *
     *     *
    *********
    Item[price=4.5]
    1 * 1 =  1  
    1 * 2 =  2  2 * 2 =  4  
    1 * 3 =  3  2 * 3 =  6  3 * 3 =  9  
    1 * 4 =  4  2 * 4 =  8  3 * 4 = 12  4 * 4 = 16  
    1 * 5 =  5  2 * 5 = 10  3 * 5 = 15  4 * 5 = 20  5 * 5 = 25  
    

    上面粗体字代码是导入fk_package包,导入该包的本质就是导入该包下的__init__.py文件。而__init__.py文件又执行了导入,它们会把三个模块内的程序单元导入fk_package包中,因此程序的下面代码可使用fk_package.前缀来访问三个模块内的程序单元。

    查看模块内容

    在导入模块之后,开发者往往需要了解模块包含哪些功能,比如包含哪些变量、哪些函数、哪些类等,还希望能查看模块中各成员的帮助信息,掌握这些信息才能正常地使用该模块。

    模块包含什么

    为了查看模块包含什么,可以通过如下两种方式:

    • 使用dir()函数 (返回模块或类所包含的全部程序单元,包括变量、函数、类和方法等)
    • 使用模块本身提供的__all__变量
    import string
    
    dir(string)
    Out[24]: 
    ['Formatter',
     'Template',
     '_ChainMap',
     '_TemplateMetaclass',
     '__all__',
     '__builtins__',
     '__cached__',
     '__doc__',
     '__file__',
     '__loader__',
     '__name__',
     '__package__',
     '__spec__',
     '_re',
     '_string',
     'ascii_letters',
     'ascii_lowercase',
     'ascii_uppercase',
     'capwords',
     'digits',
     'hexdigits',
     'octdigits',
     'printable',
     'punctuation',
     'whitespace']
    

    很明显,该模块内有大量以下划线开头的程序单元,其实这些程序单元并不希望被其他程序使用,因此列出这些程序单元意义不大,可以使用列表推导式过滤掉,示例:

    [e for e in dir(string) if not e.startswith('_')]
    Out[25]: 
    ['Formatter',
     'Template',
     'ascii_letters',
     'ascii_lowercase',
     'ascii_uppercase',
     'capwords',
     'digits',
     'hexdigits',
     'octdigits',
     'printable',
     'punctuation',
     'whitespace']
    

    也可通过该模块的__all__变量来查看模块内的程序单元(有些模块不提供__all__变量,只能使用上面列表推导式来查看),如下

    tring.__all__
    Out[26]: 
    ['ascii_letters',
     'ascii_lowercase',
     'ascii_uppercase',
     'capwords',
     'digits',
     'hexdigits',
     'octdigits',
     'printable',
     'punctuation',
     'whitespace',
     'Formatter',
     'Template']
    

    使用__doc__属性查看文档

    使用help()函数之所以能查看到程序单元的帮助信息,其实完全是因为该程序单元本身有文档信息,也就是有__doc__属性。换句话说,使用help()函数查看的其实就是程序单元的__doc__属性值。
    提示:Python库的参考文档:https://docs.python.org/3/library/index.html

    使用__file__属性查看模块的源文件路径

    通过模块的__file__属性即可查看到指定模块的源文件路径。开发者完全可以根据路径打开查看该模块的全部源代码。
    需要说明的是,并不是所有模块都是使用Python语言编写的,有些与底层交互的模块可能是用C语言编写的,而且是C程序编译之后的效果,因此这种模块可能没有__file__属性。


    原文来源于我的语雀,我的微信公众号:细细研磨

  • 相关阅读:
    Java 性能优化实战记录(3)--JVM OOM的分析和原因追查
    Map/Reduce的类体系架构
    Map/Reduce个人实战--生成数据测试集
    Ubuntu 16.04下的安装RabbitMQ
    Zookeeper数据存储总结
    Zookeeper原理分析之存储结构ZkDatabase
    Zookeeper原理分析之存储结构TxnLog
    Zookeeper原理分析之存储结构Snapshot
    Kafka迁移与扩容工具用法
    Sublime Text 格式化JSON-pretty json
  • 原文地址:https://www.cnblogs.com/onelikeone/p/15162999.html
Copyright © 2011-2022 走看看