二十一、常见模块

上一章介绍了Python模块的相关知识,在实际开发中,Python的很多功能都已经有了成熟的第三方实现,一般不需要开发者”重复造轮子“,当开发者需要完成某种功能时,通过搜索引擎进行搜索,通常可以找到第三方在Python中为该功能所提供的扩展模块。实际上,Python语言本身也内置了大量模块,对于常规的日期、时间、正则表达式、JSON支持、容器类等,Python内置的模块已经非常完备。
Python内置模块总是在不断更新中,本文只是起到抛砖引玉的作用

21.1 常用模块

21.1.1 sys模块

sys模块代表了Python解释器,主要用于获取和Python解释器相关的信息。
如下示例,查看sys模块包含的程序单元(包括变量、函数等)。

>>> import sys
>>> [e for e in dir(sys) if not e.startswith('_')]
['addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 
'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'copyright', 
'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 
'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 
'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'getallocatedblocks', 
'getdefaultencoding', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 
'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 
'getwindowsversion', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern',
 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode',
  'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 
  'platlibdir', 'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 
  'set_coroutine_origin_tracking_depth', 'setprofile', 'setrecursionlimit', 
  'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 
  'unraisablehook', 'version', 'version_info', 'warnoptions', 'winver']
>>>

不必记住所有的单元,通常时用到哪些模块就去查阅其对应的说明文档和参考手册。sys模块的参考页面.
大部分时候用不到sys模块里很冷僻的功能,本节只介绍sys模块中常用属性和函数。

  • sys.argv : 获取运行Python程序的命令行参数。其中sys.argv[0]通常指该Python程序,sys.argv[1]代表Python程序提供的第一个参数,sys.argv[2]代表第二个参数…以此类推。
  • sys.byteorder : 显示本地字节序的指示符。如果本地字节序时大端模式。则该属性返回big;否则返回little。
  • sys.copyright : 该属性返回与Python解释器相关的版权信息。
  • sys.executable : 该属性返回Python解释器在磁盘上的存储路径。
  • sys.flags : 该只读属性返回运行Python命令时指定的旗标。
  • sys.getfilesystemencoding() : 返回在当前系统中保存文件所用的字符集。
  • sys.getrefcount(object) : 返回指定对象的引用计算。前面介绍过,当object对象的引用计算为0时,系统会回收该对象。
  • sys.getrecursionlimit() : 返回Python解释器当前支持的递归深度,该属性可通过setrecursionlimit()方法重置。
  • sys.getswitchinterval() : 返回在当前Python解释器中线程切换的时间间隔,该属性可以通过setswitchinterval()函数改变。
  • sys.implementation:返回当前Python解释器的实现。
  • sys.maxsize: 返回Python整数支持的最大值。在32位平台上,该属性值位231-1;在64位平台上,该属性值为263-1.
  • sys.modules : 返回模块名和载入模块对应关系的字典。
  • sys.path : 该属性指定Python产找模块的路径列表。程序可通过修改该属性来动态增加Python加载模块的路径。
  • sys.platform : 返回Python解释器所在平台的标识符。
  • sys.stdin : 返回系统的标准输入流(类文件对象)
  • sys.stdout : 返回系统的标准输出流(类文件对象)
  • sys.stderr : 返回系统的错误输出流(类文件对象)
  • sys.version : 返回当前Python解释器的版本信息。
  • sys.winver : 返回当前Python解释器的主版本号。
    示例:
>>> import sys
>>> print(sys.argv)
['']
>>> print(sys.copyright)
Copyright (c) 2001-2021 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved.
>>> print(sys.executable)
D:\Administrator\05-Personal\Software\python-3.9.9-embed-amd64\python.exe
>>> print(sys.getfilesystemencoding())
utf-8
>>>

21.1.2 os模块

os模块代表了程序所在的操作系统,主要用于获取程序运行所在操作系统的相关信息。
在Python交互解释器中先导入os模块,使用all命令查看该模块对外开放的全部属性和函数。示例如下:

>>> import os
>>> os.__all__
['altsep', 'curdir', 'pardir', 'sep', 'pathsep', 'linesep', 'defpath', 'name', 'path', 
'devnull', 'SEEK_SET', 'SEEK_CUR', 'SEEK_END', 'fsencode', 'fsdecode', 'get_exec_path', 
'fdopen', 'popen', 'extsep', '_exit', 'DirEntry', 'F_OK', 'O_APPEND', 'O_BINARY', 
'O_CREAT', 'O_EXCL', 'O_NOINHERIT', 'O_RANDOM', 'O_RDONLY', 'O_RDWR', 'O_SEQUENTIAL', 
'O_SHORT_LIVED', 'O_TEMPORARY', 'O_TEXT', 'O_TRUNC', 'O_WRONLY', 'P_DETACH', 'P_NOWAIT', 
'P_NOWAITO', 'P_OVERLAY', 'P_WAIT', 'R_OK', 'TMP_MAX', 'W_OK', 'X_OK', 'abort', 'access', 
'chdir', 'chmod', 'close', 'closerange', 'cpu_count', 'device_encoding', 'dup', 'dup2', 
'environ', 'error', 'execv', 'execve', 'fspath', 'fstat', 'fsync', 'ftruncate', 
'get_handle_inheritable', 'get_inheritable', 'get_terminal_size', 'getcwd', 'getcwdb', 
'getlogin', 'getpid', 'getppid', 'isatty', 'kill', 'link', 'listdir', 'lseek', 'lstat', 
'mkdir', 'open', 'pipe', 'putenv', 'read', 'readlink', 'remove', 'rename', 'replace', 
'rmdir', 'scandir', 'set_handle_inheritable', 'set_inheritable', 'spawnv', 'spawnve', 
'startfile', 'stat', 'stat_result', 'statvfs_result', 'strerror', 'symlink', 'system', 
'terminal_size', 'times', 'times_result', 'truncate', 'umask', 'uname_result', 'unlink', 
'unsetenv', 'urandom', 'utime', 'waitpid', 'waitstatus_to_exitcode', 'write', 'makedirs',
 'removedirs', 'renames', 'walk', 'execl', 'execle', 'execlp', 'execlpe', 'execvp', 
 'execvpe', 'getenv', 'supports_bytes_environ', 'spawnl', 'spawnle']
 
>>>

同样不需要记住所有的函数,os模块主要包含3方面的内容:

  1. 系统相关的函数

方法

说明

os.name

返回导入依赖模块的操作系统名称,通常可返回’posix’,‘nt’,'java’等值其中之一

os.environ

返回在当前系统上所有环境变量组成的字典

os.fsencode(filename)

该函数对类路径(path-like)的文件名进行编码

os.fsdecode(filename)

该函数对类路径(path-like)的文件名进行解码

os.PathLike

这是一个类,代表一个类路径(path-like)对象

os.getenv(Key,default=None)

获取指定环境变量的值

os.getlosin()

返回当前系统的登录用户名。与该函数对应的还有os.getuid(),os.getgroups(),os.getgid()等函数,用于获取用户ID,用户组,组ID等,这些函数通常只在UNIX系统上有效

os.getpid

获取当前进程ID

os.getppid

获取当前进程的父进程ID

os.putenv(key,value)

该函数用于设置环境变量

os.cpu_count()

返回当前系统的cpu数量

os.sep

返回路径分隔符

os,pathsep

返回当前系统上多条路径之间的分隔符,一般在Windows系统上多条路径之间的分隔符是英文分号(;);在UNIX及类UNIX系统上多条路径分隔符是英文冒号(:)

os.linesep

返回当前系统的换行符。WIndows为’\r\n’;UNIX’为’\n’;Mac OS X为’\r’

os.urandom(size)

返回时和作为加密使用的、最多由N个字节组成的bytes对象。该函数通过操作系统特定的随机性来源返回随机字节,该随机字节通常是不可预测的,因此适用于绝大部分加密场景

下面程序演示了表 1 中部分函数的功能和用法:

>>> import os
>>> os.name
'nt'
>>> os.getenv('PYTHONPATH')
>>> os.getlogin()
'F21633C'
>>> os.getpid()
13676

其他的示例就不一一试验了,感兴趣的读者可以自己尝试。

  1. 文件相关的函数
    具体的用法我们还会在后续文件操作中进行介绍。

方法

说明

os.path.abspath(path)

返回 path 的绝对路径。

os.path.basename(path)

获取 path 路径的基本名称,即 path 末尾到最后一个斜杠的位置之间的字符串。

os.path.commonprefix(list)

返回 list(多个路径)中,所有 path 共有的最长的路径。

os.path.dirname(path)

返回 path 路径中的目录部分。

os.path.exists(path)

判断 path 对应的文件是否存在,如果存在,返回 True;反之,返回 False。和 lexists() 的区别在于,exists()会自动判断失效的文件链接(类似 Windows 系统中文件的快捷方式),而 lexists() 却不会。

os.path.lexists(path)

判断路径是否存在,如果存在,则返回 True;反之,返回 False。

os.path.expanduser(path)

把 path 中包含的 “~” 和 “~user” 转换成用户目录。

os.path.expandvars(path)

根据环境变量的值替换 path 中包含的 “{name}”。

os.path.getatime(path)

返回 path 所指文件的最近访问时间(浮点型秒数)。

os.path.getmtime(path)

返回文件的最近修改时间(单位为秒)。

os.path.getctime(path)

返回文件的创建时间(单位为秒,自 1970 年 1 月 1 日起(又称 Unix 时间))。

os.path.getsize(path)

返回文件大小,如果文件不存在就返回错误。

os.path.isabs(path)

判断是否为绝对路径。

os.path.isfile(path)

判断路径是否为文件。

os.path.isdir(path)

判断路径是否为目录。

os.path.islink(path)

判断路径是否为链接文件(类似 Windows 系统中的快捷方式)。

os.path.ismount(path)

判断路径是否为挂载点。

os.path.join(path1[, path2[, …]])

把目录和文件名合成一个路径。

os.path.normcase(path)

转换 path 的大小写和斜杠。

os.path.normpath(path)

规范 path 字符串形式。

os.path.realpath(path)

返回 path 的真实路径。

os.path.relpath(path[, start])

从 start 开始计算相对路径。

os.path.samefile(path1, path2)

判断目录或文件是否相同。

os.path.sameopenfile(fp1, fp2)

判断 fp1 和 fp2 是否指向同一文件。

os.path.samestat(stat1, stat2)

判断 stat1 和 stat2 是否指向同一个文件。

os.path.split(path)

把路径分割成 dirname 和 basename,返回一个元组。

os.path.splitdrive(path)

一般用在 windows 下,返回驱动器名和路径组成的元组。

os.path.splitext(path)

分割路径,返回路径名和文件扩展名的元组。

os.path.splitunc(path)

把路径分割为加载点与文件。

os.path.walk(path, visit, arg)

遍历path,进入每个目录都调用 visit 函数,visit 函数必须有 3 个参数(arg, dirname, names),dirname 表示当前目录的目录名,names 代表当前目录下的所有文件名,args 则为 walk 的第三个参数。

os.path.supports_unicode_filenames

设置是否可以将任意 Unicode 字符串用作文件名。

  1. 进程管理函数
    主要用于启动新进程、中止已有进程等

方法

说明

os.abort()

生成一个SIGABRT(硬件异常终止)信号给当前进程。在UNIX系统上,默认行为是生成内核转储;在Windows系统上,进程立即返回退出代码3

os.execl(path,arg0,arg1,…)

该函数还有一系列功能历史的函数,比如os.execle(),os.execlp()等,这些函数都是适用参数列表arg0,arg1,…来执行path所代表的执行文件的

os.forkpty()

fork一个子进程

os.kill(pid,sig)

将sig信号发送到gid对应的进程,用于结束该进程

os.kill(pgid,sig)

将sig信号发送到pgid对应的进程组

os.popen(cmd,mode=‘r’,buffering=-1

用于向cmd命令打开读写管道(当mode=r时为只读,mode=rw时为读写,buffering缓冲参数与内置的open()函数有相同的含义。该函数返回的文件对象用于读写字符串,而不是字节。

os.spawnl(mode,path,…)

此函数还有一系列功能类似的函数,比如os.spawnle(),os.spawnlp()等,这些函数都用于在新进程中执行新程序

os.startfile(path[,operation])

对指定文件适用该文件关联的工具执行operation对应的操作,如果不指定operation操作,则默认执行打开(open)操作。operation参数必须是有效的命令行操作项目,比如open(),edit(),print()等

os.system(command)

运行操作系统上的指定命令

示例如下:

import os
# 运行平台cmd命令
# **os.system('cmd')**
# 使用Excel打开g:\\abc.xls文件
os.startfile('g:\\abc.xls')
os.spawnl(os.P_NOWAIT,'E:\\Tools\\Notepad++.7.5.6.bin.x64\\notepad++.exe','')
# 使用python命令执行os_test.py程序
os.execl("D:\\Python\\Python36\\Python.exe","","os_test.py",'i')

如果直接运行上面的程序,可以看到程序运行后使用Excel打开了abc.xls文件,也打开了Notepad++工具,还使用python命令运行了os_test.py文件。但如果将程序中**包裹的代码启用,将看到程序运行后只启动了cmd命令,这是因为使用os.system()函数来运行程序,新程序所在的进程会替代原有进程。
在使用os.execl()函数运行新进程之后,也会取代原有的进程,因此上面程序将这行代码放在了最后。

21.1.3 random模块

random模块主要包含生成伪随机数的各种功能变量和函数,我们还是先来看一下它都包含哪些函数。

>>> import random
>>> random.__all__
['Random', 'SystemRandom', 'betavariate', 'choice', 'choices', 'expovariate', 
'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 
'paretovariate', 'randbytes', 'randint', 'random', 'randrange', 'sample', 'seed', 
'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']
>>>

方法

说明

random.seed(a=None,version=2)

指定种子来初始化伪随机数生成器

random.randrange(start,stop[,step])

返回从start开始到stop结束,步长为step的随机数,其实就相当于choice(range(start,stop,step))的效果,只不过实际底层并不生成区间对象

random.randint(a,b)

生成一个方位为a<=N<=b的随机数,其等同于randrange(a,b+1)的效果

random.choice(seq)

从seq中随机抽取一个元素,如果seq为空,则引发IndexError异常

random.choices(seq,weights=None,*,cum_weights=None,k=1)

从seq序列中抽取k个元素,还可通过weights指定各元素被抽取的权重(被抽取的可能性高低)

random.shuffle(x[,random])

对x序列执行洗牌,随机排列操作

random.sample(population,k)

从population序列中随机抽取k个独立元素

random.random()

生成一个从0.0(包含)开始到1.0(不包含)之间的伪随机浮点数

random.uniform(a,b)

生成一个范围为a<=N<=b的随机数

random.expovariate(lambd)

生成一个呈指数分布的随机数,其中lambd参数(其实应该使lambda,只是lambda是关键字)为1除以期望平均值。如果lambd是正值,则返回的随机数从0至正无穷,为负值,返回从负无穷到0

21.1.4 time模块

time模块主要包含各种提供日期、时间功能的类和函数,该模块既提供了把日期、时间格式化为字符串的功能,也提供了从字符串恢复日期、时间的功能。

>>> import time
>>> [e for e in dir(time) if not e.startswith('_')]
['altzone', 'asctime', 'ctime', 'daylight', 'get_clock_info', 'gmtime', 'localtime', 'mktime', 'monotonic', 'monotonic_ns', 'perf_counter', 'perf_counter_ns', 'process_time', 'process_time_ns', 'sleep', 'strftime', 'strptime', 'struct_time', 'thread_time', 'thread_time_ns', 'time', 'time_ns', 'timezone', 'tzname']
>>>

在time模块内提供了一个time.struct_time类,该类代表一个时间对象,它主要包含9个属性,每个属性的信息如下:

字段名

字段含义


tm_year


如2017、2018等

tm_mon


如2,3等,范围1~12

tm_mday


如2,3等,范围1~31

tm_hour


如2,3等,范围1~23

tm_min


如2,3等,范围0~59

tm_sec


如2,3等,范围0~59

tm_wday


周一为0,范围0~6

tm_yday

一年内的第几天

如65等,范围1~366

tm_isdst

夏令时

0,1或-1

比如,Python可以用time.struct_time(tm_year=2022,tm_mon=7,tm_mday=8,tm_hour=14,tm_min=4,tm_sec=45,tm_wday=4,tm_yday=1,tm_isdst=0)很清晰地代表时间。

此外,Python还可以用一个包含9个元素的元组来代表时间,该元组的9个元素和struct_time对象中9个属性的含义是一一对应的,比如程序可以使用(2022,7,8,14,4,45,4,1,0)来代表时间。

在日期、时间模块内常用的功能函数如下:

方法

说明



time.asctime([t])

将时间元组或struct_time转换为时间字符串。如果不指定参数t,则默认转换当前时间

time.ctime([secs])

将以秒数代表的时间转换为时间字符串,从1970年1月1日0点开始计算,由于时区问题,中国处于东八区,实际上从1970年1月1日8:00开始计算

time.gmtime([secs])

将以秒数代表的时间转换为struct_time对象,如果不传入参数,则使用当前时间(格林尼治时间)

time.localtime([secs])

将以秒数代表的时间转换为代表当前时间的struct_time对象,如果不传入参数,则使用当前时间(本地时间,东八区)

time.mktime(t)

他是localtime的反转函数,用于将struct_time对象或元组代表的时间转换为从1970.1.1日0点到现在的秒数

time.perf_counter()

返回性能计数器的值,单位秒

time.process_time()

返回当前进程使用CPU的时间,单位秒

time.sleep(secs)

暂停secs秒,什么都不干

time.strftime(format[,t])

将时间元组或struct_time对象格式化为指定格式的时间字符串,如果不指定参数t,则默认转换当前时间

time.strptime(string[,format])

将字符串格式的时间解析成struct_time对象

time.time()

返回从1970.1.1日0点到现在过来多少秒

time.timezone

返回本地时区的时间偏移,单位秒

time.tzname

返回本地时区的名字

示例:

>>> time.localtime()
time.struct_time(tm_year=2022, tm_mon=7, tm_mday=8, tm_hour=15, tm_min=7, tm_sec=44, tm_wday=4, tm_yday=189, tm_isdst=0)
>>> time.gmtime()
time.struct_time(tm_year=2022, tm_mon=7, tm_mday=8, tm_hour=7, tm_min=8, tm_sec=18, tm_wday=4, tm_yday=189, tm_isdst=0)
>>>

time.srftime()与time.strptime()涉及到编写格式模板,时间格式字符串支持的指令如下:

指令

含义

%a

本地化的星期几的缩写名,比如Sun代表星期天

%A

本地化星期几的完整名

%b

本地化月份的缩写名,比如Jan代表表一月

% B

本地化月份的完整名

% c

本地化的日期和时间的表示形式

% d

代表一个月中第几天的数值,范围:01-31

% H

代表24小时制的小时,范围:00-23

% I

代表12小时制的小时,范围:01-12

% j

一年中第几天,范围:001-366

% m

代表月份的数值,范围:01-12

%M

代表分钟的数值,范围:00-59

% p

上午或下午的本地化方式,当使用strptime()函数并使用%I指令解析小时时,%p只影响小时字段

%S

代表分钟的数值,范围:00-61,60在表示闰秒的时间戳时有效,61则是由于一些历史原因造成的

%U

代表一年中的第几周,以星期天为每周第一天,范围:00-53,在这种方式下,一年中第一个星期天被认为处于第一周,当使用strptime()函数解析时间字符串时,只有何时指定了星期几和年份该指令才生效

%w

代表星期几的数值,范围:0-6,其中0代表周日

% W

代表一年中第几周,以星期一为每周的第一天,范围:00-53,在这种方式下,一年中第一个星期一被任务处于第一周,当使用strotime()函数解析时间字符串时,只有同时指定了星期几和年份,该指令才生效

%x

本地化的提起表示形式

% X

本地化时间表示形式

% y

年份的缩写,范围:00-99.比如2018简写成18

%Y

年份的完整形式

% z

显示时区偏移

% Z

时区名

% %

代表%

21.1.5 itertools模块(函数相关)

在itertools模块中,主要包含了一些生成迭代器的函数,

>>> import itertools
>>> [e for e in dir(itertools) if not e.startswith('_')]
['accumulate', 'chain', 'combinations', 'combinations_with_replacement', 'compress', 
'count', 'cycle', 'dropwhile', 'filterfalse', 'groupby', 'islice', 'permutations', 
'product', 'repeat', 'starmap', 'takewhile', 'tee', 'zip_longest']
>>>
  1. 生成无限迭代器
  • count(start,[step]):生成start开始,步进为step(默认1)的迭代器,
  • cycle§:生成序列p无限循环的迭代器,
  • repeat(elem[,n]):生成n个元素elem重复的迭代器,不指定n,则生成无限循环。
  1. 其他常用迭代器函数
  • accumulate(p[,func]):生成按照func函数计算序列p内元素形成的迭代器,如果不指定func,默认生成序列p元素累加迭代器,p0,p0+p1,p0+p1+p2,…
  • chain(p,q,…):将多个序列元素”链“在一起,形成新的迭代器
  • compress(data,selectors):根据selectors序列的值对data序列的元素进行过滤,如果selectors[0]的值为真,保留data[0],否则删除,以此类推。
  • dropwhile(pred,seq):使用pred函数对seq序列进行过滤,保留从第一个计算为False的元素seq[i]开始至结束的元素。
  • takewhile(pred,seq):与上一函数相反,去掉从第一个计算结果为False的seq[i]至结束的元素
  • filterfalse(pred,seq):保留序列seq中,使用pred计算结果为True的值
  • islice(seq,[start,]stop[,step]):类似于序列的切片slice()函数,返回seq[start:stop:step]的结果
  • starmap(func,seq):使用func()对seq的元素进行计算,返回计算结果的迭代器,支持序列解包,即seq中多个元素给func传递参数,输出结果。所以变换后的序列长度未必等于len(seq)
  • zip_longers(p,q,…)将p,q等序列中的元素按索引合并成元组,这些元组将作为新学列的元素。
  1. 生成排列组合的工具函数
  • product(p,q,…[repeat=1]):用学了p,q中的元素进行排列组合,相当于使用嵌套循环组合。
  • permutations(p,[,r]):从序列p中取出r个元素组成全排列,将排列得到的元组作为新迭代器的元素
  • combinations(p,r):从序列p中取出r个元素组成全组合,元素不允许重复,将组合得到的元组作为新迭代器的元素
  • conbinations_with_replacement(p,r):从序列p中取出r个元素组成全组合,元素允许重复,将组合得到的元组作为新迭代器的元素。
    示例:
import itertools as it
#product 使用两个序列进行排列组合
for e in it.product('AB','CD'):
    print(''.join(e),end=',')    # AC, AD, BC, BD,
#product 使用一个序列,重复两次进行排列组合
for e in it.product('AB',repeat=2):
    print(''.join(e),end=',')    # AA, AB, BA, BB,
>>> [e for e in it.permutations('ABCD',2)]
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'), ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')]
>>> [e for e in it.combinations('ABCD',2)]
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
>>> [e for e in it.combinations_with_replacement('ABCD',2)]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'C'), ('C', 'D'), ('D', 'D')]
>>>

21.1.6 functools模块

functools模块中主要包含了一些函数装饰器和便捷的功能函数。

>>> import functools
>>> [e for e in dir(functools) if not e.startswith('_')]
['GenericAlias', 'RLock', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', 'cache', 
'cached_property', 'cmp_to_key', 'get_cache_token', 'lru_cache', 'namedtuple', 'partial', 
'partialmethod', 'recursive_repr', 'reduce', 'singledispatch', 'singledispatchmethod', 
'total_ordering', 'update_wrapper', 'wraps']
>>>
  • functools.lru_cache(maxsize=128,type=False): 该函数装饰器使用LRU(最近最少使用)缓存算法来缓存相对耗时的函数结果,避免传入相同的参数重复计算。同时,缓存并不会无限增长,不用的缓存会被释放。其中,maxsize参数用于设置缓存只占用的最大字节数。typed参数用于设置将不同类型的缓存结果分开存放
  • functools.cmp_to_key(func):将func转换为关键字函数
  • @functools.total_ordering:这个类装饰器(作用类似于函数装饰器,只是它用于装饰类)用于为类自动生成比较方法。通常来说,开发者只要提供–lt–()-、–le–()-、–gt–()-、–ge–()其中之一(最好能提供–eq–()方法),@functools.total_ordering装饰器就会为该类生成剩下的比较好的方法。
  • functools.partial(func,*args,**keywords):该函数用于为func函数的部分参数指定参数值,从而得到一个转换后的函数,程序以后调用转换后的函数时,就可以少传入哪些已指定值的参数。
  • functools.partialmethod(func,*args,**keywords):该函数与上一函数含义完全相同,只不过该函数用于为类中的方法设置参数
  • functools.reduce(function,iterable[,initialzer]):将初始值(默认为0,可由initialzer参数指定)、迭代器的当前元素传入function函数,将计算出来的函数结果作为下一次计算的初始值、迭代器的下一元素再次调用function函数,以此类推,直到迭代器的最后一个元素。
  • @functools.singledispatch:该函数装饰器用于实现函数对多个类型进行重载。比如同样的函数名称,为不同的参数类型提供不同的功能实现,该函数的本质就是根据参数类型的变换,将函数转向调用不同的函数。
  • functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES):对wrapper函数进行包装,使之看上去就像wrapper(被包装)函数。
  • functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMETS,updated=WRAPPER_UPDATES):该函数装饰器用于修饰包装函数,使包装函数看上去就像wrapper函数。
from functools import *
#设初始值(默认为0)为x,当序列元素为y,将x+y作为下一次初始值
print(reduce(lambda x,y:x+y,range(5)))  #10
print(reduce(lambda x,y:x+y,range(6)))   #15
#设初始值为10
print(reduce(lambda x,y:x+y,range(6),10))   +25

class User:
    def __init__(self,name)
        self.name = name
    def __repr__(self)
        return 'User[name]=%s'% self,name
#定义一个老式的大小比较函数,User的那么越长,该User越大
def old_cmp(u1,u2)
    return len(u1.name)-len(u2.name)
my_data = [User('kotlin'),User('Swift'),User('Go'),User('Java')]
# 对my_data排序,需要将关键字函数(调用cmp_to_key将old_cmp转换为关键字函数)
my_data.sort(key=cmp_to_key(old_cmp))
Print (my_data)  

@lru_cache(maxsize=32)
def factorial(n):
    print(’计算 %d 的阶乘',%n)
    if n==1:
        return 1
    else:
        return n*factorial(n-1)
#只有这行会计算,然后会缓存5、4、3、2、1的阶乘
print(factorial(5))
print(factorial(3))
print(factorial(5))

#内置函数int函数默认将十进制的字符串转换为整数
print(int('1234')
# 为int函数的base参数指定参数值
basetwo = partial(int,base =2 )
basetwo.__doc__ = '将二进制形式的字符串转换为整数'
#相当于执行base=2的int函数(将二进制的字符串转为十进制)
print(basetwo('10010'))    #18
print(int('10010',2))    #18

partialmethod()与partial()函数的作用基本相似,只是用于类方法部分参数值绑定。

from functools import *
class Cell:
    def __init__(self):
        self.alive = False
    # @property装饰器指定该方法可使用属性语句访问
    @property
    def alive(self):
        return self._alive
    def set_state(self,state):
        self.alive = bool(state)
    #指定set_alive()方法,就是将set_state()方法的state参数指定为Ture
    set_alive = partialmethod(set_state,Ture)
    #指定set_dead()方法,就是将set_state()方法的state参数指定为False
    set_dead = partialmethod(set_state,False)
c = Cell()
print(c.alive) # False
#相当于调用c.set_state(Ture)
c.set_alive()
print(c.alive)   # Ture
#相当于调用c.set_state(False)
c.set_alive()
print(c.alive)   # False

下面示范@total_ording类装饰器的作用。

from functools import *
@total_ordering
class User:
    def __init__(self,name):
        self.name = name
    def __repr__(self):
        return 'User|name=%s'% self.name
    #根据是否有name属性决定是否可比较
    def _is_valid_operand(self,other):
        #内置函数hasattr()判断other对象中是否有name属性
        rentrn hasattr(other,"name")
    def __eq__(self,other):
        if not self._is_valid_operaned(other)
            return NotImplemented
        #根据name判断是否相等(都转换成小写比较,或略大小写)
        return self.name.lower() == other.lastname.lower()
    def __lt__(self,other):
        if not self._is_valid_operaned(other)
            return NotImplemented
        #根据name判断是否小于(都转换成小写比较,或略大小写)
        return self.name.lower() < other.lastname.lower()
#打印被装饰之后的User类中的__gt__方法
print(User.__gt__)   #自动生成了大于的比较

@singledispatch函数装饰器的作用使根据函数参数类型,实现调用不同的函数。

from functools import *
@singledispatch
def test(are,verbose):
    if verbose:
        print('默认参数为:',end = "")
    print(arg)
#限制test函数的第一个参数为int类型的函数版本,register()方法是使用@singledispatch修饰后,自动添加的方法,用于为指定类型注册被转向调用的函数。
@test.register(int)
def (argu,verbose):
    if verbose:
        print('整型参数为:',end = "")
    print(argu)
test('Python',True)   #默认参数为:Python
test(20,True)        #整型参数为:20

21.1.7 JSON及json模块

JSON是一种轻量级、跨平台、跨语言的数据交换格式,JSON格式被广泛应用于各种语言的数据交换中,Python也提供了对JSON的支持。

21.1.7.1 JSON基础知识

JSON全称是JavaScript Object Notation,即JavaScript对象符号。
JSON主要有两种数据结构:

  • 由key-value对组成的数据结构,在Python中对应dict(字典)结构。
  • 有序集合,在Python中对应列表。
    JavaScrip1.2开始创建对象的方法如下:
    使用JSON语法创建对象:
object = 
{
porpertyName1:porpertyValue1,
porpertyName2:porpertyValue2,
...
porpertyNamen:porpertyValuen
}

以上示例是使用JSON语法创建的是JavaScript对象,属性值可以是函数、数组,甚至是另一个JSON对象。
使用JSON语法创建数组:

var a = ['value1','value2',...,valuen];

JSON数据结构与Python结构转换关系如下表:

JSON类型

Python类型

对象(object)

字典(dict)

数组(array)

列表(list)

字符串(string)

字符串(str)

整数(number(int))

整数(int)

实数(number(real))

浮点数(float)

ture

Ture

false

False

null

None

鉴于JSON语法简单易用,而且作为数据传输载体时,数据传输量更小,因此在夸平台的数据交换中,往往采用JSON作为数据交换的格式。

Python使用json模块将数据转换为JSON格式,然后在进行传输,跨语言处理操作等,最后在恢复成Python使用的数据结构进行处理。下面我们介绍Python的json模块。

21.1.7.2 Python的json模块

>>> import json
>>> json.__all__
['dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder']
>>>

python中常用的模块 python最全常用模块_python_02


json模块中使用的函数主要有4个,功能如上图所示。

  • json.dump(obj,fp,*,skipkeys=False,ensure_ascii=Ture,check_circular=Ture,allow_nan=Ture,cls=None,indent=None,separator=None,default=None,sort_keys=False,**kw):将obj对象转换成JSON字符串并输出到fp流中,fp是一个支持write()方法的类文件对象。
  • json.dumps(obj,*,skipkeys=False,ensure_ascii=Ture,check_circular=Ture,allow_nan=Ture,cls=None,indent=None,separator=None,default=None,sort_keys=False,**kw):将obj对象转换为JSON字符串,并返回该字符串。
  • json.load(fp,*,cls=None,object_hook=None,pares_float=None,pares_int=None,parse_constant=None,object_pairs_hook=None,**kw): 从fp流读取JSON字符串,将其恢复成Python对象,其中,fp是一个支持write()方法的类文件对象。
  • json.loads(s,*,encoding=None,cls=None,object_hook=None,pares_float=None,pares_int=None,parse_constant=None,object_pairs_hook=None,**kw): 将JSON字符串s恢复成Python对象。
    编码示例:
import json
# 将Python对象(元组会被当成数组)转换为JSON字符串
s = json.dumps(['python',{'favorite':('C','C++','Java',25)}])
print(s)  #['python',{'favorite':['C','C++','Java',25]}]
# 字典对象转换为JSON对象
s2 = json.dumps({'c':0,'b':0,'a':0},sort_keys=Ture)
print(s2)   #{'a':0,'b':0,'c':0}
#将列表转换为JSON字符串,并指定分隔符
s3 = json.dumps([1,2,3,{'x':4,'y':5}],separators=('&'))  
#此时会报错,因为我们缺少了一个指定的分隔符,指定分隔符要指定两个,一个用于元素分割,一个用于字典定义分割。
s4 = json.dumps([1,2,3,{'x':4,'y':5}],separators=('&','$'))
print(s4) # [1&2&3&{"x"$4&"y"$5}]
# 指定indent为4,意味着转换的JSON字符串有缩进
s5 = json.dumps({'Python':5,'C':7},sort_keys=True,indent=4)
print(s5)
#
{
    "C": 7,
    "Python": 5
}
s5 = json.dumps({'Python':5,'C':7},sort_keys=True,indent=10)
print(s5)   
#
{
          "C": 7,
          "Python": 5
}
#打开一个文件,如果没有则新建一个
f = open('a.json','w')
#使用dump()函数将转换得到的JSON字符串输出到文件中
json.dump(['C','Python],f)

dumps()与dump()的功能,所支持选项基本相同,只是dumps()直接返回JSON字符串,dump()将转换的字符串输出到文件

解码示例:

#直接解码的方式基本与编码相同,不在举例
#解码可以自定义解码函数,以便于完成Python特殊类型(如复数、矩阵)的转换
#定义一个自定义转换函数
def as_comlpex(dct)
    if '__complex__'in dct:
        return complex(dct['real'],dct['imag'])
    return dct
# 使用自定义的恢复函数
#自定义的恢复函数将real数据转换为复数的实部,将imag转换为虚部
result = json.loads({'__complex__':Ture,'real':1,'imag':2},object_hook=as_complex)
print (result) #(1+2j)
f=open('a.json')
result2 = json.load(f)
print(result2)    #['C','Python]

Python支持的数据类型要比JSON丰富的多,比如复数、矩阵等,如果直接使用dumps()或dump()函数进行转换,程序肯定会出问题。此时就需要开发者对JSONEncoder类进行扩展。
例如:

import json
#定义JSONEncoder的子类
class ComplexEncoder(json.JSONEncoder):
    def default(self,obj):
        #如果需转换的对象是复数,程序负责处理
        if isinstance(obj,complex):
            return {"__complex__":'Ture','real':obj.real,'imag':obj.imag}
        #对于其他类型,直接使用JSONEncoder处理
        return json.JSONEncoder.default(self,obj)
#使用dumps()或dump()函数中通过cls属性指定自定义子类处理
s1 = json.dumps(2+1j,cls=ComplexEncoder)
print(s)  # '{"__complex__":'ture','real':2.0,'imag':1.0}'
直接使用JSONEncoder的自定义子类的encode()方法转换
s2 = ComplexEncoder().encode(2+1j)
print(s2)   # '{"__complex__":'ture','real':2.0,'imag':1.0}'