• python学习之模块


    python学习之模块

    模块

    基本概念

    在python中,模块是相对于命令行执行的一个概念。如果我们抛开IDE,在cmd下调用python并使用命令行执行命令,就会存在一个问题,前边命令创建的变量在后续执行中无法保存和使用。而模块就是为此存在,简单的说模块就是一组变量、函数、类的集合,到这里我们就可以发现,其实单个的python源代码文件就是一个模块。

    #test.py
    def test():
        print("this is a module test")
    a=test
    a()
    print(dir())
    print(__name__)
    

    输出

    this is a module test
    ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'test']
    __main__

    上边代码中dir()的作用是输出当前已注册的命名,可以简单的理解为当前可以使用的变量、函数、类等。__name__是当前的模块名,如果__name__="__main__“则表示当前模块是这次执行的入口,也就是说这次是由python程序直接执行test.py,而非其它模块引用。

    模块引用

    当然,一个模块是可以引入另一个模块的,我们可以在同目录下创建另一个模块:

    #test2.py
    print("this is module test2")
    def test2Function():
        print("this is a function in module test2")
    print("this is test2 module name:"+__name__)
    

    我们可以使用import moduleName的方式引入test2模块:

    #test.py
    import test2
    def test():
        print("this is a module test")
    a=test
    a()
    print(dir())
    print(__name__)
    test2.test2Function()
    

    输出

    this is module test2
    this is test2 module name:test2
    this is a module test
    ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'test', 'test2']
    __main__
    this is a function in module test2

    这样在test中就可以执行test2的函数了,import moduleName的本质就是把moduleName加入当前命名空间,可以在当前直接使用moduleName这个模块。但现在还有个问题,我们注意到test2中函数体外的print也直接输出了,这表明一旦我们使用了import moduleName,相应模块中没包在函数体或者类中的程序都将执行,这在我们一些情况下是不希望的结果。这里需要用到__name__,我们可以这样:

    #test2.py
    if __name__=="__main__":
        print("this is module test2")
    def test2Function():
        print("this is a function in module test2")
    if __name__=="__main__":
        print("this is test2 module name:"+__name__)
    

    这样再执行test时候就不会显示this is module test2了,如果直接执行test2,依然会显示。这就是前边所说的,利用__name__区分当前模块是不是程序入口。

    此外,我们可以使用from moduleName import funcName的方式直接引用某个模块的单个函数。

    #test.py
    from test2 import test2Function
    print(dir())
    test2Function()
    

    输出

    ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test2Function']
    this is a function in module test2

    可以看到,当前命名空间中没有test2,而是直接加载了test2Function,调用的时候自然不需要模块名,可以直接使用test2Function

    如果要一次引用单个模块的多个函数,可以这样:from moduleName import func1,func2...,也可以from moduleName import *。但是后者要谨慎使用,因为可能污染当前的命名空间。

    包是常见的代码组织方式,python同样支持。现在假设我们有一组时间工具函数,组织成一个包。工作目录结构为:

    • time_tools
      • __init__.py
      • date.py
      • time.py
    • test.py

    初始化文件

    包中的__init__.py文件是包的初始化文件,当包被引用的时候会执行。包中之所以会存在这个文件,主要有两点用途。

    1. 区分包和普通的文件夹,避免编译器对无效文件目录的检索。
    2. 可以在包所属的__init__.py文件中编写引入包时需要的初始化逻辑,或者定义整个包共享的工具方法等。
    #__init__.py
    #比较两个时间戳大小
    def compareTimestamp(time1,time2):
        if time1>time2:
            return 1
        elif time1==time2:
            return 0
        else:
            return -1
    
    #date.py
    #显示当前日期
    def showNowDate():
        print("now date is X year X month X date")
    #判断今天是否工作日
    def isWorkDay():
        return True
    
    #time.py
    #显示当前时间
    def showNowTime():
        print("now time is XX:XX:XX")
    #获取当前时间戳
    def getTimestamp():
        return "time stamp"
    

    包引用

    我们可以使用import package1.package2.module的方式来引入其它包的模块:

    #test.py
    import time_tools.date
    import time_tools.time
    time_tools.date.showNowDate()
    time_tools.time.showNowTime()
    

    输出

    now date is X year X month X date
    now time is XX:XX:XX

    但这样的话每次使用都要使用package1.package2.module.func()这样,如果要更方便的使用模块,可以使用from package1.package2 import module的方式:

    #test.py
    from time_tools import time
    from time_tools import date
    date.showNowDate()
    time.showNowTime()
    

    但同样的,这样做要注意污染命名空间的问题,如果遇到了,或许可以这样:

    #test.py
    import time_tools.date
    import time_tools.time
    timeToosDate=time_tools.date
    timeToosTime=time_tools.time
    timeToosDate.showNowDate()
    timeToosTime.showNowTime()
    

    与使用from module import *类似,我们也可以使用from pack1.pack2 import *一次性引用包下边所有的模块。比如:

    #test.py
    from time_tools import *
    print(compareTimestamp(111,222))
    time.showNowTime()
    

    输出

    -1
    Traceback (most recent call last):
    File "d:workspacepython est.py", line 4, in
    time.showNowTime()
    NameError: name 'time' is not defined

    可以看到__init__.py文件中的函数可以正常调用,但对time模块的调用报错,并没有正常引用。

    这涉及另一个问题。每当我们引入一个模块,编译器就会去文件目录查找,但如果是使用*一次性引入某个包下边的所有模块,那就是编译器直接读取文件目录下的所有文件,然后加载。这看似没有问题,但在windows下有个致命问题——windows的文件目录是不区分大小写的。这就导致python的编译器不知道一个模块echo.py是要加载为echo还是ECHO甚至Echo。为了解决这个问题,在使用*加载包时候,需要在包的初始化文件中显示地指定包下边的模块名,比如:

    #__init__.py
    #比较两个时间戳大小
    __all__=["date","time"]
    def compareTimestamp(time1,time2):
        if time1>time2:
            return 1
        elif time1==time2:
            return 0
        else:
            return -1
    

    这样就可以正常加载了:

    #test.py
    from time_tools import *
    # print(compareTimestamp(111,222))
    time.showNowTime()
    

    输出

    now time is XX:XX:XX

    此时包time_tools本身并没有被加载,所以compareTimestamp无法使用

    参考资料

    https://www.runoob.com/python3/python3-module.html

    标准库

    标准库是python自带的模块,已经封装了一些常用功能。

    random

    random模块提供随机数的相关功能:

    import random
    print(random.random())
    #生成一个随机整数
    print(random.randint(1,10))
    #在一个list中随机选定多个
    print(random.sample([1,2,3,4,5,6],3))
    

    输出

    0.7140202136447632
    2
    [1, 6, 5]

    sys

    sys模块可以输出一些python系统信息,比如主程序的输入参数,编译器的检索路径等:

    import sys
    print(sys.argv)
    print(sys.path)
    sys.path.append("D:\worksapce\python\time_tools")
    print(sys.path)
    

    sys.argv会返回主程序输入参数,sys.path则是一个包含了编译器检索用的目录列表,而可以通过sys.path.append()的方式追加目录。

    第三方模块

    pip的第三方模块存放在pypi.org,检索相当方便,并不会如同github一般经常性抽风。

    第三方模块的安装和管理工具是pip,经常使用的命令有:

    pip install moduleName
    pip uninstall moduelName
    pip list #列出已安装模块
    

    you-get

    下面用一个视频下载模块you-get举例:

    在cmd下输入

    pip install you-get
    

    等待片刻后就安装好了,接下来可以试着直接下载视频:

    you-get https://www.bilibili.com/video/BV1y7411m7Xj
    

    需要注意的是you-get.exe的所在的目录需要加入系统环境变量,而且电脑要重启生效后才能在cmd下直接调用you-get

    当然,pip安装的不仅仅是可执行程序,还安装了python源码,可以直接在python中调用:

    from you_get import common
    common.any_download(url="https://www.bilibili.com/video/BV1y7411m7Xj",output_dir="d:\download",merge=True,info_only=False,stream_id="flv")
    

    输出

    site: Bilibili
    title: 你这么可爱 可惜不会谈恋爱
    stream:
    - format: flv
    container: flv
    quality: 高清 1080P
    size: 17.7 MiB (18576369 bytes)
    # download-with: you-get --format=flv [URL]

    Downloading 你这么可爱 可惜不会谈恋爱.flv ...
    100% ( 17.7/ 17.7MB) ├████████████████████████████████████████┤[1/1] 7 MB/s

    Skipping captions or danmaku.

    • 我比较迷惑的是pypi官网并没有该模块的python调用文档,不知道是为什么。

    • 关于第三方库的更多内容可以阅读简明教程

    本篇文章首发自魔芋红茶的博客https://www.cnblogs.com/Moon-Face/ 请尊重其他人的劳动成功,转载请注明。
  • 相关阅读:
    非递归的中序遍历(inorder)树 leetcode 94
    基于二叉树的抢劫问题 leetcode337
    查询二叉树的公共父结点 leetcode 236
    链表的无锁操作 (JAVA)
    kexec 内核快速启动流程分析
    最近几天严重营养不良。。。
    Better Me
    《大四上寒假总结》--3.10
    《计算机网络》学习总结
    记录
  • 原文地址:https://www.cnblogs.com/Moon-Face/p/14460098.html
Copyright © 2020-2023  润新知