导入模块
静态导入
(1)import 模块
(2)from 模块 import 函数
:可以选定要导入的函数,逗号隔开,如果全导入用*
(3)import 模块 as 命名
:这种方法最好用,此时导入的包被自己命名,例如:import random as r
,则就可以r.randint(1,10)
了
(4)from 路径 import 模块
:比如自己写的模块在别的路径,可以这样弄,比如:from Desktop.dir import demo
动态导入
__import__
通过__import__
函数可以动态导入模块,举例:
# 通过模块名的字符串来导入math模块
math = __import__("math")
print(math)
# <module 'math' (built-in)>
import_string
/import_module
两个也都是实现动态载入模块的函数,但是支持多级导入,举例:
import import_string
from importlib import import_module
import test.a as a
a1 = import_string("test.a")
a2 = import_module("test.a")
a3 = __import__("test.a")
print(a)
print(a1)
print(a2)
print(a3)
# <module 'test.a' from 'xxx\\test\\a.py'>
# <module 'test.a' from 'xxx\\test\\a.py'>
# <module 'test.a' from 'xxx\\test\\a.py'>
# <module 'test' from 'xxx\\test\\__init__.py'>
可以看出如果使用内置的__import__
函数,导入的并不是我们期望的test.a
模块,如果要导入该模块,则需要修改如下:
__import__("a", fromlist=("test",))
因此__import__
要实现import
这样的多级导入,还需要我们自己分级拆开字符串进行导入,而import_string
和import_module
则在内部帮我们实现了import
逻辑相关的检查以及解析,我们只需要传入对应的字符串即可
if __name__ == '__main__'
简单来说就是判断这个文件是直接执行还是作为模块被其他文件调用执行,如果是自己执行,比如这个文件叫a.py,直接运行a.py,那么这个main后面的内容就会执行,而如果在b.py里import a,那么a.py里的main后面内容就不会执行
因为导入的模块都是.py文件,一般里面都只写函数、类等内容,然后导入后等着被实例化和调用,但是像一般语句,比如一个不在函数或者类里的输出语句,导入时则会被自动执行,举例,一个a.py内容:
def ...
...
print('abc') #不是函数里的语句
则import a 的时候则会输出'abc',为了防止这种情况,我们一般会在被导入的文件里的一般语句前面加一句判断其是否为主程序,如果是才能运行那些句子,就拿刚才的a.py改成:
def ...
...
if __name__ == "__main__":
#只有正在运行的那个程序才是__main__,其他程序就是自己的名字,比如该文件就是a
print('abc')
那么此时,import a就不会输出'abc'了,因为a现在只是一个导入的模块,他不是__main__
程序
搜索路径
模块要在路径下才能导入,查看python能搜索到哪些路径:
>>> import sys
>>> sys.path
['', 'E:\\Python35\\Lib\\idlelib', 'E:\\Python35\\python35.zip',
'E:\\Python35\\DLLs', 'E:\\Python35\\lib', 'E:\\Python35',
... 'E:\\Python35\\lib\\site-packages\\Pythonwin']
就会显示有哪些路径了,如果要追加路径就:
sys.path.append('路径')
包
存放许多模块的一个文件夹
导入包步骤
(1)创建一个文件夹
(2)在文件夹当中创建一个__init__.py
的文件,一定要有,可以是空的文件,只是为了让python把该文件夹当成一个包(py3.3以后多了一个命名空间包的概念,所以之后的版本不用加这个文件也可以,详细参考:https://blog.csdn.net/weixin_34248705/article/details/86000989
)
(3)把模块放入该文件夹
(4)导入时按:import 包.模块
来导入
模块对象
python中一切皆对象,包括模块导入后也是以对象形式存在的,举例:
import math
print(math, type(math))
# <module 'math' (built-in)> <class 'module'>
模块对象属性
__package__
模块所在包
__name__
命名空间,如果是当前文件就是__main__
,如果是别的文件,就是其模块名,举例:
import config
print(__name__) #__main__
print(config.__name__) #config
__file__
模块所在文件,如果是当前文件就是文件名,如果是别的文件就是路径/文件名
,举例:
import config
print(__file__) #b.py,当前文件的名字
print(config.__file__) #C:\...\config.py,文件路径/文件名
常用模块
os
(1)getcwd()
:返回当前工作目录
(2)chdir()
:改变工作目录,括号里是改变到的路径
(3)listdir()
:以列表形式输出当前文件夹下所有文件名,括号里是路径
(4)mkdir()
:创建一个目录,比如:os.mkdir('E:/aaa')
,就在E盘下创建了一个叫aaa的目录,
(5)makedirs()
:递归创建子目录,比如:os.makedirs('E:/aaa/bbb/ccc')
(6)remove()
:删除文件
(7)rmdir()
:删除单层空目录
(8)removedirs()
:从子目录往父目录递归删除空目录,比如前面的'aaa/bbb/ccc'都是空的,如果从bbb删除会报错,只能从ccc删除,并且会连带aaa全部删除
(9)rename(old,new)
:将文件名从旧名字换新名字,记得加上路径和后缀名
(10)urandom()
:产生某长度的随机字节码,举例:
>>> os.urandom(10)
b'j\x06F\xba#\xffu\xf1u\x10'
如果希望将字节码转成字符串可以使用binascii
模块,举例:
>>> binascii.hexlify(os.urandom(24)).decode('utf-8')
'72efa00e79308160688597cb52cacad950604296c74748f6'
(11)curdir
:后面没有括号,代表当前目录,例如:os.curdir
结果就是'.'
,可以用在比如:os.listdir(os.curdir)
,就会把当前目录内容全部输出
(12)pardir
:指代上一级目录'..'
(13)sep
:代表路径分隔符,Windows下是\\
,Linux下是/
(14)linesep
:代表当前平台的行终止符,Windows下是'\r\n'
,Linux是'\n'
(15)name
:指代当前操作系统,'posix'为Linux,'nt'为Windows,'mac'为MAC
(16)getpid()
:获得当前进程号,要获得父进程号则getppid()
(17)system(command)
:运行系统shell命令,例如:os.system('cmd')
就会打开命令行,calc
就是打开计算器等,类似bat文件一样,与其相似的还有popen()
,使用方法类似,但popen是通过管道方式实现,运行时无输出,并且返回值是保存该命令输出结果的类似文件流(和文件一样可以用read()
读取命令输出结果),而前者会把过程的结果直接输出,然后返回值只是指令执行状态码(0为成功)
(18)popen()
:执行命令行指令,返回的是一个类似文件类型的数据,需要.read()
来读取执行命令的结果,比如:os.popen('dir').read()
,就能知道当前目录下的文件了
os.path
只要导入os模块就行,但是要用os.path.方法
(1)basename()
:去掉路径返回文件名
(2)dirname()
:去掉文件里返回路径
(3)join(path1[,path2,...])
:将几个部分组成一个文件名,例如:os.path.join('E:\\','A','B')
结果为'E:\A\B'(如果加了:
,就要自己加\\
)
(4)split(path)
:将路径和文件拆开并保存到元组,例如:os.path.split('E:\\A\\B')
结果为('E:\A', 'B')
(5)splitext(path)
:分离文件名和扩展名,例如:例如:os.path.splitext('E:\\A\\B.txt')
结果为('E:\A\B', '.txt'),如果最后一个没有后缀,第二个值就为''
(6)getsize(file)
:返回文件尺寸,单位是字节
(7)getatime(file)
:返回文件最近的访问时间,结果是浮点型秒数,可以用time模块的localtime()
、gmtime()
函数换算
(8)getctime(file)
:返回文件创建时间
(9)getmtime(file)
:返回文件最新修改时间
(10)exist(path)
:判断路径(文件或目录)是否存在
(11)isabs(path)
:判断是否为绝对路径
(12)isdir(path)
:判断是否路径存在且是一个目录
(13)isfile(path)
:判断是否路径存在且是一个文件
(14)islink(path)
:判断是否路径存在且是一个符号链接
(15)ismount(path)
:判断是否路径存在且是一个挂载点
(16)samefile(path1,path2)
:判断path1和path2路径是否指向同一个文件
sys
提供了系统相关功能方法
(1)getsizeof
:获取指定对象的字节大小
(2)argv
:获取命令行传入参数
更多参考:
https://www.cnblogs.com/cherishry/p/5725184.html
https://blog.csdn.net/qq_38526635/article/details/81739321
time
(1)localtime()
:返回本地时间,返回的是时间元组struct_time
的时间格式,
其中索引值:
0-年
1-月(1-12)
2-日(1-31)
3-时(0-23)
4-分(0-59)
5-秒(0-61,60代表闰秒)
6-周几(0-6)
7-一年的第几天(1-366)
8-是否为夏令时(正数为夏令时,0为非夏令时,负数为不确定)
(2)gmtime()
:返回格林威治时间
(3)sleep(时间)
:延迟几秒
(4)time()
:返回当前时间戳,单位是秒。一般用于测试程序运行了多少时间,可以在运行开始时将当前赋值给一个变量,然后结束的时候在将当前时间减去前面那个变量,就能获得运行秒数了
(5)clock()
:也是返回时间,但是要比上面的更为精确,所以可以更细致地计算代码运行时间
注:
计算代码执行效率可以用上面的time()
、clock()
、还有timeit
模块下的timeit()
方法,三种的使用和比较可以参考:
https://blog.csdn.net/ehcoing/article/details/52046181
pickle
可以将一个变量或对象等东西保存成一个二进制文件,并在读取后转回原来的变量或对象等,和java里的序列化、反序列化有点像
(1)dump(xxx,filename)
:把一个变量或对象等存到文件里
(2)load(filename)
:读取转存成pickle的二进制文件,结合举例:
import pickle
class A:
def __init__(self, i):
self.i = i
print(i)
def run(self, j):
print(self.i*j)
a = A(1) # 实例化一个A对象
with open("g:/test/a.pickle", "wb") as f:
pickle.dump(a, f) # 保存成二进制文件
with open("g:/test/a.pickle", "rb") as f:
b = pickle.load(f) # 重新载入文件,此时b就是前面的对象a
print(b) # <__main__.A object at 0x00000071998FDEF0>
b.run(5) # 可以发现执行A类下的run()方法成功
random
(1)randint(a,b)
:生成一个a到b之间的随机整数,例如:ran = random.randint(1,10)
(2)choice(seq)
:从列表、元组或字符串等(可索引的序列)里随机选一个内容,举例:
>>> a = [1,2,3,4,5]
>>> random.choice(a)
4
(3)choices(list, k=n)
:python 3.6以后才有的方法,随机从数据里选n个出来,但是可能会重复,举例:
>>> random.choices([1,2,3,4,5], k=3)
[5, 5, 3]
(4)sample(list, n)
:随机从数据里选n个出来,并且不会重复,举例:
>>> random.sample([1,2,3,4,5], 3)
[2, 1, 4]
(5)shuffle(list)
:打乱一个列表的顺序,把列表当参数传入即可,举例:
>>> a = [1,2,3,4,5]
>>> random.shuffle(a)
>>> a
[1, 3, 4, 5, 2]
(6)random()
:生成一个[0.0, 1.0)的随机浮点数
timeit
准确测量小段代码的执行时间
(7)timeit(stmt, number)
:第一个参数是执行的语句,第二个是执行该语句的次数,结果会返回执行的时间,举例:
>>> timeit.timeit('range(10)', number=10000)
0.0063434376421440675
内容参考:http://bbs.fishc.com/thread-55593-1-1.html](http://bbs.fishc.com/thread-55593-1-1.html)
datetime
(基本日期和时间类型,这里使用时往往导入datetime包下的datetime模块)
(1)now()
:获取当前时间,结果返回一个类似结构体的内容,查看源码可知内容有:年月日时分秒微秒,分别是year/month/day/hour/minute/second/microsecond
,所以要获取返回值的年月日等要用.
来索引,举例:
>>> from datetime import datetime
>>> a = datetime.now()
>>> a
datetime.datetime(2017, 8, 4, 17, 46, 10, 197663)
>>> a.year
2017
>>> str(a)
'2018-09-30 10:57:25.724832'
#用str会自动转成字符串格式
(2)strptime(timeresource,'%Y年%m月%d日%H:%M')
:将字符串转时间,举例:
>>> a = '2017年4月28日11:20'
>>> datetime.strptime(a, '%Y年%m月%d日%H:%M')
datetime.datetime(2017, 4, 28, 11, 20)
(3)strftime('%Y-%m-%d')
:时间转字符串,对于上面的strptime,举例:
>>> b = datetime.strptime(a, '%Y年%m月%d日%H:%M')
>>> b.strftime('%Y-%m-%d-%H-%M')
'2017-04-28-11-20'
(4)timedelta()
,时间差,这个是在datetime
下的,不是在datetime.datetime
下,可以计算两个时间相减之差,或者设置时间差,举例:
>>> from datetime import datetime as dt
>>> import datetime
>>> a = dt.now()
>>> b = dt.now()
>>> a
datetime.datetime(2018, 9, 30, 10, 36, 53, 31098)
>>> b
datetime.datetime(2018, 9, 30, 10, 37, 1, 335076)
>>> b-a
datetime.timedelta(0, 8, 303978)
#可以看出两个时间相减差8秒
>>> c = datetime.timedelta(hours=8)
>>> a+c
datetime.datetime(2018, 9, 30, 18, 36, 53, 31098)
#可以看到a加了8h
>>> c = datetime.timedelta(days=8)
>>> a+c
datetime.datetime(2018, 10, 8, 10, 36, 53, 31098)
#加了8天
>>> str(a+c)
'2018-10-08 10:36:53.031098'
更多timedelta参考:https://blog.csdn.net/lina_acm/article/details/60151712
更多参考:http://bbs.fishc.com/thread-51725-1-1.html
openpyxl
用于保存excel文件,举例:
wb = openpyxl.Workbook()
wb.guess_types = True
ws = wb.active
ws.append(["课程号","课程序号","课程名","英文名","学分","形式","分数"])
for each in targets:
ws.append(each)
wb.save("grade_2.xlsx")
hashlib
用于md5加密,举例:
>>> md5 = hashlib.md5() #实例化一个md5对象
>>> md5.update(bytes('sfasfsafa', encoding='utf-8')) #指定编码方式,并对字符串进行md5加密
>>> sign = md5.hexdigest() #此时生成的是一个32为定长加密数据
>>> sign
'9b77223f0a07cb7108d6d873a8f91961'
>>> a = hashlib.md5(bytes('sfasfsafa', encoding='utf-8')) #直接实例指定编码也行
>>> a.hexdigest()
'9b77223f0a07cb7108d6d873a8f91961'
>>> a.update(bytes('sfasfsafa', encoding='utf-8'))
>>> a.hexdigest()
'749d255fec6826bcc2fa8f4fce154dda'
#可以发现用了update以后,输入相同的加密字符串,结果却不一样
#事实上update是在原来字符串上添加内容,所以最后一个加密的应该是'sfasfsafasfasfsafa'
>>> b = hashlib.md5(bytes('sfasfsafasfasfsafa', encoding='utf-8'))
>>> b.hexdigest()
'749d255fec6826bcc2fa8f4fce154dda'
#可以看出结果是一样的,所以每次要加密一个字符串前最好先重新实例化一个对象
这个md5不但用于加密,有时也可用在一些防止内容重复的情况,比如爬虫下载图片时,可以取名为str(md5(content).hexdigest()) + '.jpg'
,这样就根据其内容来生成一个文件名,内容一样的生成的文件名也就一样,举例:
import requests
from hashlib import md5
res = requests.get('https://www.baidu.com/img/bd_logo1.png')
with open(str(md5(res.content).hexdigest()) + '.jpg', 'wb' ) as f:
#一般内容需要先用update编码后才能生成加密数据
#这里爬的图片的content本身就是字节流,所以才可以不用再重复编码
f.write(res.content)
uuid
根据你的主机ID, 序列号, 和当前时间来生成UUID, 可保证全球范围的唯一性,里面有:uuid1()
(基于时间戳+随机数+MAC地址,因此必然不重复,但加上MAC地址导致安全性有失), uuid3()
(基于名字的MD5散列值),uuid4()
(基于随机数,有很小的概率重复), uuid5()
(基于名字的SHA-1散列值)来生成1, 3, 4, 5各个版本的UUID(python里没有2),举例:
>>> uuid.uuid1()
UUID('23c29dc2-97cc-11e8-bb6f-5ce0c577c64f')
>>> uuid.uuid1()
UUID('2e8feb7e-97cc-11e8-8ea5-5ce0c577c64f') #再运行又是不同的结果
>>> uuid.uuid3(uuid.NAMESPACE_DNS, "test")
UUID('45a113ac-c7f2-30b0-90a5-a399ab912716')
>>> uuid.uuid3(uuid.NAMESPACE_DNS, "test")
UUID('45a113ac-c7f2-30b0-90a5-a399ab912716') # 相同命名空间下且名字相同的话生成的是相同的
>>> uuid.uuid5(uuid.NAMESPACE_DNS, "test")
UUID('4be0643f-1d98-573b-97cd-ca98a65347dd')
>>> uuid.uuid4()
UUID('2579afc8-dfdd-4378-8dda-17b87dc7625a')
>>> uuid.uuid4()
UUID('154a1c49-7350-4189-860d-e00c11cfcde1')
常用的有1和4,但是1有安全性的问题,而4有一定概率重复的问题
使用参考:https://www.cnblogs.com/haiyan123/p/9752813.html
区别参考:https://segmentfault.com/q/1010000010862121
math
一些数学相关方法
(1)pi
:就是π,你输入math.pi
就会看到:3.1415936...
(2)sin()
:举例:math.sin(math.pi/2)
,结果为:1.0,cos()
也一样,里面是弧度制,所以要把角度换成弧度可以像上面那样通过pi(180度的弧度)乘除得到想要角度的弧度,也可以通过:角度/360*2*math.pi
((2πa)/360),来得到对应的弧度
(3)tan()
:参照sin()
和cos()
(4)log()
:包括log(x[,base])
(默认base
为e
,即以e为底)、log10()
、log2()
,比如:
>>> math.log10(100)
2.0
>>> math.log(9,3)
2.0
(5)sqrt()
:求根号,比如sqrt(4)
,结果为2
(6)math.e
:就是e,即2.718281828459045
(7)math.exp()
:e的几次方,比如math.exp(2)
,结果为:7.38905609893065
(8)ceil()
:向上取整,比如math.ceil(3.2)
,结果为4,如果要向下取整直接用内置的int()
就行,四舍五入的话用内置的round()
(9)modf()
:分别获取一个数的小数和整数部分,结果会返回一个元组,比如
>>> math.modf(3.35)
(0.3500000000000001, 3.0) #结果有精确问题
shutil
(1)move(file, path)
:移动文件到某一路径下
(1)copy(file, object)
:复制文件到某一指定位置
copy
(1)copy()
:浅拷贝,重新开辟个地址存放,但只拷贝父对象,子对象还是指向原对象,所以规律就是子变父不变
(2)deepcopy()
:深拷贝,重新开辟个地址存放,完全独立于原对象,所以不论原对象怎么变都不会跟着变
注:
赋值在python里就相当于指向,所以原来指向的对象如果产生变化,其也会产生变化,下面是三种复制对比:
>>> a = [1,2,3,[4,5]]
>>> import copy
>>> b = a #指向
>>> c = copy.copy(a) #浅拷贝
>>> d = copy.deepcopy(a) #深拷贝
>>> a.append(6) #更改父对象
>>> b
[1, 2, 3, [4, 5], 6]
>>> c #浅拷贝父对象不变
[1, 2, 3, [4, 5]]
>>> d
[1, 2, 3, [4, 5]]
>>> a[3].append(6) #更改子对象
>>> b #指向的都跟着变
[1, 2, 3, [4, 5, 6], 6]
>>> c #浅拷贝子对象变
[1, 2, 3, [4, 5, 6]]
>>> d #深拷贝都不跟着变
[1, 2, 3, [4, 5]]
>>> id(a)
34160688
>>> id(b) #指向的地址一样
34160688
>>> id(c) #深浅拷贝地址都不一样
69945144
>>> id(d)
67159864
pprint
可以对数据进行格式化处理,变得更加美观
(1)pprint()
:将内容格式化输出,举例:
>>> import pprint as p
>>> a = [['aaa','bbb'],'ccc','ddd','eee',['fff','ggg','hhh','iii','jjj'], 'kkk', 'lll', 'mmm']
>>> a
[['aaa', 'bbb'], 'ccc', 'ddd', 'eee', ['fff', 'ggg', 'hhh', 'iii', 'jjj'], 'kkk', 'lll', 'mmm']
>>> p.pprint(a)
[['aaa', 'bbb'],
'ccc',
'ddd',
'eee',
['fff', 'ggg', 'hhh', 'iii', 'jjj'],
'kkk',
'lll',
'mmm']
(2)pformat()
:将内容格式化后返回,举例:
>>> a
[['aaa', 'bbb'], 'ccc', 'ddd', 'eee', ['fff', 'ggg', 'hhh', 'iii', 'jjj'], 'kkk', 'lll', 'mmm']
>>> b = p.pformat(a)
>>> b
"[['aaa', 'bbb'],\n 'ccc',\n 'ddd',\n 'eee',\n ['fff', 'ggg', 'hhh', 'iii', 'jjj'],\n 'kkk',\n 'lll',\n 'mmm']"
#可以看到a已经被格式化
>>> print(b)
[['aaa', 'bbb'],
'ccc',
'ddd',
'eee',
['fff', 'ggg', 'hhh', 'iii', 'jjj'],
'kkk',
'lll',
'mmm']
dis
对python代码进行反汇编操作(注意这里的汇编是指专门在python中的汇编指令,也称为opcode),在性能优化之类的时候可以用上(一般来说每条汇编指令执行的时间都差不多,因此可以通过让反汇编的指令减少来达到优化的目的)
(1)dis
:查看某个语句/函数的对应汇编指令(该方法对于类/模块等对象的不同,返回的结果也不同,如输入模块则打印每个函数的汇编指令,输入类则打印每个方法的汇编指令),举例:
>>> dis.dis("x=1")
# 查看语句x=1的汇编指令
1 0 LOAD_CONST 0 (1)
2 STORE_NAME 0 (x)
4 LOAD_CONST 1 (None)
6 RETURN_VALUE
>>> def xxx(i):
print(i)
return i
>>> dis.dis(xxx)
# 查看函数xxx的汇编指令
# 可以看到有5列,分别对应:
# 对应源代码行数(因为是函数,所以内容从第二行开始)、对应内存字节码索引、操作指令、指令参数、对应代码参数
# 比如前四行就代表第二行print(i)这个代码执行的内容
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_FAST 0 (i)
4 CALL_FUNCTION 1
6 POP_TOP
3 8 LOAD_FAST 0 (i)
10 RETURN_VALUE
>>> def xxx(i):
print(i)
# 将xxx函数去掉返回值语句查看会有什么变化
>>> dis.dis(xxx)
# 可以发现倒数第二行发生了改变:应该不难理解去掉return后指令从上面的压入变量变成了压入一个None的常量
# 所以可以得知在python中函数如果不设置return返回值则会默认返回一个None
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_FAST 0 (i)
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
更多dis使用和python反汇编参考:
深入理解python之Opcode备忘录
python编程(反汇编)
其他相关性能优化模块,如时间分析(timeit
/profile
/cProfile
/line_profiler
)、内存分析(memory_profiler
)、内存泄漏排查(objgraph
)等参考:
Python的7种性能测试工具
python优化分析工具
Python性能分析优化及测试
参考:https://blog.csdn.net/qs9816/article/details/51661659
json
(1)dumps()
:将字典转json格式,举例:
>>> a = {'a':'1',"b":'2'}
>>> b = json.dumps(a)
>>> b
'{"a": "1", "b": "2"}'
>>> c = json.dumps(a, sort_keys=True, indent=4, separators=(',', ':'))
# 自定义dump后字符串格式
>>> c
'{\n "a":"1",\n "b":"2"\n}'
>>> print(c)
{
"a":"1",
"b":"2"
}
默认中文会被转成ascii字符,如果希望保持中文原样的话,可以配置参数ensure_ascii
为False
:,举例:
>>> test = {"test": "测试"}
>>> json.dumps(test)
# 默认转成ascii字符
'{"test": "\\u6d4b\\u8bd5"}'
>>> json.dumps(test, ensure_ascii=False)
'{"test": "测试"}'
(2)loads()
:将json转字典,举例:
>>> c = json.loads(b)
>>> c
{'a': '1', 'b': '2'}
(3)dump()
:将字典以json形式存到文件当中
(4)load()
:从文件当中读取json数据并转成字典
dump()
/load()
使用及和dumps()
/loads()
区别参考:
https://www.cnblogs.com/everfight/p/json_file.html
更多参考:https://www.cnblogs.com/tjuyuan/p/6795860.html
psutil
查看系统一些信息,如内存使用情况,CPU等,需要pip install psutil
(1)virtual_memory()
:查看系统内存情况,举例:
>>> psutil.virtual_memory()
svmem(total=8487653376, available=3343429632, percent=60.6, used=5144223744, free=3343429632)
>>> psutil.virtual_memory().percent
60.1
(2)Process
:获取当前进程的信息,举例:
>>> psutil.Process(os.getpid()) #当前进程信息
psutil.Process(pid=83496, name='pythonw.exe', started='17:35:15')
>>> psutil.Process(os.getpid()).memory_info() #内存信息
pmem(rss=20221952, vms=13062144, num_page_faults=5481, peak_wset=20283392, wset=20221952, peak_paged_pool=175680, paged_pool=175680, peak_nonpaged_pool=19664, nonpaged_pool=18480, pagefile=13062144, peak_pagefile=13287424, private=13062144)
>>> psutil.Process(os.getpid()).memory_info().rss #当前进程内存使用
20221952
(3)cpu_count
:CPU个数,举例:
>>> psutil.cpu_count()
4
更多参考:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001511052957192bb91a56a2339485c8a8c79812b400d49000
https://www.cnblogs.com/liu-yao/p/5678157.html
configparser
from configparser import ConfigParser
,配置文件使用,后缀名为cfg
参考:
http://www.cnblogs.com/cnhkzyy/p/9294829.html
http://www.cnblogs.com/camilla/p/7234657.html
logging
打印日志文件使用
参考:https://www.jianshu.com/p/b84b0eb86577
https://www.cnblogs.com/CJOKER/p/8295272.html
注:
pysnooper
也是一个日志跟踪很好的模块
fire
pip install fire
,生成命令行接口,为函数提供命令行接口的模块,举例:
import fire
def output(x):
print(x)
def add(x, y):
return x + y
if __name__ == '__main__':
fire.Fire()
在命令行输入命令如下:
python .\test.py output --x=1
# 1
python .\test.py add --x=1 --y=2
# 3
参考:https://www.jianshu.com/p/2f83b3fc1e79
apscheduler
pip install apscheduler
,定时任务使用,参考:https://www.cnblogs.com/luxiaojun/p/6567132.html
注:
上面的链接里提供的实例定时任务器是BlockingScheduler
,其在主线程,因此会阻塞其他的工作,如果希望是后台运行,那么则使用``,可以参考下面的示例:
from apscheduler.schedulers.background import BackgroundScheduler
# 后台运行任务
def job():
print(1)
scheduler = BackgroundScheduler()
scheduler.add_job(job, 'interval', seconds=1)
scheduler.start()
注2:
定时任务的框架不只这一个,但是个人认为这个最方便使用,需要了解其他几个定时任务框架可以参考:
https://www.jianshu.com/p/94b273f6ed77
faker
生成各种假数据,包括什么姓名、城市、IP地址等等特别多,参考:
https://www.jianshu.com/p/402e37956183
官方文档:https://faker.readthedocs.io/en/master/providers.html
PySnooper/Behold
用于debug数据,参考:https://www.jianshu.com/p/a33e4b038b6c
numba
能够大大提高python代码的计算性能(主要是提高了计算性能,因此使用不当可能反而会导致运行效率降低),尤其是在for循环这样的语句中,可以使其加速得和C那样快,使用方法也特别简单,只需要添加一个注解就可以了,举例:
import numba
import time
@numba.jit
def aaa():
num = 0
start = time.time()
for i in range(100000000):
num += i
end = time.time() - start
print('a:{}-->{}'.format(num, end))
def bbb():
num = 0
start = time.time()
for i in range(100000000):
num += i
end = time.time()-start
print('b:{}-->{}'.format(num, end))
aaa() # numba加速
bbb() # 无加速
# 结果:
# a:4999999950000000-->0.030257701873779297
# b:4999999950000000-->5.524895191192627
注:
numba适合数据量大的适合使用,如果将上面的循环去掉几个0,结果可能不用numba更快
注2:
numba只对当前装饰的函数起效果,而对当前函数调用的函数不起效果,例如下面的情况,是不会被加速的:
import numba
import time
def aaa():
num = 0
start = time.time()
for i in range(100000000):
num += i
end = time.time() - start
print('a:{}-->{}'.format(num, end))
@numba.jit
def bbb():
aaa()
bbb() # 只对bbb()加速,而对调用的aaa()函数无加速
# 结果:
# a:4999999950000000-->6.7566587924957275
更多参考:https://www.jianshu.com/p/69d9d7e37bc5
GeoPy
获取地理位置等,并且能计算两个地方间的距离
参考:
https://www.osgeo.cn/geopy/
https://www.jianshu.com/p/593c5c64ef91
natsort
自然排序,而不是像内置的sort
排序一样基于字符串排序,参考:
https://www.wandouip.com/t5i127468/
glob
可以使用通配符*
/[]
/?
等查找文件路径,举例:
>>> glob.glob('G:/a/*/*.jpg')
['G:\\a\\b\\1.jpg', 'G:\\a\\c\\2.jpg']
# 获取g盘文件夹a的所有子目录的所有jpg文件
更多参考:
https://www.cnblogs.com/luminousjj/p/9359543.html
functools
里面内置了一些常用方法
1.reduce(func, iter)
:对迭代器内容循环操作,举例:
>>> functools.reduce(lambda x, y: x*y, [1,2,3])
# 将每个数依次相乘,x接收上一次计算返回的结果,y接收当前循环的数
6
2.partial(fun, arg1, ...)
:用于包装函数,并固定函数中的某些参数,举例:
>>> def test(x, y):
print(x, "->", y)
>>> test1 = partial(test, y = "xxx")
# 重新包装了test函数为test1,并固定了参数y的值为"xxx"
# 此时test1等价于函数:
# def test1(x, y = "xxx"):
# print(x, "->", y)
>>> test1("aaa")
aaa -> xxx
实际上就是通过装饰器将函数的参数进行修改包装,我们可以自己模拟实现一下,举例:
def test(x, y, z=0):
print(x + y + z)
def partial(func, *args, **kwargs):
def wrapper(*a, **k):
kwargs.update(k)
return func(*(args + a), **kwargs)
return wrapper
test2 = partial(test, 1, z=100)
test2(10)
# 111
collections
里面内置了很多工具
(1)Counter
:计数器,举例:
>>> counter = collections.Counter([1,2,3,2,3])
>>> counter
# 2和3都2个,1有1个
Counter({2: 2, 3: 2, 1: 1})
>>> counter[2]
2
(2)OrderDict
:有序字典(在python3.6及以后的版本当中字典都是有序的,但是有时候为了兼容低版本还是需要用到),举例:
>>> od = collections.OrderedDict([(1,2), (3,4)])
>>> od
OrderedDict([(1, 2), (3, 4)])
>>> od[1]
2
warnings
可以设置和忽略一些警告提示,举例:
>>> import warnings
>>> def warn_fun():
warnings.warn("this is a warning")
print("result")
# 定义一个有警告的函数
>>> warn_fun()
# 调用后可以看到会显示警告
Warning (from warnings module):
File "__main__", line 2
UserWarning: this is a warning
result
>>> warnings.filterwarnings('ignore')
# 设置忽略警告
>>> warn_fun()
# 可以看到警告没有了
result
itertools
一个好用的迭代器工具库
(1)cycle
:将一份数据不断循环迭代,举例:
>>> x = itertools.cycle([1,2,3])
>>> x
# 可以看出x是个数据的迭代循环生成器
<itertools.cycle object at 0x0000025C2B049798>
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
# 可以看到数据又自动从第一个开始了
1
>>> next(x)
2
(2)chain
:将多个可迭代内容拼接在一起迭代,举例:
>>> x = itertools.chain([1,2,3], [7,8,9], [4,5,6])
>>> for i in x:
print(i, end='\t')
# 可以看到3份数据被拼在一起迭代了
1 2 3 7 8 9 4 5 6
(3)chain.from_iterable
:将列表内容展开(即列表里如果还有列表则都展开在一起,但只限第一级的列表),举例:
>>> list(itertools.chain.from_iterable([[1,2], [3,4]]))
[1, 2, 3, 4]
>>> list(itertools.chain.from_iterable([[1,2], [3,4, [5,6]]]))
# 可以看到第二级里的列表没有展开
[1, 2, 3, 4, [5, 6]]
更多参考:https://blog.csdn.net/u013300049/article/details/79313979
array
数组对象,要求内部所有类型统一,并且一般空间消耗小于列表
数组/列表区别:
- 列表:由于列表内部可以存放任意类型的数据,初始化时无法确定每个数据的类型,因此也就无法确定每个存储位置的空间大小,所以列表里面存储的是地址的指向,每个位置的内存空间是不连续的,因此效率较低;
- 数组:由于存放的数据必须为同一类型,且在一开始实例化时就需要指定好类型,因此可以确定每个位置所需的空间大小,所以每个位置存储的是实际数据值,且内存空间是连续的,效率很高
参考:
https://www.cnblogs.com/jlf0103/p/9168093.html
https://blog.csdn.net/xc_zhou/article/details/88538793
bitarray
字节流数组,所有的数据由0和1构成,在实现如布隆过滤器这样只需要0和1的数组时,能够很大程度地节省空间,举例:
>>> import sys
>>> from bitarray import bitarray
>>> a = [0 for _ in range(1000)]
>>> b = bitarray(1000)
>>> sys.getsizeof(a)
9024
>>> sys.getsizeof(b)
96
# 可以看出字节流数组大小远小于列表
(1)初始化:根据传入的数据类型初始化,如果是数字,则生成指定长度字节流,值随机(根据内存分配),如果是迭代器,则返回迭代器对应值转成0/1后的结果,...,举例:
>>> from bitarray import bitarray
>>> bitarray(10)
bitarray('1100100000')
(2)setall(v)
:统一设置值,举例:
>>> a = bitarray(10)
>>> a
bitarray('1100100000')
>>> a.setall(1)
>>> a
bitarray('1111111111')
还有提供很多方法如:all
/any
/append
/pop
/count
/...等方法,基本使用都跟列表差不多
heapq
提供了小顶堆相关的操作,有利于我们更好的维护一个堆结构
- heapify:批量建堆,举例:
>>> from heapq import *
>>> heap = [5,7,4,1,2,9]
>>> heapify(heap)
>>> heap
[1, 2, 4, 7, 5, 9]
- heappush:往堆里添加元素,举例:
>>> heap = []
>>> heappush(heap, 10)
>>> heappush(heap, 3)
>>> heappush(heap, 5)
>>> heap
[3, 10, 5]
- nlargest/nsmallest:返回前N个最大/最小值,举例:
>>> heap = [5,7,4,1,2,9]
>>> heapify(heap)
>>> heap
[1, 2, 4, 7, 5, 9]
>>> nlargest(3, heap)
[9, 7, 5]
>>> nsmallest(3, heap)
[1, 2, 4]
bisect
处理已排序的序列,提供了几个插入和查找的方法
-
insort(insort_right的别名)
:往排好序的位置插入数据 -
bisect(bisect_right的别名)
:查询要插入的索引 -
insort_left
:和insort一样,只不过从左边开始 -
bisect_left
:和bisect一样,只不过从右边开始
这些方法都是基于二分查找进行处理,因此十分高效,而且可以保证在该序列当中的数始终都是有序的
传入参数:排好序的可修改序列类和要插入的数据
随便放一个源码看看:
def insort_right(a, x, lo=0, hi=None):
"""Insert item x in list a, and keep it sorted assuming a is sorted.
If x is already in a, insert it to the right of the rightmost x.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if x < a[mid]: hi = mid
else: lo = mid+1
a.insert(lo, x)
inspect
提供了一些较为底层的操作,如:获取函数堆栈信息、一些基本类型的判断、获取函数方法的参数信息、获取源码等
-
signature
:获取函数、方法的签名,举例:
import inspect
def add(x:int, y:int):
return x + y
sig = inspect.signature(add)
params = sig.parameters
print(params)
# OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">)])
-
isclass
:判断是否为一个类,举例:
import inspect
class A: pass
def b(): pass
print(inspect.isclass(A))
print(inspect.isclass(b))
# True
# False
-
currentframe
:获取当前函数栈帧,举例:
import inspect
def func1():
print(inspect.currentframe().f_code.co_name)
func1()
# func1
-
stack
:获取堆栈信息,举例:
import inspect
def func1():
def func2():
for each in inspect.stack():
print(each.frame.f_code.co_name)
func2()
func1()
# func2
# func1
# <module>
-
getsource
:获取源码,举例:
import inspect
class A:
def __init__(self):
self.x = 1
print(inspect.getsource(A))
# class A:
# def __init__(self):
# self.x = 1
-
getfile
:获取文件名,源码如下:
def getfile(object):
"""Work out which source or compiled file an object was defined in."""
if ismodule(object):
if hasattr(object, '__file__'):
return object.__file__
raise TypeError('{!r} is a built-in module'.format(object))
if isclass(object):
if hasattr(object, '__module__'):
object = sys.modules.get(object.__module__)
if hasattr(object, '__file__'):
return object.__file__
raise TypeError('{!r} is a built-in class'.format(object))
if ismethod(object):
object = object.__func__
if isfunction(object):
object = object.__code__
if istraceback(object):
object = object.tb_frame
if isframe(object):
object = object.f_code
if iscode(object):
return object.co_filename
raise TypeError('{!r} is not a module, class, method, '
'function, traceback, frame, or code object'.format(object))
可以看出python中只会将文件名存入到module
对象当中或者code
对象当中
py_compile
内置模块,用于将.py
文件编译成.pyc
文件
.pyc
文件很容易反编译,如果希望自己的代码加密后无法被反编译,可以转.pyd
文件,参考:
-
.pyd
编译环境配置:
安装VS2015 C++环境:https://download.microsoft.com/download/5/f/7/5f7acaeb-8363-451f-9425-68a90f98b238/visualcppbuildtools_full.exe
rc.exe不存在配置:https://blog.csdn.net/yapingxin/article/details/80541537
-
.py
转.pyd
文件步骤:
uncompyle
需要安装:pip install uncompyle
,用于将.pyc
文件反编译成.py
文件
更多模块参考
https://www.jianshu.com/p/d25a9169fe86
其他
同样的模块只会导入一次
在python中,导入模块就会将这个模块加载入编译器当中,当下次再载入同样的模块时,如果已经存在,那么就不会再重复加载。例如有三个文件a、b、c,其中a和b都导入c,a还导入了b,代码如下:
- a.py
import b
import c
- b.py
import c
print("b被调用")
- c.py
print("c被调用")
运行a.py
文件的结果如下:
c被调用
b被调用
由此可以看到c模块并没有被重复导入,而是只有第一次的时候会进行导入
模块只导入一次原理
python会将所有载入的模块记录到sys.modules
的字典当中,每当导入模块时,会判断该字典中是否存在要导入的模块,如果存在则直接取出,无需再次导入,从而加快程序运行的速度,举例:
import sys
print("math" in sys.modules)
import math
print("math" in sys.modules)
# False
# True
可以看到模块导入后被记录进sys.modules
当中,可以查看下该字典中初始化都有哪些模块:
import sys
for module in sys.modules:
print(module)
# builtins
# sys
# _frozen_importlib
# _imp
# _warnings
# _thread
# _weakref
# _io
# io
# abc
# _weakrefset
# site
# os
# errno
# stat
# _stat
# ntpath
# genericpath
# os.path
# _collections_abc
# _sitebuiltins
# sysconfig
# types
# functools
# _functools
# collections
# operator
# _operator
# keyword
# heapq
# _heapq
# itertools
# _collections
# weakref
# collections.abc
# importlib
# ...
# 部分展示
可以看到很多内置模块即使我们没有主动去导入,初始化时也会自动帮我们导入
模块导入路径
当导入模块时,会从sys.path
路径下寻找模块文件,如果希望能够导入我们自定义路径下的模块,则可以往sys.path
中通过append
方法添加我们自己的路径,举例:
import sys
try:
import m
except ImportError:
print("模块不存在")
# 动态添加模块搜索路径test目录
sys.path.append("./test/")
import m
print(m)
# 模块不存在
# <module 'm' from './test\\m.py'>
可以看出模块m
一开始不在搜索路径中,因此导入失败,之后添加到m
所在目录到搜索路径中后,即可成功导入
工程模块导入方案
当要将一个工程的代码加入到另一个工程当中时,可能会出现模块路径导入失败的问题,这里提出几个实用的解决方案
工程里使用相对路径
优点:导包方便
缺点:只能作为包调用,不能在包内直接运行
示例
- 目录结构如下:
-pardir
-__init__.py
-test.py
-x1.py
-y1.py
-z1.py
-subdir
-__init__.py
-x2.py
-y2.py
-test.py
在使用相对路径的情况下,__init__.py
文件可有可无,其他文件内容如下:
- test.py:
import pardir.x1
- pardir/test.py:
import x1
- pardir/x1.py:
from .y1 import *
- pardir/y1.py:
from .subdir.x2 import *
- pardir/subdir/x2.py:
from .y2 import *
- pardir/subdir/y2.py:
print("import success")
结果可以发现执行test.py
是可以成功导入的,但是执行pardir/test.py
则会导入失败
将包路径添加到sys.path中
优点:不论是以包的形式调用还是在当前模块调用都兼容
缺点:需要将对应的路径都加入到sys.path中
示例
参考和相对导入方式同样的结构,各文件内容如下:
-
pardir/__init__.py
和pardir/subdir/__init__.py
:
import os
import sys
path = os.path.dirname(__file__)
sys.path.append(path)
其他文件将相对路径的.
去掉即可,例如pardir/x1.py
文件从:
from .y1 import *
改成
from y1 import *
结果可以发现两个test.py
文件都能成功导入