第十章 python 常见模块

python 常见模块

  • 一、sys模块
      • 1. sys模块的作用
      • 2. sys模块的参考页面:
      • 3. sys模块中常用的属性和函数:
  • 二、os模块
      • 1. os模块的作用
      • 2. os模块的参考页面:
      • 3. os模块中常用的属性和函数:
  • 三、random模块
      • 1. random模块的作用
      • 2. random模块的参考页面:
      • 3. random模块中常用的属性和函数:
  • 四、time模块
      • 1. time模块的作用
      • 2. time模块的参考页面:
      • 3. time模块中常用的属性和函数:
  • 五、JSON支持
      • 1. JSON模块的作用
      • 2. JSON的基本知识
        • (1)使用JSON语法创建对象
        • (2)使用JSON语法创建数组
      • 3. python的JSON支持
        • (1)JSON类型转换为python类型的对应关系
        • (2)python类型转换为JSON类型的对应关系
      • 4. JSON模块中常用的属性和函数:
      • 5. JSON主要应用场景
  • 六、正则表达式(re模块)
      • 1. 正则表达式的作用
      • 2. 正则表达式支持
        • (1)re.compile(pattern, flags=0)
        • (2)re.match(pattern, string, flags=0)
        • (3)re.search(pattern, string, flags=0)
        • (4)re.findall(pattern, string, flags=())
        • (5)re.finditer(pattern, string, flags=())
        • (6)re.fullmatch(pattern, string, flagsM))
        • (7)re.sub(pattern, repl, string, count=0, flags=0)
        • (8)re.split(pattern, string, maxsplit=(), flags=0)
        • (9)re.purge()
        • (10)re-escape(pattern)
      • 3. 正则表达式旗标
      • 4. 创建正则表达式
        • (1)正则表达式所支持的合法字符
        • (2)正则表达式中的特殊字符
        • (3)正则表达式所支持的预定义字符
        • (4)方括号表达式
        • (5)边界匹配符
      • 5. 子表达式(P244-246)
      • 6. 贪婪模式与勉强模式
  • 七、容器相关类
      • 1. set和frozenset
      • 2. 双端队列(deque)
      • 3. python的堆操作(heapq)
  • 八、collections下的容器支持
      • 1. ChainMap(多个dict链在一起)
      • 2. Counter对象
      • 3. defaultdict对象
      • 4. namedtuple工厂函数
      • 5. OrderedDict对象
  • 九、函数相关模块
      • 1. itertools模块的功能函数(迭代器、排列组合)
      • 2. functools模块的功能函数(装饰器)

一、sys模块

1. sys模块的作用

sys模块代表了python解释器,主要用于获取和python解释器相关的信息。
[e for r in dir(sys) if not e.startswith(’_’)]

2. sys模块的参考页面:

https://docs.python.org/3/library/sys.html

3. sys模块中常用的属性和函数:

  • sys.argy:获取运行Python程序的命令行参数。argv属性值是一个列表
    其中sys.argv[0]通常就是指该Python程序,
    sys.argv[l]代表为Python程序提供的第一个参数,
    sys.argv[2]代表为Python程序提供的第二个参数……依此类推。
  • sys.byteorder:显示本地字节序的指示符。如果本地字节序是大端模式,则该属性返回big;否则返回little。
  • sys.copyright:该属性返回与Python解释器有关的版权信息。
  • sys.executable:该属性返回Python解释器在磁盘上的存储路径。
  • sys.exit():通过引发SystemExit异常来退出程序。将其放在try块中不能阻止finally块的执行。
  • 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位平台上,该属性值为2** 31-1;在64位平台上,该属性值为2**63-1。
  • sys.modules:返回模块名和载入模块对应关系的字典。
  • sys.path:该属性指定Python査找模块的路径列表。程序可通过修改该属性来动态增加Python加载模块的路径。
  • sys.platform:返回Python解释器所在平台的标识符。
  • sys.stdin:返回系统的标准输入流—— 一个类文件对象。
  • sys.stdout:返回系统的标准输出流—— 一个类文件对象。
  • sys.stderr:返回系统的错误输出流—— 一个类文件对象。
  • sys.version:返回当前Python解释器的版本信息。
  • sys.winver:返回当前Python解释器的主版本号。

二、os模块

1. os模块的作用

os模块代表了程序所在的操作系统,主要用于获取程序运行所在操作系统的相关信息。
os.__all__命令

2. os模块的参考页面:

https://docs.python.org/3/library/os.html

3. os模块中常用的属性和函数:

  • os.name:返回导入依赖模块的操作系统名称,通常可返回,‘posix’、‘nt’、‘java’等值其中之一。
  • os.environ:返回在当前系统上所有环境变量组成的字典。
  • os.fsencode(filename):该函数对类路径(path-like)的文件名进行编码。
  • os.fedecode(filename):该函数对类路径(path-like)的文件名进行解码。
  • os.PathLike:这是一个类,代表一个类路径(path-like)对象。
  • os.getenv(key, default=None):获取指定环境变量的值。
  • os.getlogin():返回当前系统的登录用户名。与该函数对应的还有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系统(如Linux、Mac OS X)上多条路径之间的分隔符是英文冒号(:)。
  • os.linesep:返回当前系统的换行符。一般在Windows系统上换行符是“\r\n";在UNIX系统上换行符是“\n” ;在Mac OS X系统上换行符是“\r”。
  • os.urandom(size):返回适合作为加密使用的、最多由N个字节组成的bytes对象。该函数通过操作系统特定的随机性来源返回随机字节,该随机字节通常是不可预测的,因此适用于绝大部分加密场景。

在os模块下还包含大量操作文件和目录的功能函数。

在os模块下与进程管理相关的函数如下。

  • os.abort():生成一个SIGABRT信号给当前进程。在UNIX系统上,默认行为是生成内核转储;在Windows系统上,进程立即返回退出代码3。
  • os.execl(path, arg0, argl, …):该函数还有一系列功能类似的函数,比如 os.execle()、os. execlp()等,这些函数都是使用参数列表argO,argl,…来执行path所代表的执行文件的。
  • os.fbrkpty(): fbrk—个子进程。
  • os.kill(pid, sig):将sig信号发送到pid对应的过程,用于结束该进程。
  • os.killpg(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):运行操作系统上的指定命令。

三、random模块

1. random模块的作用

random模块主要包含生成伪随机数的各种功能变量和函数。
random.__all__命令

2. random模块的参考页面:

https://docs.python.org/3/library/random.html

3. random模块中常用的属性和函数:

  • 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.unifbrm(a, b):生成一个范围为a≤N≤b的随机数。
  • random.expovariate(lambd):生成呈指数分布的随机数。其中lambd参数(其实应该是lambda,只是lambda是Python关键字,所以简写成lambd)为1除以期望平均值。如果lambd是正值,则返回的随机数是从0到正无穷大;如果lambd为负值,则返回的随机数是从负无
    穷大到0。

四、time模块

1. time模块的作用

time模块主要包含各种提供日期、时间功能的类和函数。该模块既提供了把日期、时间格式化为字符串的功能,也提供了从字符串恢复日期、时间的功能。
[e for r in dir(time) if not e.startswith(’_’)]

2. time模块的参考页面:

https://docs.python.org/3/library/time.html

3. time模块中常用的属性和函数:

time.struct_time类中各属性的含义:

字段名 字段含义
tm_year 如2017、2018等
tm_mom 如2、3等,范围为1~12
tm_mday 如2、3等,范围为1~12
tm_hour 如2、3等,范围为1~12
tm_min 如2、3等,范围为1~12
tm_sec 如2、3等,范围为1~12
tm_wday 周一为0,范围为0~6
tm_yday 一年内第几天 如65,范围为1~366
tm_isdst 夏令时 0、1或-1

比如,Python 可以用 time.struct_time(tm_year=2018, tm_mon=5, tm_mday=2, tm_hour=8,tm_min=0, tm_sec=30, tm_wday=3, tm_yday=1, tm_isdst=0)很清晰地代表时间。
此外,Python还可以用一个包含9个元素的元组来代表时间,该元组的9个元素和struct_time对象中9个属性的含义是一一对应的。比如程序可以使用(2018,5,2, 8,0,30,3, 1,0)来代表时商。

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

  • time.asctime([t]):将时间元组或struct_time转换为时间字符串。如果不指定参数t,则默认
    转换当前时间。
  • time.ctime([secs]):将以秒数代表的时间转换为时间字符串。
  • 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:返回本地时区的名字。

代码如下:

# 生命时间,假如活到90岁
import time

starttimes = (1990, 6, 23, 0, 0, 0, 0, 0, 0)
endtimes = (2080, 6, 23, 0, 0, 0, 0, 0, 0)

# 出生日期,"1990年6月23日"
starttime = time.mktime(starttimes)
# 当面时间
nowtime = time.strftime('%Y年%m年%d日 %H时%M分%S秒',time.localtime(time.time()))
# 已度时光(荒废),秒数

overtime = time.time() - starttime
# 时间尽头(90岁),"2080年6月23日"
endtime = time.mktime(endtimes)
# 剩余时间(奋斗),年、月、日、时、分、秒
# 秒
futuretimes = endtime - time.time()
# 分
futuretimeM = futuretimes//60
# 时
futuretimeh = futuretimeM//60
# 天
futuretimed = futuretimeh//24
# 周
futuretimew = futuretimed//7
# 月
futuretimem = futuretimew//4.34
# 年
futuretimey = futuretimem//12

print(nowtime)
print(overtime)
print(('%s秒') % int(futuretimes))
print(('%s分') % int(futuretimeM))
print(('%s时') % int(futuretimeh))
print(('%s天') % int(futuretimed))
print(('%s周') % int(futuretimew))
print(('%s月') % int(futuretimem))
print(('%s年') % int(futuretimey))


# 遗留问题,加单位,参考P86练习

python时间格式字符串所支持的指令

指令 含义
%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 上午或下午的本地化方式。当使用strptimeO函数并使用%1指令解析小时时,%p只影响小时字段
%S 代表分钟的数值,范围:00-61,该范围确实是00~61, 60在表示闰秒的时间戮时有效,而61则是由于一些
历史原因造成的
%U 代表一年中第几周,以星期天为每周的第一天,范围:00~53。在这种方式下,一年中第一个星期天被认为处
于第一周。当使用stiptime()函数解析时间字符串时,只有同时指定了星期几和年份该指令才会有效
%w 代表星期几的数值,范围:0~6,其中0代表
%W 代表一年中第儿周,以星期一为每周的第一天,范围:00-53。在这种方式下,一年中第一个星期-被认为处
于第一周。当使用strptime()函数解析时间字符串时,只有同时指定了星期几和年份该指令才会有效
%x 本地化的日期的表示形式
%X 本地化的日期的表现形式
%y 年份的缩写,范围:00~99,比如2018年就简写就好了
%Y 年份的完整形式,如2018
%z 显示时区偏移
%Z 时区名(如果时区不存在,则显示的空)
%% 用于代表%符号

五、JSON支持

1. JSON模块的作用

JSON是一种轻量级、跨平台、跨语言的数据交换格式,JSON格式被广泛应用于各种语言的
数据交换中。
json.__all __

2. JSON的基本知识

JSON 的全称是 JavaScript Object Notation。

JSON主要有如下两种数据结构。

  • 由key-value对组成的数据结构。这种数据结构在不同的语言中有不同的实现。例如,在
    JavaScript中是一个对象;在Python中是一•种diet对象;在C语言中是一个struct;在其他
    语言中,则可能是 record、dictionary、hash table 等。
  • 有序集合。这种数据结构在Python中对应于列表;在其他语言中,可能对应于list、vector.
    数组和序列等。

(1)使用JSON语法创建对象

语法格式:
var a = { key1 : value1, key2: value2,…}

(2)使用JSON语法创建数组

语法格式:
var a = [value1, value2,…]

3. python的JSON支持

(1)JSON类型转换为python类型的对应关系

JSON类型 python类型
对象(object) 字典(dict)
数组(array) 列表(list)
字符串(string) 字符串(str)
整数(number(int)) 整数(int)
实数(number(real)) 浮点数(float)
true True
false False
null None

(2)python类型转换为JSON类型的对应关系

python类型 JSON类型
字典(dict) 对象(object)
列表(list)和元组(tuple) 数组(array)
字符串(str) 字符串(string)
整数、浮点数、以及整型、浮点型派生的枚举(float,int-&float-derived Enums) 整数(number)
实数(number(real)) 浮点数(float)
True true
False false
None null

4. JSON模块中常用的属性和函数:

  • json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan= True,
    cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw):将 obj 对象
    转换成JSON字符串输出到fp流中,币是一个支持write()方法的类文件对象。
  • json.dumps(obj, * , skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan= True,
    cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw):将 obj 对象
    转换为JSON字符串,并返回该JSON字符串。
  • json.Ioad(fp,* , cls=None, object_hook=None, parse_float=None, parse_int=None, parse_
    constant=None, object_pairs_hook=None, **kw):从fp流读取 JSON 字符串,将其恢复成 JSON对象,其中fp是一个支持write()方法的类文件对象。
  • json.loads(s, * , encoding=None, cls=None, object_hobk=None, parse_float=None, parse_
    int=None, parse_constant=None, object_pairs_hook=None, **kw):将 JSON 字符串 s 恢复成
    JSON对象。

5. JSON主要应用场景

decode(用load或loads)
JSON字符串→python对象

encode(用dump或dumps)
python对象→JSON字符串

dumps()和dump()函数的encode操作(将Python对象转换成JSON字符串)

代码如下:

import json
# 将Python对象转JSON字符串(元组会当成数组)
s = json.dumps(['yeeku', {
     'favorite': ('coding', None, 'game', 25)}])
print(s) # ["yeeku", {"favorite": ["coding", null, "game", 25]}]
# 简单的Python字符串转JSON
s2 = json.dumps("\"foo\bar")
print(s2) #"\"foo\bar"
# 简单的Python字符串转JSON
s3 = json.dumps('\\')
print(s3) #"\\"
# Python的dict对象转JSON,并对key排序
s4 = json.dumps({
     "c": 0, "b": 0, "a": 0}, sort_keys=True)
print(s4) #{"a": 0, "b": 0, "c": 0}
# 将Python列表转JSON,
# 并指定JSON分隔符:逗号和冒号之后没有空格(默认有空格)
s5 = json.dumps([1, 2, 3, {
     'x': 5, 'y': 7}], separators=(',', ':'))
# 输出的JSON字符串中逗号和冒号之后没有空格
print(s5) # '[1,2,3,{"4":5,"6":7}]'
# 指定indent为4,意味着转换的JSON字符串有缩进
s6 = json.dumps({
     'Python': 5, 'Kotlin': 7}, sort_keys=True, indent=4)
print(s6)
# 使用JSONEncoder的encode方法将Python转JSON
s7 = json.JSONEncoder().encode({
     "names": ("孙悟空", "齐天大圣")})
print(s7) # {"names": ["\u5b59\u609f\u7a7a", "\u9f50\u5929\u5927\u5723"]}
f = open('a.json', 'w')
# 使用dump()函数将转换得到JSON字符串输出到文件
json.dump(['Kotlin', {
     'Python': 'excellent'}], f)

sort_keys:排序,(sort_keys=True)
indent:缩进,(indent=4)
separators:没有空格(separators=(’,’, ‘:’))

loads()和load()函数的decode操作(将JSON字符串转换成Python对象)。

代码如下:

import json
# 将JSON字符串恢复成Python列表
result1 = json.loads('["yeeku", {"favorite": ["coding", null, "game", 25]}]')
print(result1) # ['yeeku', {'favorite': ['coding', None, 'game', 25]}]
# 将JSON字符串恢复成Python字符串
result2 = json.loads('"\\"foo\\"bar"')
print(result2) # "foo"bar
# 定义一个自定义的转化函数
def as_complex(dct):
    if '__complex__' in dct:
        return complex(dct['real'], dct['imag'])
    return dct
# 使用自定义的恢复函数
# 自定义回复函数将real数据转成复数的实部,将imag转成复数的虚部
result3 = json.loads('{"__complex__": true, "real": 1, "imag": 2}',\
    object_hook=as_complex)
print(result3) # (1+2j)
f = open('a.json')
# 从文件流恢复JSON列表
result4 = json.load(f)
print(result4) # ['Kotlin', {'Python': 'excellent'}]

六、正则表达式(re模块)

1. 正则表达式的作用

正则表达式(Regular Expression)用于描述一种字符串匹配的模式(Pattern),它可用于检査一个字符串是否含有某个子串,也可用于从字符串中提取匹配的子串,或者对字符串中匹配的子串执行替换操作。

2. 正则表达式支持

re.__all __

(1)re.compile(pattern, flags=0)

该函数用于将正则表达式字符串编译成_sre.SRE_Pattem对象,该对象代表了正则表达式编译之后在内存中的对象,它可以缓存并/用正则尾达式字符串。如果程序需要多次使用同一个正则表达式字符串,则可考虑先编译它。
该函数的partem参数就是它所编译的正则表达式字符串,flags则代表了正则表达式的匹配旗
标。

(2)re.match(pattern, string, flags=0)

尝试从字符串的开始位置来匹配正则表达式,如果从开始
位置匹配不成功,match。函数就返回None。其中pattern参数代表正则表达式;string代表
被匹配的字符串;flags则代表正则表达式的匹配旗标。该函数返回_sre.SRE_Match对象,
该对象包含的span(n)方法用于获取第n+1个组的匹配位置,group(n)方法用于获取第n+1
个组所匹配的子串。

(3)re.search(pattern, string, flags=0)

扫描整个字符串,并返回字符串中第一处匹配pattern的
匹配对象。其中pattern参数代表正则表达式:string代表被匹配的字符串:flags则代表正
则表达式的匹配旗标。该函数也返回sre.SRE Match对象。

match()与search()的区别在于:match()必须从字符串开始处就匹配,
但search()则可以搜索整个字符串。

span()、group()

代码如下:

import re
m1 = re.match('www', 'www.fkit.org')# 开始位置可以匹配
print(m1.span())  # span返回匹配的位置
print(m1.group()) # group返回匹配的组
print(re.match('fkit', 'www.fkit.com')) # 开始位置匹配不到,返回None
m2 = re.search('www', 'www.fkit.org') # 开始位置可以匹配
print(m2.span())
print(m2.group())
m3 = re.search('fkit', 'www.fkit.com') # 中间位置可以匹配,返回Match对象
print(m3.span())
print(m3.group())

>> (0, 3)
>> WWW
>> None
>> (0, 3)
>> WWW
>> (4, 8)
>> fkit

(4)re.findall(pattern, string, flags=())

扫描整个字符串,并返回字符串中所有匹配pattern的子
串组成的列表。其中pattern参数代表正则表达式;string代表被匹配的字符串;flags则代
表正则表达式的匹配旗标。

(5)re.finditer(pattern, string, flags=())

扫描整个字符串,并返回字符串中所有匹配pattern的子
串组成的迭代器,迭代器的元素是_sre.SRE_Match对象。其中pattern参数代表正则表达式;
string代表被匹配的字符串;flags则代表正则表达式的匹配旗标。

findall()与finditer()函数的功能基本相似,区别在于它们的返回值不同,
findall()函数返回所有匹配patten的子串组成的列表;而finditer()函数则返回所有匹配pattern的子
串组成的迭代器。
如果对比findall(), finditer()和search()函数,它们的区别也很明显,search。只返回字符串中第
一处匹配pattern的子串;而findall()和finditer()则返回字符串中所有匹配pattern的子串。

re.I、span()、group()

代码如下:

import re
# 返回所有匹配pattern的子串组成的列表, 忽略大小写
print(re.findall('fkit', 'FkIt is very good , Fkit.org is my favorite' , re.I))
# 返回所有匹配pattern的子串组成的迭代器, 忽略大小写
it = re.finditer('fkit', 'FkIt is very good , Fkit.org is my favorite' , re.I)
for e in it:
    print(str(e.span()) + "-->" + e.group())

>> ['FkIt', 'Fkit']
>> 0-->FkIt
>> 20-->Fkit

(6)re.fullmatch(pattern, string, flagsM))

该函数要求整个字符串能匹配pattern,如果匹配则返
回包含匹配信息的一sre.SRE_Match对象;否则返回None。

(7)re.sub(pattern, repl, string, count=0, flags=0)

该函数用于将 string 字符串中所有匹配 pattern
的内容替换成repl; repl既可是被替换的字符串,也可是一个函数。count参数控制最多替
换多少次,如果指定count为0,则表示全部替换。

代码如下:

import re
my_date = '2020-02-12'
# 将my_date字符串里中画线替换成斜线
print(re.sub(r'-', '/' , my_date))
# 将my_date字符串里中画线替换成斜线,只替换一次
print(re.sub(r'-', '/' , my_date, 1))

>> 2020/02/12
>> 2020/02-12

# 在匹配的字符串前后添加内容
def fun(matched):
    # matched就是匹配对象,通过该对象的group()方法可获取被匹配的字符串
    value = "《疯狂" + (matched.group('lang')) + "讲义》"
    return value
s = 'Python很好,Kotlin也很好'
# 将s里面的英文单词(用re.A旗标控制)进行替换
# 使用fun函数指定替换的内容
print(re.sub(r'(?P\w+)', fun, s, flags=re.A))

>> 《疯狂Python讲义》很好,《疯狂Kotlin讲义》也很好

由于此时还未深入介绍正则表达式的语法,因此前面所使用的正则表达式都很简单,但此处使
用了一个稍微复杂的正则表达式:r’(?P < lang > \w+)'

r’(?P < lang > \w+)'正则表达式用圆括号表达式创建了一个组,并使用“?P”选项为该组起名为
lang——所起的组名要放在尖括号内。剩下的“\w+”才是正则表达式的内容,其中“\w”代表任
意字符;"+”用于限定前面的“\w”可出现一次到多次,因此“\w+”代表一个或多个任意字符。

又由于程序执行sub()函数时指定了 re.A选项,这样“\w”就只能代表ASCII字符,不能代表汉字。
当使用sub()函数执行替换时,正则表达式“\w+”所匹配的内容可以通过组名“lang”来获取,
这样fun()函数就调用了 matched.group(‘lang’)来获取"\w+"所匹配的内容。

(8)re.split(pattern, string, maxsplit=(), flags=0)

使用 pattern 对 string 进行分割,该函数返回分
割得到的多个子串组成的列表。其中maxsplit参数控制最多分割几次。

代码如下:

import re
# 使用逗号对字符串进行分割
print(re.split(', ', 'fkit, fkjava, crazyit'))
# 输出:['fkit', 'fkjava', 'crazyit']
# 指定只分割1次,被切分成2个子串
print(re.split(', ', 'fkit, fkjava, crazyit', 1))
# 输出:['fkit', 'fkjava, crazyit']
# 使用a进行分割
print(re.split('a', 'fkit, fkjava, crazyit'))
# 输出:['fkit, fkj', 'v', ', cr', 'zyit']
# 使用x进行分割,没有匹配内容,则不会执行分割
print(re.split('x', 'fkit, fkjava, crazyit'))
# 输出:['fkit, fkjava, crazyit']


#print(re.split('\W+', ' runoob, runoob, runoob.'))
#['', ' ', 'runoob', ', ', 'runoob', ', ', 'runoob', '.', '']
#re.split('\W+', ' runoob, runoob, runoob.', 1) 
#['', 'runoob, runoob, runoob.']

#>>> re.split('a*', 'hello world')   # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
#['hello world']

(9)re.purge()

清除正则表达式缓存。

(10)re-escape(pattern)

对模式中除ASCII字符、数值、下画线(_)之外的其他字符进行转义。

代码如下:

import re
# 对模式中特殊字符进行转义
print(re.escape(r'www.crazyit.org is good, i love it!'))
# 输出:www\.crazyit\.org\ is\ good\,\ i\ love\ it\!
print(re.escape(r'A-Zand0-9?'))
# 输出:A\-Zand0\-9\?
import re
# 编译得到正则表达式对象
pa = re.compile('fkit')
# 调用match方法,原本应该从开始匹配,
# 此处指定从索引为4的地方开始匹配,就可以成功匹配了
print(pa.match('www.fkit.org', 4).span()) # (4, 8)
# 此处指定从索引为4到索引6之间执行匹配,匹配失败
print(pa.match('www.fkit.org', 4, 6)) # None
# 此处指定从索引为4到索引8之间执行全匹配,匹配成功
print(pa.fullmatch('www.fkit.org', 4, 8).span()) # (4, 8)

_sre.SRE Match对象包含了如下方法或属性。

  • match.group([group 1,…]):获取该匹配对象中指定组所匹配的字符串。
  • match.__ etitem_(g):这是 match.group(g)的简化写法。由于 match 对象提供了_getitem_()
    方法,因此程序可使用match[g]来代替match.group(g)o
  • match.groups(default=None):返回match对象中所有组所匹配的字符串组成的元组。
  • match.groupdict(default=None):返回match对象中所有组所匹配的字符串组成的字典。
  • match.start([group]):获取该匹配对象中指定组所匹配的字符串的开始位置。
  • match.end([group]):获取该匹配对象中指定组所匹配的字符串的结束位置。
  • match.span([group]):获取该匹配对象中指定组所匹配的字符串的开始位置和结束位置。该
    方法相当于同时返回start()^ end()方法的返回值。
import re
# 在正则表达式中使用组
m = re.search(r'(fkit).(org)', r"www.fkit.org is a good domain")
print(m.group(0)) # fkit.org
# 调用简化写法,底层是调用m.__getitem__(0)
print(m[0]) # fkit.org
print(m.span(0)) # (4, 12)
print(m.group(1)) # fkit
# 调用简化写法,底层是调用m.__getitem__(1)
print(m[1]) # fkit
print(m.span(1)) # (4, 8)
print(m.group(2)) # org
# 调用简化写法,底层是调用m.__getitem__(2)
print(m[2]) # org
print(m.span(2)) # (9, 12)
# 返回所有组所匹配的字符串组成的元组

如果在正则表达式中为组指定了名字(用?P〈名字〉为正则表达式的组指定名字),就可以调用
groupdict()方法来获取所有组所匹配的字符串组成的字典一其中组名作为字典的key。例如如下
代码。

代码如下:

# 为正则表达式定义了两个组,并为组指定了名字
m2 = re.search(r'(?Pfkit).(?Porg)',\
r"www.fkit.org is a good domain")
print(m2.groupdict()) # {'prefix'1: 'fkit', 'suffix': 'org'}

>> {
     'prefix': 'fkit', 'suffix': 'org'}
  • match.pos:该属性返回传给正则表达式对象的search。、match。等方法的pos参数。
  • match.endpos:该属性返回传给正则表达式对象的search。、match。等方法的endpos参数。
  • matchJastindex:该属性返回最后一个匹配的捕获组的整数索引。如果没有组匹配,该属性
    返回None。例如用(a)b、((a)(b))或((ab))对字符串,ab,执行匹配,该属性都会返回1;但如果
    使用(a)(b)正则表达式对,ab,执行匹配,则lastindex等于2。
  • matchJastgroup:该属性返回最后一个匹配的捕获组的名字;如果该组没有名字或根本没有
    组匹配,该属性返回None-
  • match.re:该属性返回执行正则表达式匹配时所用的正则表达式。
  • match.string:该属性返回执行正则表达式匹配时所用的字符串。-

3. 正则表达式旗标

  • re.A或re.ASCII:该旗标控制\w, \W, \b, \B, \d, \D, \s和\S只匹配ASCII.字符,而不匹配所
    有的Unicode字符。也可以在正则表达式中使用(?a)行内旗标来代表。
  • re.DEBUG:显示编译正则表达式的Debug信息。没有行内旗标。
  • re.I或re.IGNORECASE:使用正则表达式匹配时不区分大小写。对应于正则表达式中的(?i)
    行内旗标。
  • re.L或re.LOCALE:根据当前区域设置使用正则表达式匹配时不区分大小写。该旗标只能
    对bytes模式起作用,对应于正则表达式中的(?L)行内旗标。
  • re.M或re.MULTILINE:多行模式的旗标。当指定该旗标后,“^"能匹配字符串的开头和
    每行的开头(紧跟在每一个换行符的后面):“$ ”能匹配字符串的末尾和每行的末尾(在
    每一个换行符之前)。在默认情况下,“^”只匹配字符串的开头,“$”只匹配字符串的
    结尾,或者匹配到字符串默认的换行符(如果有)之前。对应于正则表达式中的(?m)行内
    旗标。
  • re.S或S.DOTALL:让点(.)能匹配包括换行符在内的所有字符,如果不指定该旗标,则
    点(.)能匹配不包括换行符的所有字符。对应于正则表达式中的(?s)行内旗标。
  • re.U或re.Unicode:该旗标控制\w, \W, \b, \B, \d, \D,\s和\S能匹配所有的Unicode字符。这
    个旗标在Python 3.x中完全是多余的,因为Python 3.x默认就是匹配所有的Unicode字符。
  • re.X或re.VERBOSE:通过该旗标允许分行书写正则表达式,也允许为正则表达式添加注
    释,从而提高正则表达式的可读性。对应于正则表达式中的(?x)对行内旗标。

4. 创建正则表达式

(1)正则表达式所支持的合法字符

字符 解释
x 字符x(x可代表任意合法的字符)
\uhhhh 十六进制值0xhhhh所表示的Unicode字符
\t 制表符(’\u0009’)
\n 新行(换行)符(’\u000A’)
\r 回车符(’\u000D’)
\f 换页符(’\u000C’)
\a 报警(bell)符(’\u0007’)
\e Escape符(’\u001B’)
\cx x对应的控制符。例如,\cM匹配

(2)正则表达式中的特殊字符

特殊字符 说明
$ 匹配一行的结尾。要匹配 $ 字符本身,请使用 \ $
^ 匹配一行的开头。要匹配 ^ 字符本身,请使用 \ ^
( ) 标记子表达式(也就是组)的开始位置和结束位置。要匹配这些字符,请使用\(和\)
[ ] 用于确定中括号表达式的开始位置和结束位置。要匹配这些字符,请使用 \ [ 和 \ ]
{ } 用于标记前面子表达式的出现频度。要匹配这些字符,请使用 \ {和 \ }
* 指定前面子表达式可以出现零次或多次。要匹配 * 字符本身,请使用 \ *
+ 指定前面子表达式可以出现一次或多次。要匹配+字符本身,请使用 \ +
? 指定前面子表达式可以出现零次或一次。要匹配?字符本身,请使用 \ ?
. 匹配除换行符\n之外的任意单个字符。要匹配・字符本身,请使用 \ .
\ 用于转义下一个字符,或指定八进制、十六进制字符。如果需匹配\字符,请使用 \ \
l 指定在两项之间任选一项。如果要匹配I字符本身.请使用\ l

(3)正则表达式所支持的预定义字符

预定义字符 说明
. 默认可匹配除换行符之外的任意字符,在使用re.S或S.DOTALL旗标之后,它还可匹配换行符
\d 匹配0~9的所有数字
\D 匹配非数字
\s 匹配所有的空白字符,包括空格、制表符、回车符、换页符、换行符等
\S 匹配所有的非空白字符
\w 匹配所有的单词字符,包括0〜9的所有数字、26个英文字母和下画线(_)
\W 匹配所有的非单词字符

(4)方括号表达式

方括号表达式 说明
表示枚举 例如[abc],表示a、b、c其中任意一个字符;[gz],表示g、z其中任意一个字符
表示范围 例如[a・f],表示a~f范围内的任意字符;[\u0041-\u0056],表示十六进制字符\u0041到\u0056
范围的字符。范围可以和枚举结合使用,如[a-cx-z],表示a~c、x〜z范围内的任意字符
表示求否:^ 例如[^abc],表示非a、b、c的任意字符:[ ^ a-f ],表示不是a〜f范围内的任意字符

(5)边界匹配符

边界匹配符 说明
^ 行的开头
$ 行的结尾
\b 单词的边界,即只能匹配单词前后的空白
\B 非单词的边界,即只能匹配不在单词前后的空白
\A 只匹配字符串的开头
\Z 只匹配字符串的结尾,仅用于最后的结束符

代码如下:

>>> print (re.fullmatch (r'\u0041\\', 'A\\')) # 匹配 A\
<_sre.SREMatch object; span=(0, 2), match='A\\'>
>>> print(re.fullmatch (r'\u0061\t', 'a\t')) # 匹配 a< 制表符〉
<_sre.SRE_Match object; span=(0, 2), match='a\t'>
>>> print(re.fullmatch (r'\?\ [', '?[')) # // 匹配?[
<_sre.SRE_Match object; span=(0, 2), match='?['>

>>> re.fullmatch (r'c\wt', 'cat') # c\wt 可以匹配 cat、cbt、cct、cOt、c9t 等一批字符串
<_sre.SRE_Match object; span=(O, 3), match='cat'>
>>> re.fullmatch (r'c\wt', 'c9t') # c\wt 可以匹配 cat、cbt、cct、cOt、c9t 等一批字符串
<_sre.SRE_Match object; span=(0, 3), match='c9t'>
# 匹配如000-000-0000形式的电话号码
>>> re.fullmatch(r'\d\d\d-\d\d\d-\d\d\d\d' , '123-456-8888')
<_sre.SRE_Match object; span=(0, 12), match='123-456-8888'>

5. 子表达式(P244-246)

正则表达式还支持圆括号表达式,用于将多个表达式组成一个子表达式,在圆括号中可以使用
或运算符(|)。圆括号表达式也是功能丰富的用法之一。

子表达式(组)支持如下用法。

  • (exp):匹配exp表达式并捕获成一个自动命名的组,后面可通过“\1”引用第一个捕获组
    所匹配的子串,通过“\2”引用第二个捕获组所匹配的子串……依此类推。
  • (?P < name >exp):匹配exp表达式并捕获成命名组,该组的名字为name。后面可通过(?P=name)来引用前面捕获的组。通过此处介绍不难看出,(exp)和(?Pexp)的功能大致相似,
    只是exp捕获的组没有显式指定组名,因此后面使用\1、\2等方式来引用这种组所匹配的
    子串:而(?p < name >exp)捕获的组指定了名称,因此后面可通过这种方式来引用
    命名组所匹配的子串。
  • (?P=name):引用name命名组所匹配的子串。
  • (?:exp):匹配exp表达式并且不捕获。这种组与(exp)的区别就在于它是不捕获的,因此不
    能通过\1、\2等来引用。例如,在交互式解释器中执行如下命令,将会岀现错误,原因是
    (?:95|98|NT|2000)是一个不捕获的组,因此在该正则表达式中不能使用“\1”来引用该组。
  • (?<=exp):括号中的子模式必须出现在匹配内容的左侧,但exp不作为匹配的一部分。
  • (?=exp):括号中的子模式必须出现在匹配内容的右侧,但exp不作为匹配的一部分。
  • (? 实它是(?<=exp)的逆向表达。
  • (?!exp):括号中的子模式必须不出现在匹配内容的右侧,但exp不作为匹配的一部分。其
    实它是(?=exp)的逆向表达。
  • (?#comment):注释组。"?#”后的内容是注释,不影响正则表达式本身。
  • (?aiLmsux):旗标组,用于为整个正则表达式添加行内旗标,可同时指定一个或多个旗标
  • (?imsx-imsx:exp):只对当前组起作用的旗标。该组旗标与前一组旗标的区别是,前一组旗
    标作用于整个正则表达式,而这组旗标只影响组内的子表达式。

6. 贪婪模式与勉强模式

Python正则表达式支持如下几种频度限定。

  • ‘ * ’:限定前面的子表达式可出现0〜N次。例如正则表达式 r’zo*’,能匹配 ‘z’,也能匹配’zoo’、
    'zooo’等。* 等价于{0,}。
  • +:限定前面的子表达式可出现1〜N次。例如正则表达式 r’zo+’,不能匹配 ‘z’,可匹配’zo’、
    ‘zoo’、 ‘zooo’ 等。+ 等价于{1,}。
  • ?:限定前面的子表达式出现0〜1次。例如正则表达式 r’zo?’,能匹配 'z’和 ‘zo’ 两个字符串。?
    等价于(0,1 }。
  • (n,m}: n和m均为非负整数,其中n≤m,限定前面的子表达式出现n~m成次。例如正则表
    达式 r’fo{l,3}d’ 可匹配’fod’、 ‘food’、 ‘foood’ 这三个字符串。
  • {n,}: n是一个非负整数,限定前面的子表达式至少出n次。例如正则表达式 r’fo{2,}d’
    可匹配 ‘food’、 ‘foood’、 ‘fooood’ 等字符串。
  • {,m}: m是一个非负整数,限定前面的子表达式至多出现m次。例如正则表达式 r’fo{.3}d’
    可匹配 ‘fd’ 、‘fod’、‘food’、'foood’这四个字符串。
  • {n}: n是一个非负整数,限定前面的子表达式必须出现n次。例如正则表达式 r’fo{2}d’
    只能匹配 ‘food’ 字符串。

000-000-000,可写成r’\d{3}-\d{3}-\d{4}

七、容器相关类

deque则代表一个双端队列。双端队列的特征是它的两端都可以添加、删除元素,它既可作为
栈(stack)使用,也可作为队列(queue)使用。

1. set和frozenset

set集合有如下两个特征:

  • set不记录元素的添加顺序。
  • 元素不允许重复。

[e for e in dir(set) if not e.startswith(’_’))],查看方法

代码如下:

# 使用花括号构建set集合
c = {
     '白骨精'}
# 添加元素
c.add("孙悟空")
c.add(6)
print("c集合的元素个数为:" , len(c)) # 输出3
# 删除指定元素
c.remove(6)
print("c集合的元素个数为:" , len(c)) # 输出2
# 判断是否包含指定字符串
print("c集合是否包含'孙悟空'字符串:" , ("孙悟空" in c)) # 输出True
c.add("轻量级Java EE企业应用实战")
print("c集合的元素:" , c)
# 使用set()函数(构造器)来创建set集合
books = set()
books.add("轻量级Java EE企业应用实战")
books.add("疯狂Java讲义")
print("books集合的元素:" , books)
# issubset()方法判断是否为子集合
print("books集合是否为c的子集合?", books.issubset(c)) # 输出False
# issubset()方法与<=运算符效果相同
print("books集合是否为c的子集合?", (books <= c)) # 输出False
# issuperset()方法判断是否为父集合
# issubset和issuperset其实就是倒过来判断
print("c集合是否完全包含books集合?", c.issuperset(books)) # 输出False
# issuperset()方法与>=运算符效果相同
print("c集合是否完全包含books集合?", (c >= books)) # 输出False
# 用c集合减去books集合里的元素,不改变c集合本身
result1 = c - books
print(result1)
# difference()方法也是对集合做减法,与用-执行运算的效果完全一样
result2 = c.difference(books)
print(result2)
# 用c集合减去books集合里的元素,改变c集合本身
c.difference_update(books)
print("c集合的元素:" , c)
# 删除c集合里的所有元素
c.clear()
print("c集合的元素:" , c)
# 直接创建包含元素的集合
d = {
     "疯狂Java讲义", '疯狂Python讲义', '疯狂Kotlin讲义'}
print("d集合的元素:" , d)
# 计算两个集合的交集,不改变d集合本身
inter1 = d & books
print(inter1)
# intersection()方法也是获取两个集合的交集,与用&执行运算的效果完全一样
inter2 = d.intersection(books)
print(inter2)
# 计算两个集合的交集,改变d集合本身
d.intersection_update(books)
print("d集合的元素:" , d)
# 将range对象包装成set集合
e = set(range(5))
f = set(range(3, 7))
print("e集合的元素:" , e)
print("f集合的元素:" , f)
# 对两个集合执行异或运算
xor = e ^ f
print('e和f执行xor的结果:', xor)
# 计算两个集合的并集,不改变e集合本身
un = e.union(f)
print('e和f执行并集的结果:', un)
# 计算两个集合的并集,改变e集合本身
e.update(f)
print('e集合的元素:', e)

set集合支持的运算符:

  • <=:相当于调用issubset()方法,判断前面的set集合是否为后面的set集合的子集合。
  • ‘>=’:相当于调用issuperset()方法,判断前面的set集合是否为后面的set集合的父集合。
  • -:相当于调用difference()方法,用前面的set集合减去后面的set集合的元素。
  • &:相当于调用intersection()方法,用于获取两个set集合的交集。
  • ^:计算两个集合异或的结果,就是用两个集合的并集减去交集的元素。
  • 交集运算:intersection()和intersection_update().前者不改变集合本身,而是返回两个集合的交集;后者会通过交集运算改变第一个集合。
  • 并集运算:union()和update(),前者不改变集合本身,而是返回两个集合的并集;后者会通过并集运算改变第一个集合。
  • 减法运算:difference()和difference_update(),前者不改变集合本身,而是返回两个集合做减法的结果;后者改变第一个集合。

frozenset的作用主要有两点。

  • 当集合元素不需要改变时,使用G>zenset代替set更安全。
  • 当某些API需要不可变对象时,必须用Bozenset代替set。比如diet的key必须是不可变
    对象,因此只能用Bozenset;再比如set本身的集合元素必须是不可变的,因此set不能包
    含 set, set 只能包含 frozenset。

2. 双端队列(deque)

在“数据结构"课程中最常讲授的数据结构有栈、队列、双端队列。
栈是一种后进先出(LIFO)的线性表。
队列是一种先进先出(FIFO)的线性表。
双端队列(即此处介绍的deque)代表一种特殊的队列,它可以在两端同时进行插入、删除操作。

deque位于collections包下,在交互式解释器中先导入collections包,然后输入[e for e in
dir(collections.deque) if not e.startswith(’_’)命令来査看deque的全部方法。

  • append和appendleft:在deque的右边或左边添加元素,也就是默认在队列尾添加元素。
  • pop和popleft:在deque的右边或左边弹出元素,也就是默认在队列尾弹出元素。
  • extend和extendleft:在deque的右边或左边添加多个元素,也就是默认在队列尾添加多个元素。

代码如下:

from collections import deque
stack = deque(('Kotlin', 'Python'))
# 元素入栈
stack.append('Erlang')
stack.append('Swift')
print('stack中的元素:' , stack)
# 元素出栈,后添加的元素先出栈
print(stack.pop())
print(stack.pop())
print(stack)

3. python的堆操作(heapq)

heapq.__all __

  • heappush(heap, item):将 item 元素加入堆。
  • heappop(heap):将堆中最小元素弹出。
  • heapify(heap):将堆属性应用到列表上。
  • heapreplace(heap, x):将堆中最小元素弹出,并将元素x入堆。
  • merge(*iterables, key=None, reverse=False):将多个有序的堆合并成一个大的有序堆,然后
    再输出。
  • heappushpop(heap, item):将item入堆,然后弹出并返回堆中最小的元素。
  • nlargest(n, iterable, key=None):返回堆中最大的n个元素。
  • nsmallest(n, iterable, key=None):返回堆中最小的n个元素。

代码如下:

from heapq import *
my_data = list(range(10))
my_data.append(0.5)
# 此时my_data依然是一个list列表
print('my_data的元素:', my_data)
# 对my_data应用堆属性
heapify(my_data) 
print('应用堆之后my_data的元素:', my_data)
heappush(my_data, 7.2)
print('添加7.2之后my_data的元素:', my_data)
# 弹出堆中最小的元素
print(heappop(my_data)) # 0
print(heappop(my_data)) # 0.5
print('弹出两个元素之后my_data的元素:', my_data)
# 弹出最小元素,压入指定元素
print(heapreplace(my_data, 8.1))
print('执行replace之后my_data的元素:', my_data)
print('my_data中最大的3个元素:', nlargest(3, my_data))
print('my_data中最小的4个元素:', nsmallest(4, my_data))

八、collections下的容器支持

1. ChainMap(多个dict链在一起)

ChainMap是一个方便的工具类,它使用链的方式将多个dict “链”在一起,从而允许程序可
直接获取任意一个diet所包含的key对应的value。(有重复的key)

代码如下:

from collections import ChainMap
# 定义3个dict对象
a = {
     'Kotlin': 90, 'Python': 86}
b = {
     'Go': 93, 'Python': 92}
c = {
     'Swift': 89, 'Go': 87}
# 将3个dict对象链在一起,就像变成了一个大的dict
cm = ChainMap(a, b , c)
print(cm)
# 获取Kotlin对应的value
print(cm['Kotlin']) # 90
# 获取Python对应的value
print(cm['Python']) # 86
# 获取Go对应的value
print(cm['Go']) # 93

2. Counter对象

可以自动统计容器中各元素出现的次数。

代码如下:

from collections import Counter
# 创建空的Counter对象
c1 = Counter()
# 以可迭代对象创建Counter对象
c2 = Counter('hannah')
print(c2)
# 以可迭代对象创建Counter对象
c3 = Counter(['Python', 'Swift', 'Swift', 'Python', 'Kotlin', 'Python'])
print(c3)
# 以dict来创建Counter对象
c4 = Counter({
     'red': 4, 'blue': 2})
print(c4)
# 使用关键字参数的语法创建Counter
c5 = Counter(Python=4, Swift=8)
print(c5)
  • elements():该方法返回该Counter所包含的全部元素组成的迭代器。
  • most_common([n]):该方法返回Counter中出现最多的n个元素。
  • subtract([iterable-or-mapping]):该方法计算Counter的减法,其实就是计算减去之后各元素
    出现的次数。

代码如下:

from collections import Counter
# 创建Counter对象
cnt = Counter()
# 访问并不存在的key,将输出该key的次数为0.
print(cnt['Python']) # 0
for word in ['Swift', 'Python', 'Kotlin', 'Kotlin', 'Swift', 'Go']:
    cnt[word] += 1
print(cnt)
# 只访问Counter对象的元素
print(list(cnt.elements()))
# 将字符串(迭代器)转换成Counter
chr_cnt = Counter('abracadabra')
# 获取出现最多的3个字母
print(chr_cnt.most_common(3))  # [('a', 5), ('b', 2), ('r', 2)]
c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
# 用Counter对象执行减法,其实就是减少各元素的出现次数
c.subtract(d)
print(c) # Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
e = Counter({
     'x': 2, 'y': 3, 'z': -4})
# 调用del删除key-value对,会真正删除该key-value对
del e['y']
print(e)
# 访问'w'对应的value,'w'没有出现过,因此返回0
print(e['w']) # 0
# 删除e['w'],删除该key-value对
del e['w']
# 再次访问'w'对应的value,'w'还是没有,因此返回0
print(e['w']) # 0

Counter执行各种运算的含义如下。

  • 加:将两个Counter对象中各key出现的次数相加,且只保留出现次数为正的元素。
  • 减:将两个Counter对象中各key出现的次数相减,且只保留出现次数为正的元素。
  • 交:取两个Counter对象中都出现的key且各key对应的次数的最小数。
  • 并:取两个Counter对象中各key对应的出现次数的最大数。
  • 求正:只保留Counter对象中出现次数为0或正数的key-value对。
  • 求负:只保留Counter对象中出现次数为负数的key-value对,并将岀现次数改为正数。

代码如下:

from collections import Counter
# 创建Counter对象
c = Counter(Python=4, Swift=2, Kotlin=3, Go=-2)
# 统计Counter中所有出现次数的总和
print(sum(c.values())) # 7
# 将Counter转换为list,只保留各key
print(list(c)) # ['Python', 'Swift', 'Kotlin', 'Go']
# 将Counter转换为set,只保留各key
print(set(c)) # {'Go', 'Python', 'Swift', 'Kotlin'}
# 将Counter转换为dict
print(dict(c)) # {'Python': 4, 'Swift': 2, 'Kotlin': 3, 'Go': -2}
# 将Counter转换为list,列表元素都是(元素, 出现次数)组
list_of_pairs = c.items()
print(list_of_pairs) # dict_items([('Python', 4), ('Swift', 2), ('Kotlin', 3), ('Go', -2)])
# 将列表元素为(元素, 出现次数)组的list转换成Counter
c2 = Counter(dict(list_of_pairs))
print(c2) # Counter({'Python': 4, 'Kotlin': 3, 'Swift': 2, 'Go': -2})
# 获取Counter中最少出现的3个元素
print(c.most_common()[:-4:-1]) # [('Go', -2), ('Swift', 2), ('Kotlin', 3)]
# 清空所有key-value对
c.clear()
print(c) # Counter()
c = Counter(a=3, b=1, c=-1)
d = Counter(a=1, b=-2, d=3)
# 对Counter执行加法
print(c + d)  # Counter({'a': 4, 'd': 3})
# 对Counter执行减法
print(c - d)  # Counter({'b': 3, 'a': 2})
Counter({
     'a': 2})
# 对Counter执行交运算
print(c & d) # Counter({'a': 1})
print(c | d) # Counter({'a': 3, 'd': 3, 'b': 1}) 
print(+c) # Counter({'a': 3, 'b': 1})
print(-d) # Counter({'b': 2})

3. defaultdict对象

defaultdict是diet的子类,因此defaultdict也可被当成diet来使用,diet支持的功能,defaultdict
基本都支持。但它与diet最大的区别在于:如果程序试图根据不存在的key来访问diet中对应的
value,则会引发KeyError异常:而defaultdict则可以提供—个default_factory属性,该属性所指定
的函数负责为不存在的key来生成value。

代码如下:

from collections import defaultdict
s = [('Python', 1), ('Swift', 2), ('Python', 3), ('Swift', 4), ('Python', 9)]
# 创建defaultdict,设置由list()函数来生成默认值
d = defaultdict(list)
for k, v in s:
    # 直接访问defaultdict中指定key对应的value即可。
    # 如果该key不存在,defaultdict会自动为该key生成默认值
    d[k].append(v)
print(list(d.items()))

>> [('Python', [1, 3, 9]), ('Swift', [2, 4])]

4. namedtuple工厂函数

namedtuple。是一个工厂函数,使用该函数可以创建一个tuple类的子类,该子类可以为tuple
的每个元素都指定字段名,这样程序就可以根据字段名来访问namedtuple的各元素了。当然,如
果有需要,程序依然可以根据索引来访问namedtuple的各元素。
namedtuple是轻量级的,性能很好,其并不比普通tuple需要更多的内存。

语法格式如下:
namedtuple(typename, field_names, *, verbose=False, rename=False, module=None)

参数说明如下:

  • typename:该参数指定所创建的tuple子类的类名,相当于用户定义了一个新类。
  • field_names:该参数是一个字符串序列,如[‘x’, ‘y’]。此外,field_names也可直接使用单个
    字符串代表所有字段名,多个字段名用空格、逗号隔开,如’xy’或’x,y’,。任何有效的Python
    标识符都可作为字段名(不能以下画线开头)。有效的标识符可由字母、数字、下画线组
    成,但不能以数字、下画线开头,也不能是关键字(如return、global、 pass、raise等)。
  • rename:如果将该参数设为True,那么无效的字段名将会被自动替换为位置名。例如指定
    [‘abc’, ‘def’, ‘ghi’, ‘abc’],它将会被替换为[‘abc’, ‘_1’, ‘ghi’, ‘_3’],这是因为def字段名是关键字,
    而abc字段名重复了。
  • verbose:如果该参数被设为True,那么当该子类被创建之后,该类定义就会被立即打印出来。
  • module:如果设置了该参数,那么该类将位于该模块下,因此该自定义类的__module__属
    性将被设为该参数值。

代码如下:

from collections import namedtuple
# 定义命名元组类:Point
Point = namedtuple('Point', ['x', 'y'])
# 初始化Point对象,即可用位置参数,也可用命名参数
p = Point(11, y=22)
# 像普通元组一样用根据索引访问元素
print(p[0] + p[1]) # 33
# 执行元组解包,按元素的位置解包
a, b = p
print(a, b) # 11, 22
# 根据字段名访问各元素
print(p.x + p.y) # 33
print(p) # Point(x=11, y=22)

my_data = ['East', 'North']
# 创建命名元组对象
p2 = Point._make(my_data)
print(p2) # Point(x='East', y='North')
# 将命名元组对象转换成OrderedDict
print(p2._asdict()) # OrderedDict([('x', 'East'), ('y', 'North')])
# 替换命名元组对象的字段值
p2._replace(y='South')
print(p2) # Point(x='East', y='North')
# 输出p2包含的所有字段
print(p2._fields) # ('x', 'y')
# 再次定义一个命名元组类
Color = namedtuple('Color', 'red green blue')
# 再次定义命名元组类,其字段由Point的字段加上Color的字段
Pixel = namedtuple('Pixel', Point._fields + Color._fields)
# 创建Pixel对象,分别为x、y、red、green、blue字段赋值
pix = Pixel(11, 22, 128, 255, 0)
print(pix) # Pixel(x=11, y=22, red=128, green=255, blue=0)

为命名元组提供了如下方法和属性。

  • _make(iterable):类方法。该方法用于根据序列或可迭代对象创建命名元组对象。
  • _asdict():将当前命名元组对象转换为OrderedDict字典。
  • _replace(**kwargs):替换命名元组中一个或多个字段的值。
  • _source:该属性返回定义该命名元组的源代码。
  • _fields:该属性返回该命名元组中所有字段名组成的元组。

5. OrderedDict对象

OrderedDict也是diet的子类,其最大特征是:它可以“维护”添加key-value对的顺序。简单
来说,就是先添加的key-value对排在前面,后添加的key-value对排在后面。
由于OrderedDict能维护key-value对的添加顺序,因此即使两个OrderedDict中的key-value
对完全相同,但只要它们的顺序不同,程序在判断它们是否相等时也依然会返回false。

代码如下:

from collections import OrderedDict
# 创建OrderedDict对象
dx = OrderedDict(b=5, c=2, a=7)
print(dx) # OrderedDict([('b', 5), ('c', 2), ('a', 7)])
d = OrderedDict()
# 向OrderedDict中添加key-value对
d['Python'] = 89
d['Swift'] = 92
d['Kotlin'] = 97
d['Go'] = 87
# 遍历OrderedDict的key-value对
for k,v in d.items():
    print(k, v)

# 创建普通的dict对象
my_data = {
     'Python': 20, 'Swift':32, 'Kotlin': 43, 'Go': 25}
# 创建基于key排序的OrderedDict
d1 = OrderedDict(sorted(my_data.items(), key=lambda t: t[0]))
# 创建基于value排序的OrderedDict
d2 = OrderedDict(sorted(my_data.items(), key=lambda t: t[1]))
print(d1) # OrderedDict([('Go', 25), ('Kotlin', 43), ('Python', 20), ('Swift', 32)])
print(d2) # OrderedDict([('Python', 20), ('Go', 25), ('Swift', 32), ('Kotlin', 43)])
print(d1 == d2) # False
  • popitem(last=True):默认弹出并返回最右边(最后加入)的key-value对;如果将last参数
    设为False,则弹出并返回最左边(最先加入)的key-value对。
  • move_to_end(key, last=True):默认将指定的key-value对移动到最右边(最后加入);如果
    将last改为False,则将指定的key-value对移动到最左边(最先加入)。

代码如下:

from collections import OrderedDict
d = OrderedDict.fromkeys('abcde')
# 将b对应的key-value对移动到最右边(最后加入)
d.move_to_end('b')
print(d.keys()) # odict_keys(['a', 'c', 'd', 'e', 'b'])
# 将b对应的key-value对移动到最左边(最先加入)
d.move_to_end('b', last=False)
print(d.keys()) # odict_keys(['b', 'a', 'c', 'd', 'e'])
# 弹出并返回最右边(最后加入)的key-value对
print(d.popitem()[0]) # e
# 弹出并返回最左边(最先加入)的key-value对
print(d.popitem(last=False)[0]) # b

九、函数相关模块

1. itertools模块的功能函数(迭代器、排列组合)

[ e for e in dir(itertools) if not e.startswith(’_’)]

迭代器

代码如下:

import itertools as it
# count(10, 3)生成10、13、16……迭代器
for e in it.count(10, 3):
    print(e)
    # 用于跳出无限循环
    if e > 20:
        break
print('---------')
my_counter = 0
# cycle用于对序列生成无限循环的迭代器
for e in it.cycle(['Python', 'Kotlin', 'Swift']):
    print(e)
    # 用于跳出无限循环
    my_counter += 1
    if my_counter > 7:
        break
print('---------')
# repeat用于生成n个元素重复的迭代器
for e in it.repeat('Python', 3):
    print(e)
  • accumulate(p [,func]):默认生成根据序列p元素累加的迭代器,p0, p0+pl, p0+p1+p2,…序列,如果指定了 func函数,则用func函数来计算下一个元素的值。
  • chain(p,q, …):将多个序列里的元素“链”在一起生成新的序列。
  • compress(data, selectors):根据selectors序列的值对data序列的元素进行过滤。如果selector[0]
    为真,则保留data[0];如果selector[l]为真,则保留data[l].依此类推。
  • dropwhile(pred, seq):使用pred函数对seq序列进行过滤,从seq中第一个使用pred函数计算为False的元素开始,保留从该元素到序列结束的全部元素。
  • takewhile(pred, seq):该函数和上一个函数恰好相反。使用pred函数对seq序列进行过滤,
    从seq中第一个使用pred函数计算为False的元素开始,去掉从该元素到序列结束的全部元素。
  • filterfalse(pred, seq):使用pred函数对seq序列进行过滤,保留seq中使用pred计算为True
    的元素。比如 filterfalse(lambda x: x%2, range(10)), 得到 0,2,4,6,8。
  • islice(seq, [start,] stop [, step]):其功能类似于序列的slice方法,实际上就是返回seq[start: stop: step]的结果。
  • starmap(fiinc, seq):使用fimc对seq序列的每个元素进行计算,将计算结果作为新的序列
    元素。当使用fimc计算序列元素时,支持序列解包。比如seq序列的元素长度为3,那么
    func可以是一个接收三个参数的函数,该函数将会根据这三个参数来计算新序列的元素。
  • zip_longest(p, q,…):将p、q等序列中的元素按索引合并成元组,这些元组将作为新序列
    的元素。

代码如下:

import itertools as it
# 默认使用累加的方式计算下一个元素的值
for e in it.accumulate(range(6)):
    print(e, end=', ') # 0, 1, 3, 6, 10, 15
print('\n---------')
# 使用x*y的方式来计算迭代器下一个元素的值
for e in it.accumulate(range(1, 6), lambda x, y: x * y):
    print(e, end=', ') # 1, 2, 6, 24, 120
print('\n---------')
# 将两个序列“链”在一起,生成新的迭代器
for e in it.chain(['a', 'b'], ['Kotlin', 'Swift']):
    print(e, end=', ') # 'a', 'b', 'Kotlin', 'Swift'
print('\n---------')
# 根据第二个序列来筛选第一个序列的元素,
# 由于第二个序列只有中间两个元素为1(True),因此前一个序列只保留中间两个元素
for e in it.compress(['a', 'b', 'Kotlin', 'Swift'], [0, 1, 1, 0]):
    print(e, end=', ') # 只有: 'b', 'Kotlin'
print('\n---------')
# 获取序列中从长度不小于4的元素开始、到结束的所有元素
for e in it.dropwhile(lambda x:len(x)<4, ['a', 'b', 'Kotlin', 'x', 'y']):
    print(e, end=', ') # 只有: 'Kotlin', 'x', 'y'
print('\n---------')
# 去掉序列中从长度不小于4的元素开始、到结束的所有元素
for e in it.takewhile(lambda x:len(x)<4, ['a', 'b', 'Kotlin', 'x', 'y']):
    print(e, end=', ')  # 只有: 'a', 'b'
print('\n---------')
# 只保留序列中从长度不小于4的元素
for e in it.filterfalse(lambda x:len(x)<4, ['a', 'b', 'Kotlin', 'x', 'y']):
    print(e, end=', ') # 只有: 'Kotlin'
print('\n---------')
# 使用pow函数对原序列的元素进行计算,将计算结果作为新序列的元素
for e in it.starmap(pow, [(2,5), (3,2), (10,3)]):
    print(e, end=', ') # 32, 9, 1000
print('\n---------')
# 将'ABCD'、'xy'的元素按索引合并成元组,这些元组作为新序列的元素
# 长度不够的序列元素使用'-'字符代替
for e in it.zip_longest('ABCD', 'xy', fillvalue='-'):
    print(e, end=', ') # ('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-')

>> 0, 1, 3, 6, 10, 15, 
>> ---------
>> 1, 2, 6, 24, 120, 
>> ---------
>> a, b, Kotlin, Swift, 
>> ---------
>> b, Kotlin, 
>> ---------
>> Kotlin, x, y, 
>> ---------
>> a, b, 
>> ---------
>> Kotlin, 
>> ---------
>> 32, 9, 1000, 
>> ---------
>> ('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-'), 

排列组合

  • product(p, q,… [repeat=1]):用序列p、q、…中的元素进行排列组合,就相当于使用嵌套循环组合。
  • permutations(p[, r]):从序列p中取出r个元素组成全排列,将排列得到的元组作为新迭代器的元素。
  • combinations(p,r):从序列p中取出r个元素组成全组合,元素不允许重复,将组合得到的
    元组作为新迭代器的元素。
  • combinations_with_replacement(p, r),从序列p中取出r个元素组成全组合,元素允许重复,
    将组合得到南元垣作为新迭代器的元素。

代码如下:

import itertools as it
# 使用两个序列进行排列组合
for e in it.product('AB', 'CD'):
    print(''.join(e), end=', ') # AC, AD, BC, BD,
print('\n---------')
# 使用一个序列、重复2次进行全排列
for e in it.product('AB', repeat=2):
    print(''.join(e), end=', ') # AA, AB, BA, BB,
print('\n---------')
# 从序列中取2个元素进行排列
for e in it.permutations('ABCD', 2):
    print(''.join(e), end=', ') # AB, AC, AD, BA, BC, BD, CA, CB, CD, DA, DB, DC,
print('\n---------')
# 从序列中取2个元素进行组合、元素不允许重复
for e in it.combinations('ABCD', 2):
    print(''.join(e), end=', ') # AB, AC, AD, BC, BD, CD,
print('\n---------')
# 从序列中取2个元素进行组合、元素允许重复
for e in it.combinations_with_replacement('ABCD', 2):
    print(''.join(e), end=', ') # AA, AB, AC, AD, BB, BC, BD, CC, CD, DD,

2. functools模块的功能函数(装饰器)

[ e for e in dir(functools) if not e.startswith(’_’)]

  • functools.cmp_to_key(func):将老式的比较函数(func)转换为关键字函数(key function)。
    在Python3 大小、排序都是基于关键字函数的,Python 3不支持老式的比较函数。
  • @functools.lru_cache(maxsize= 128, typed=False):该函数装饰器使用 LRU (最近最少使用)缓存算法来缓存相对耗时的函数结果,避免传入相同的参数重复计算。同时,缓存并不会无限增长,不用的缓存会被释放。其中maxsize参数用于设置缓存占用的最大字节数,typed参数用于设置将不同类型的缓存结果分开存放。
  • @functools.total_ordering:这个类装饰器(作用类似于函数装饰器,只是它用于修饰类)
    用于为类自动生成比较方法。通常来说,开发者只要提供_ lt _ ()、_ le _ ()、_ gt _ ()、_ ge _ ()其中之一(最好能提供__eq__ ()方法),@functools.total_ordering装饰器就会为该类生成剩下的比较方法。
  • functools.partial(func, *args, **keywords):该函数用于为func函数南部分参数指定参数值,从而得到一个转换后的函数,程序以后调用转换后的函数时,就可以少传入那些巳指定值的参数。
  • fimctoo!s.partialmethod(func, *args, **keywords):该函数与上一个函数的含义完全相同,只不过该函数用于为类中的方法设置参数值。
  • functools.reduce(function, iterable[, initializer]):将初始值(默认为 0,可由 initializer 参数指定)、迭代器的当前元素传入function函数,将计算岀来的函数结果作为下一次计算的初
    始值、迭代器的下一个元素再次调用function函数……依此类推,直到迭代器的最后一个元素。
  • @functools.singledispatch:该函数装饰器用于实现函数对多个类型进行重载。比如同样的函数名称,为不同的参数类型提供不同的功能实现。该函数的本质就是根据参数类型的变换,将函数转向调用不同的函数。
  • functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):对wrapper函数进行包装,使之看上玉就像wrapped (被包装)函数。
  • @functools.wraps( wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):该函数装饰器用于修饰包装函数,籟包装函数看上去就像wrapped函数。

代码如下:

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
print('----------------')
class User:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'User[name=%s' % self.name
# 定义一个老式的大小比较函数,User的name越长,该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)
print('----------------')
@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))
print('----------------')
# int函数默认将10进制的字符串转换为整数
print(int('12345'))
# 为int函数的base参数指定参数值
basetwo = partial(int, base=2)
basetwo.__doc__ = '将二进制的字符串转换成整数'
# 相当于执行base为2的int()函数
print(basetwo('10010'))
print(int('10010', 2))

partialmethod
代码如下:

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参数指定为True
    set_alive = partialmethod(set_state, True)
    # 指定set_dead()方法就是将set_state()方法的state参数指定为False
    set_dead = partialmethod(set_state, False)
c = Cell()
print(c.alive)
# 相当于调用c.set_state(True)
c.set_alive()
print(c.alive)
# 相当于调用c.set_state(False)
c.set_dead()
print(c.alive)

total_ordering
代码如下:

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):
        return hasattr(other, "name")
    def __eq__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        # 根据name判断是否相等(都转成小写比较、忽略大小写)
        return self.name.lower()  == other.name.lower()
    def __lt__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        # 根据name判断是否相等(都转成小写比较、忽略大小写)
        return self.name.lower() < other.name.lower()
# 打印被装饰之后的User类中的__gt__方法
print(User.__gt__)

singledispatch
代码如下:

from functools import *
@singledispatch
def test(arg, verbose):
    if verbose:
        print("默认参数为:", end=" ")
    print(arg)
# 限制test函数第一个参数为int型的函数版本
@test.register(int)
def _(argu, verbose):
    if verbose:
        print("整型参数为:", end=" ")
    print(argu)
test('Python', True)  # ①
# 调用第一个参数为int型的版本
test(20, True)  # ②
# 限制test函数第一个参数为list型的函数版本
@test.register(list)
def _(argb, verbose=False):
    if verbose:
        print("列表中所有元素为:")
    for i, elem in enumerate(argb):
        print(i, elem, end=" ")
test([20, 10, 16, 30, 14], True) # ③
print("\n---------------") 
# 定义一个函数,不使用函数装饰器修饰
def nothing(arg, verbose=False):
    print("~~None参数~~")
# 当test函数第一个参数为None类型时,转向为调用nothing函数
test.register(type(None), nothing)
test(None, True) # ④
print("\n---------------") 

from decimal import Decimal
# 限制test函数第一个参数为float或Decimal型的函数版本
@test.register(float)
@test.register(Decimal)
def test_num(arg, verbose=False):
    if verbose:
        print("参数的一半为:", end=" ")
    print(arg / 2)
# test.dispatch(类型)即可获取它转向的函数
# 当test()函数第一个参数为float时将转向到调用test_num
print(test_num is test.dispatch(float)) # True
# 当test()函数第一个参数为Decimal时将转向到调用test_num
print(test_num is test.dispatch(Decimal)) # True
# 直接调用test并不等于test_num
print(test_num is test) # False

# 获取test函数所绑定的全部类型
print(test.registry.keys())
# 获取test函数为int类型绑定的函数
print(test.registry[int])

wraps
代码如下:

from functools import wraps

def fk_decorator(f):
    # 让wrapper函数看上去就像f函数
    @wraps(f)
    def wrapper(*args, **kwds):
        print('调用被装饰函数')
        return f(*args, **kwds)
    return wrapper
@fk_decorator
def test():
    """test函数的说明信息"""
    print('执行test函数')
test()
#print(test.__name__)
#print(test.__doc__)

你可能感兴趣的:(python,python)