模块
这篇博文是之前的博文的整理和重发,以和目前发的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
# ['__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
# ['__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_v2 import test2Function
print(dir())
test2Function()
# ['__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
文件是包的初始化文件,当包被引用的时候会执行。包中之所以会存在这个文件,主要有两点用途。
-
区分包和普通的文件夹,避免编译器对无效文件目录的检索。
-
可以在包所属的
__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()
# now date is X year X month X date
# now time is XX:XX:XX
但同样的,这样做要注意污染命名空间的问题,如果遇到了,或许可以这样:
#test.py
import time_tools.date
import time_tools.time
timeToosDate=time_tools.date
timeToosTime=time_tools.time
timeToosDate.showNowDate()
timeToosTime.showNowTime()
# now date is X year X month X date
# now time is XX:XX:XX
与使用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:\workspace\python\python-learning-notes\note5\test.py", line 4, in <module>
# 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
无法使用
参考资料
标准库
标准库是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.9078117032490299
# 9
# [3, 2, 1]
sys
sys模块可以输出一些python系统信息,比如主程序的输入参数,编译器的检索路径等:
import sys
print(sys.argv)
print(sys.path)
sys.path.append("D:\\worksapce\\python\\time_tools")
print(sys.path)
# ['D:\\workspace\\python\\python-learning-notes\\note5\\test.py']
# ['D:\\workspace\\python\\python-learning-notes\\note5', 'D:\\software\\Coding\\Python\\python39.zip', 'D:\\software\\Coding\\Python\\DLLs', 'D:\\software\\Coding\\Python\\lib', 'D:\\software\\Coding\\Python', 'C:\\Users\\70748\\AppData\\Roaming\\Python\\Python39\\site-packages', 'D:\\software\\Coding\\Python\\lib\\site-packages', 'D:\\software\\Coding\\Python\\lib\\site-packages\\you_get-0.4.1500-py3.9.egg']
# ['D:\\workspace\\python\\python-learning-notes\\note5', 'D:\\software\\Coding\\Python\\python39.zip', 'D:\\software\\Coding\\Python\\DLLs', 'D:\\software\\Coding\\Python\\lib', 'D:\\software\\Coding\\Python', 'C:\\Users\\70748\\AppData\\Roaming\\Python\\Python39\\site-packages', 'D:\\software\\Coding\\Python\\lib\\site-packages', 'D:\\software\\Coding\\Python\\lib\\site-packages\\you_get-0.4.1500-py3.9.egg', 'D:\\worksapce\\python\\time_tools']
sys.argv
会返回主程序输入参数,sys.path
则是一个包含了编译器检索用的目录列表,而可以通过sys.path.append()
的方式追加目录。
第三方模块
pip的第三方模块存放在,检索相当方便,并不会如同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调用文档,不知道是为什么。
关于第三方库的更多内容可以阅读。
关于如何自己开发模块,可以阅读。
本系列文章的代码都存放在Github项目:
文章评论