一、sys模块

sys模块代表了Python解释器,主要用于获取和Python解释器相关的信息。

>>> import sys
>>> [e for e in dir(sys) if not e.startswith('_')]
['api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', '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', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'set_coroutine_wrapper', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions', 'winver']

sys模块的参考页面为https://docs.python.org/3/library/sys.html

sys.argv:获取运行Python程序的命令行参数。其中sys.argv[0]通常就是指该Python程序,sys.argv[1]代表为Python程序提供的第一个参数,以此类推。

sys.byteorder:显示本地字节序的指示符。如果本地字节序是大端模式,则该属性返回big;否则返回little。

sys.copyright:该属性返回与Python解释器有关的版权信息。

sys.executable:该属性返回Python解释器在磁盘上的存储路径。

sys.exit():通过印发SystemExit异常来退出程序。将其放在try块中不能阻止finally块的执行。

sys.flags:该只读属性返回运行Python命令时指定的旗标。

sys.getfilesystemencoding():返回在当前系统中保存文件所用的字符集。

sys.getrefcount(object):返回指定对象的引用计数。

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解释器的主版本号。

可以通过sys.path属性来动态添加Python模块的加载路径。

二、os模块

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

需要时可参考https://docs.python.org/3/library/os.html

os.name:返回导入依赖模块的操作系统名称,通常可返回'posix'、'nt'、'java'等值其中之一。

os.environ:返回在当前系统上所有环境变量组成的字典。

os.fsencode(filename):该函数对类路径(path-like)的文件名进行编码。

os.fsdecode(filenmae):该函数对类路径(path-like)的文件名进行解码。

os.PathLike:这是一个类,代表一个类路径(path-like)对象。

os.getenv(key, default=None):获取指定环境变量的值。

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

os.getpid():获取当前进程ID。

os.getppid():获取当前进程的父进程ID。

os.putenv(key, value):该函数用于设置环境变量。

os.cpu_count():返回当前系统的CPU数量。

os.sep:返回路径分隔符。

os.pathsep:返回当前系统上多条路径之间的分隔符。一般在windows系统上多条路径之间的分隔符是英文分号(;);在UNIX及类UNIX系统(如Linux、Mac OSX)上多条路径之间的分隔符是英文冒号(:)

os.llinesep:返回当前系统的换行符。一般在windows系统行换行符是“\r\n”;在UNIX系统上换行符是“\n”;在Mac OS X系统上换行符是“\r”。

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

os模块的进程管理函数:

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信号发送到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模块

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

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

参考文档位置:https://docs.python.org/3/library/random.html

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是Python关键字,所以简写成lambd)为1除以期望平均值。如果lambd是正值,则返回的随机数是从0到正无穷大;如果lambd为负值,则返回的随机数是从负无穷大到0.

关于生成伪随机浮点数的函数,Python还提供了random.triangular(low, high, mode)、random.gauss(mu, sigma)等,它们用于生成呈对称分布、高斯分布的随机数。

import random
​
​
# 生成范围为0.0≤x≤1.0的伪随机浮点数
print(random.random())
# 生成范围为2.5≤x≤10.0的伪随机浮点数
print(random.uniform(2.5, 10.0))
# 生成呈指数分布的伪随机浮点数
print(random.expovariate(1 / 5))
# 生成从0到100的随机偶数
print(random.randrange(0, 101, 2))
# 随机抽取一个元素
print(random.choice(['Python', 'Swift', 'Kotlin']))
book_list = ['Python', 'Swift', 'Kotlin']
# 对列表元素进行随机排列
random.shuffle(book_list)
print(book_list)
# 随机抽取4个独立的元素
print(random.sample([10, 20, 30, 40, 50], k=4))
import random
import collections
​
​
# 指定随机抽取6个元素,各元素被抽取的权重(概率)不同
print(random.choices(['Python', 'Swift', 'Kotlin'], [5, 5, 1], k=6))
# 下面模拟从52张扑克牌中抽取20张
# 在被抽到的20张牌中,牌面为10(包括J、Q、K)的牌占多大比例
# 生成一个16个tens(代表10)和36个low_cards(代表其他牌)的集合
deck = collections.Counter(tens=16, low_cards=36)
# 从52张牌中随机抽取20张
seen = random.sample(list(deck.elements()), k=20)
# 统计tens元素有多少个,再除以20
print(seen.count('tens') / 20)

四、time

time模块主要包含各种提供日期、时间功能的类和函数。

>>> import time
>>> [e for e in dir(time) if not e.startswith('_')]
['altzone', 'asctime', 'clock', '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等,范围为0~23
tm_min 如2,3等,范围为0~59
tm_sec 如2,3等,范围为0~59
tm_wday 周一为9,范围为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)很清晰的代表时间,也可以用一个包含9个元素的元组来代表时间,该元素的9个元素和struct_time对象中9个属性的含义是一一对应的。如:(2018, 5, 2, 8, 0, 30, 3, 1, 0)

time模块的常用功能函数:

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(sesc):暂停secs秒,什么都不干。

time.strftime(format[, t]):将时间元组或struct_time对象格式化为指定格式的时间字符串。如果不指定参数t,则默认转换当前时间。

time.strptime(string[, format]):将字符串格式的时间解析成struct_time对象。

time.time():返回从1970年1月1日0点整到现在过了多少秒。

time.timezone:返回本地时区的时间偏移,以秒为单位。

time.tzname:返回本地时区的名字。

import time
​
​
# 将当前时间转换为时间字符串
print(time.asctime())
# 将指定时间转换为时间字符串,时间元组的后面3个元素没有设置
print(time.asctime((2018,2, 4, 11, 8, 23, 0, 0, 0)))
# 将以秒数代表的时间转换为时间字符串
print(time.ctime(30))
# 将一秒数代表的时间转换为struct_time对象
print(time.gmtime(30))
# 将当前时间转换为struct_time对象
print(time.gmtime())
# 将以秒数代表的时间转换为代表当前时间的struct_time对象
print(time.localtime(30))
# 将元组格式的时间转换为以秒数代表的时间
print(time.mktime((2018, 2, 4, 11, 8, 23, 0, 0, 0)))
# 返回性能计数器的值
print(time.perf_counter())
# 返回当前进程使用CPU的时间
print(time.process_time())
# time.sleep(10)
# 将当前时间转换为指定格式的字符串
print(time.strftime('%Y-%m-%d %H:%M:%S'))
st = '2018年3月20日'
# 将指定时间字符串恢复成struct_time对象
print(time.strptime(st, '%Y年%m月%d日'))
# 返回从1970年1月1日0点整到现在过去了多少秒
print(time.time())
# 返回本地时区的时间偏移,以秒为单位
print(time.timezone)    # 在中国东八区输出-28800

执行结果:

Fri Aug  9 11:47:38 2019
Mon Feb  4 11:08:23 2018
Thu Jan  1 08:00:30 1970
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=30, tm_wday=3, tm_yday=1, tm_isdst=0)
time.struct_time(tm_year=2019, tm_mon=8, tm_mday=9, tm_hour=3, tm_min=47, tm_sec=38, tm_wday=4, tm_yday=221, tm_isdst=0)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=0, tm_sec=30, tm_wday=3, tm_yday=1, tm_isdst=0)
1517713703.0
0.05234362
0.09375
2019-08-09 11:47:38
time.struct_time(tm_year=2018, tm_mon=3, tm_mday=20, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=79, tm_isdst=-1)
1565322458.9641535
-28800

time模块中的strftime()和strptime()两个函数互为逆函数,其中strftime()用于将struct_time对象或时间元组转换为时间字符串;而strptime()函数用于将时间字符串转换为struct_time对象。这两个函数都设计编写格式模板,指令表如下:

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

五、JSON支持

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

JSON的全称是JavaScript Object Notation,即JavaScript对象符号,是一种轻量级的数据交换格式。

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

● 由key-value对组成的数据结构。这种数据结构在不同的语言中有不同的实现。例如,在JavaScript中是一个对象;在Python中是一种dict对象;在C语言中是一个struct;在其他语言中,则可能是record、dictionary、hash table等。

● 有序集合。这种数据结构在Python中对应于列表;在其他语言中,可能对应于list、vector、数组和序列等。

在创建对象object时,总以“{”开始,以“}"结束,对象的每个属性名和属性值之间以英文冒号(:)隔开,多个属性定义之间以英文逗号(,)隔开。语法格式如下:

object =

{

propertyName1 : propertyValue1,

propertyName2 : propertyValue2,

...

}

必须当后面还有属性定义时才需要有逗号(,)。

使用JSON语法创建数组总是以英文方括号([)开始,然后依次放入数组元素,元素与元素之间以引文逗号(,)隔开,最后一个数组元素后面不需要英文逗号,但以英文反方括号(})结束。语法格式:

arr = [value1, value2, ...]

json模块提供了对JSON的支持,它既包含了将JSON字符串恢复成Python对象的函数,也提供了将Python对象转换成JSON字符串的函数。

JSON类型转换Python类型的对应关系:

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

Python类型转换JSON类型的对应关系:

Python类型 JSON类型
字典(dict) 对象(object)
列表(list)和元组(tuple) 数组(array)
字符串(str) 字符串(string)
整型、浮点型,以及整型、浮点型派生的枚举(float,int-&float-derived Enums) 数值型(number)
True true
False false
None null
>>> json.__all__
['dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder']

json模块中常用的函数和类的功能:

● json.dump(obj, fg, *, 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流中,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.load(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.laods(s, * , encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):将JSON字符串s恢复成JSON对象。

import json
​
​
# 将Python对象转换为JSON字符串(元组会被当成数组)
s = json.dumps(['yeeku', {'favorite': ('coding', None, 'game', 25)}])
print(s)
# 将简单的Python字符串转换为JSON字符串
s2 = json.dumps("\"foo\bar")
print(s2)
# 将简单的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)
# 将python列表转换为JSON字符串
# 并制定JSON分隔符:在逗号和冒号之后没有空格(默认有空格)
s5 = json.dumps([1, 2, 3, {'x': 5, 'y': 7}], separators=(',', ':'))
print(s5)
# 指定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)
f = open('a.json', 'w')
# 使用dump()函数将转换得到的JSON字符串输出到文件中
json.dump(['Kotlin', {'Python': 'excellent'}], f)
import json
​
​
# 将JSON字符串恢复成Python列表
result1 = json.loads('["yeeku", {"favorite": ["coding", null, "game", 25]}]')
print(result1)
# 将JSON字符串恢复成Python列表
result2 = json.loads('"\\"foo\\"bar"')
print(result2)
​
​
# 定义一个自定义的转换函数
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)
f = open('a.json')
# 从文件流恢复JSON列表
result4 = json.load(f)
print(result4)

Python支持很多JSON所不支持的类型,比如复数、矩阵等,如果直接使用dumps()或dump()函数进行转换,程序肯定会出问题。此时就需要开发者对JSONEncoder类进行扩展,通过这种扩展完成从Python特殊类型到JSON类型的转换。

import json
​
​
# 定义JSONEncoder的子类
class ComplexEncoder(json.JSONEncoder):def default(self, obj):# 如果要转换的对象是复数类型,程序负责处理if isinstance(obj, complex):return {"__complex__": 'true', 'real': obj.real, 'imag': obj.imag}# 对于其他类型,还使用JSONEncode默认处理return json.JSONEncoder.default(self, obj)
​
​
s1 = json.dumps(2 + 1j, cls=ComplexEncoder)
print(s1)
s2 = ComplexEncoder().encode(2 + 1j)
print(s2)

一旦扩展了JSONEncode的子类之后,程序有两种方式来使用自定义的子类。

● 在dumps()或dump()函数中通过cls属性指定使用JSONEncoder的自定义子类。

● 直接使用JSONEncode的自定义子类的encode()方法来执行转换。

六、正则表达式

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

>>> import re
>>> re.__all__
['match', 'fullmatch', 'search', 'sub', 'subn', 'split', 'findall', 'finditer', 'compile', 'purge', 'template', 'escape', 'error', 'Pattern', 'Match', 'A', 'I', 'L', 'M', 'S', 'X', 'U', 'ASCII', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL', 'VERBOSE', 'UNICODE']

常用函数:

● re.compile(pattern, flags=0):该函数用于将正则表达式字符串编译成_sre.SRE_Pattern对象,该对象代表了正则表达式编译之后在内存中的对象,它可以缓存并复用正则表达式字符串。如果程序需要多次使用同一个正则表达式字符串,则可考虑先编译它。

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

● re.search(pattern, string, flags=0):扫描整个字符串,并返回字符串中第一处匹配pattern的匹配对象。该函数也返回_sre.SRE_Match对象。

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

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())
​

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

● re.finditer(pattern, string,flags=0):扫描整个字符串,并返回字符串中所有匹配pattern的子串组成的迭代器,迭代器的元素是_sre.SRE_Match对象。

findall()、finditer()和search()函数的区别:search()只返回字符串中第一处匹配pattern的子串,而findall()和finditer()则返回字符串中所有匹配pattern的子串。

● re.fullmatch(pattern, string, flags=0):该函数要求整个字符串匹配pattern,如果匹配则返回包含匹配信息的_sre.SRE_Match对象,否则返回None。

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

● re.split(pattern, string, maxsplit=0, flags=0):使用pattern对string进行分割,该函数返回分割得到的多个子串组成的列表。其中maxsplit参数控制最多分割多少次。

● re.purge():清楚正则表达式缓存。

● re.escape(pattern):对模式中除ASCII字符、数值、下划线(_)之外的其他字符进行转义。

import re
​
​
# 使用逗号对字符串进行分割
print(re.split(', ', 'fkit, fkjava, crazyit'))
# 输出:['fkit', 'fkjava', 'crazyit']
# 指定只分割一次,被切分成两个子串
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.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\?

search()、match()、fullmatch()、findall()、finditer()方法都可以额外指定pos和endpos两个参数,用于指定只处理目标字符串从pos开始到endpos结束之间的子串。

_sre.SRE_Match对象包含了如下方法和属性:

● match.group([group1,...]):获取该匹配对象中指定组所匹配的字符串。

● match.__getitem__(g):这是match.group(g)的简化写法。由于match对象提供了__getitem__()方法,因此程序可使用match[g]来代替match.group(g)。

● match.groups(default=None):返回match对象中所有组所匹配的字符串组成的元组。

● mtach.groupdict(default=None):返回match对象中所有组所匹配的字符串组成的字典。

● match.start([group]):获取该匹配对象中指定组所匹配的字符串的开始位置。

● match.end([group]):获取该匹配对象中指定组所匹配的字符串的结束位置。

● match.span([group]):获取该匹配对象中指定组所匹配的字符串的开始位置和结束位置,该方法相当于同时返回start()和end()方法的返回值。

● match.pos:该属性返回传给正则表达式对象的search()、match()等方法的pos参数。

● match.endpos:该属性返回传给正则表达式对象的search()、match()等方法的endpos参数。

● match.lastindex:该属性返回最后一个匹配的捕获组的整数索引。如果没有组匹配,该属性返回None。

● match.lastgroup:该属性返回最后一个匹配的捕获组的名字;如果改组没有名字或根本没有组匹配,该属性返回None。

● match.re:该属性返回执行正则表达式匹配时所用的正则表达式。

● match.string:该属性返回执行正则表达式匹配时所用的字符串。

旗标:

● 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:根据当前区域设置使用正则表达式匹配时不区分大小写。该旗标只能对byte模式起作用,对应于正则表达式中的(?L)行内旗标。

● re.M或re.MULTILINE:多行模式的旗标。当指定该旗标后,"^"能匹配字符串的开头和每行的开头(紧跟字啊每一个换行符的后面);“$”能匹配字符串的末尾和每行的末尾(在每一个换行符之前)。在默认情况下,"^"只能匹配字符串的开头,"$"只能匹配字符串的结尾,或者匹配到字符串默认的换行符(如果有)之前。对应于正则表达式中的(?m)行内旗标。

● re.S或re.DOTALL:让点(.)能匹配包括换行符在内的所有字符,如果不指定该旗标,则点(.)能匹配不包括换行符的所有字符。对应于正则表达式中的(?s)行内旗标。

● re.U或re.Unicode:该旗标控制\w, \W, \b, \B, \d, \D, \s和\S能匹配所有的Unicode字符。这个旗标在python3.x中完全是多余的,因为python3.x默认就是匹配所有Unicode字符。

● re.X或re.VERBOSE:通过该旗标允许分行书写正则表达式,也允许为正则表达式添加注释,从而提高正则表达式的可读性。对应于正则表达式中的(?x)行内旗标。

表10.5 正则表达式所支持的合法字符

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

表10.6 正则表达式中的特殊字符

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

表10.7 正则表达式所支持的预定义字符

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

表10.8 方括号表达式

方括号表达式 说明
表示枚举 例如[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范围内的任意字符

表10.9 边界匹配符

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

正则表达式的子表达式(组)支持如下用法:

● (exp):匹配exp表达式并捕获成一个自动命名的组,后面可通过“\1”引用第一个捕获组所匹配的子串,以此类推。

● (?P<name>exp):匹配exp表达式并捕获成命名组,该组的名字为name。后面可以通过(?P=name)来引用前面捕获的组。

● (?P=name):引用name命名组所匹配的子串。

● (?:exp):匹配exp表达式并且不捕获。这种组与(exp)的区别在于它不是捕获组的,因此不能通过\1、\2等来引用。

● (?<=exp):括号中的子模式必须出现在匹配内容的左侧,但exp不作为匹配的一部分。

● (?=exp):括号中的字母是必须出现字啊匹配内容的右侧,但exp不作为匹配的一部分。

● (?<!exp):括号中的子模式必须不出现在匹配内容的左侧,但exp不作为匹配的一部分。其实它是(?<=exp)的逆向表达。

● (?!exp):括号中的子模式必须不出现在匹配内容的右侧,但exp不作为匹配的一部分。其实他是(?=exp)的逆向表达。

● (?#comment):注释组。"?#"后的内容是注释,不影响正则表达式本身。

● (?aiLmsux): 旗标组,用于为整个正则表达式添加行内旗标,可同时指定一个或多个旗标。

● (?imsx-imsx:exp):只对当前组起作用的旗标。

在默认情况下,正则表达式的频度限定是贪婪模式的。会尽可能多地匹配字符。只要在频度限定之后添加一个英文问号,贪婪模式就变成了勉强模式,会尽可能少地匹配字符。

七、容器相关类

set集合类似于一个罐子,把一个对象添加到set集合时,set集合无法记住添加这个元素的顺序,所以set里的元素不能重复(否则系统无法准确识别这个元素)

list容器可以记住每次添加元素的顺序,程序可以通过索引来存取元素,list容器的元素允许重复。

dict容器也像一个罐子,只是它里面的每项数据都由key-value对组成,程序可以通过key来存取value。

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

set集合有两个特征:

● set不记录元素的添加顺序

● 元素不允许重复

set集合是可变容器,程序可以改变容器中的元素。与set对应的还有frozenset集合,是set的不可变版本。

>>> [e for e in dir(set) if not e.startswith('_')]
['add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

其中remove()和discard()方法都用于删除集合中的元素,区别在于,如果集合中不包含被删除元素,remove()方法抛出KeyError异常,discard()方法什么都不做。

# 使用花括号构建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
# 使用issuperset()方法判断是否为父集合
print("c集合是否完全包含books集合?", c.issuperset(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()方法

● >=:相当于issuperset()方法

● -:相当于difference()方法

● &:相当于intersection()方法

● ^:计算两个集合异或的结果,就是用两个集合的并集减去交集的元素。

因为集合是可变的,因此集合运算方法都有两个版本:

● 交集运算:intersection()和intersection_update(),前者不改变集合本身。

● 并集运算:union()和update()。

● 减法运算:difference()和difference_update()

frozenset是set的不可变版本,因此set集合中所有能改变集合本身的方法(如add、remove、discard、xxx_update等)frozenset都不支持;set集合中不改变集合本身的方法,forzenset都支持。

[e for e in dir(frozenset) if not e.startswith('_')]
['copy', 'difference', 'intersection', 'isdisjoint', 'issubset', 'issuperset', 'symmetric_difference', 'union']

frozenset有两个用途:

● 当集合元素不需要改变时,使用frozenset代替set更安全。

● 当某些API需要不可变对象时,必须用frozenset代替set。

栈是一种特殊的线性表,它只允许在一端进行插入、删除操作,这一端被成为栈顶(top),另一端则被称为栈底(bottom)。从栈顶插入一个元素被称为进栈,将一个元素插入栈顶被称为“压入栈”,对应的应为说法为push。从栈顶删除一个元素被称为出栈,将一个元素从栈顶删除被称为“弹出栈”,对应的英文说明为pop.

栈是一种后进先出(LIFO)的线性表。

队列也是一种特殊的线性表,它只与允许在表的前端(front)进行删除操作,在表的后端(rear)进行插入操作。进行插入操作的端被称为队尾,进行删除操作的端被称为队头。队列是一种先进先出(FIFO)的线性表。

双端队列代表一种特殊的队列,它可以在两端同时进行插入、删除操作

对于一个双端队列来说,它可以从两端分别插入、删除操作,如果程序将所有的插入、删除操作都固定在一端进行,那么这个双端队列就变成了栈;如果固定在一端只添加元素,在另一端只删除元素,那么它就变成了队列。因此,deque即可以被当成队列使用,也可以被当成栈使用。

deque位于collections包下。

>>> from collections import deque
>>> [e for e in dir(deque) if not e.startswith('_')]
['append', 'appendleft', 'clear', 'copy', 'count', 'extend', 'extendleft', 'index', 'insert', 'maxlen', 'pop', 'popleft', 'remove', 'reverse', 'rotate']

deque的左边(left)就相当于它的队列头(front),右边(right)就相当于它的队列尾(rear)

● append和appendleft:在deque的右边或左边添加元素,也就是默认在队列尾添加元素。

● pop和popleft:在deque的右边或左边弹出元素,也就是默认在队列尾弹出元素。

● extend和extendleft:在deque的右边或左边添加多个元素,也就是默认在队列尾添加多个元素。

● clear()方法用于清空队列;insert()方法是线性表方法,用于在指定位置插入元素。

假如程序把deque当成栈使用,则意味着只在一端添加,删除元素,因此调用append和pop方法即可。

假如程序把deque当成队列使用,则意味着一端只用来添加元素,另一端只用来删除元素,因此调用append、popleft方法即可。

deque还有一个ratate()方法,该方法的作用是将队列的队尾元素移动到队头,使之首尾相连。

Python提供了关于堆的操作:

假设有n个数据元素的序列k0,k1,......,kn-1,当且仅当满足如下关系时,可以将这组数据成为小顶堆(小根堆):

或者满足如下关系时,可以将这组数据成为大顶堆(大根堆):

Python并没有提供“堆”这种数据类型,它是直接把列表当成堆处理的。Python提供的heapq包中有一些函数,当程序用这些函数来操作列表时,该列表就会表现出“堆”的行为。

>>> import heapq
>>> heapq.__all__
['heappush', 'heappop', 'heapify', 'heapreplace', 'merge', 'nlargest', 'nsmallest', 'heappushpop']

● 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 heapify, heappush
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)

Python的heapq包提供的函数,其实就是提供对排序算法中“堆排序”的支持。

当程序要获取列表中最大的n个元素,或者最小的n个元素时,使用堆能缓存列表的排序结果,因此具有较好的性能。

八、collections下的容器支持

(一)ChainMap对象

ChainMap使用链的方式将多个dict“链”在一起,从而允许程序可直接获取任意一个dict所包含的key对应的value。

ChainMap相当于把多个dict合并成一个大的dict,但实际上底层并未真正合并这些dict,因此程序无须调用多个update()方法将多个dict进行合并。

ChainMap允许有重复的key,排在链前面的dict中的key具有更高的优先级。

from collections import ChainMap
​
​
# 定义三个dict对象
a = {'Kotlin': 90, 'Python': 86}
b = {'Go': 93, 'Python': 92}
c = {'Swift': 89, 'Go': 87}
# 将三个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

(二)Counter对象

自动统计容器中各元素出现的次数。Counter的本质是一个特殊的dict,只不过它的key都是其所包含的元素,而它的value则记录了该key出现的次数。

程序可通过任何可迭代对象参数来创建Counter对象,此时Counter将会自动统计各元素出现的次数,并以元素为Key,出现次数为value来构建Counter对象;程序也能以dict为参数来构建Counter对象;还能通过关键字参数来构建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)

Counter继承了dict类,因此它完全可以调用dict所支持的方法。此外,Counter还提供了如下三个常用方法:

● elements():该方法返回该Counter所包含的全部元素组成的迭代器。

● most_common([n]):该方法返回Counter中出现最多的n个元素。

● substract([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')
# 获取出现最多的三个字母
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))
# 将Counter转换为dict
print(dict(c))
# 将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中最少出现的三个元素
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})

(三)defaultdict对象

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

from collections import defaultdict
​
​
my_dict = {}
# 使用int作为defaultdict的default_factory
# 当key不存在时,将会返回int函数的返回值
my_defaultdict = defaultdict(int)
print(my_defaultdict['a'])    # 0
print(my_dict['a'])    # KeyError异常

dict的setdefault()方法,用于获取指定key对应的value,如果该key不存在,setdefault()方法就会先为该key设置一个默认的value。

(四)namedtuple工厂函数

namedtuple()是一个工厂函数,使用该函数可以创建一个tuple类的子类,该子类可以为tuple的每个元素都指定字段名,这样程序就可以根据字段名来访问namedtuple的各元素了。

语法格式:

namedtuple(typename, field_names, *, verbose-False, rename=False, module=None)

参数说明:

● typename:该参数指定所创建的tuple子类的类名,相当于用于定义了一个新类。

● field_names:该参数是一个字符串序列,如['x', 'y']。field_names也可直接使用单个字符串代表所有字段名,多个字段名勇空格、逗号隔开,如'x y'或'x,y'。任何有效的python标识符都可以作为字段名(不能以下划线开头)。也不能是关键字。

● 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)

命名元组还提供了如下方法和属性:

● _make(iterable):类方法。该方法用于根据序列和可迭代对象创建命名元组对象。

● _asdict():将当前命名元组对象转换为OrderedDict字典。

● _replace(**kwargs):替换命名元组中一个或多个字段的值。

● _source:该属性返回定义该命名元组的源代码。

● _fields:该属性返回该命名元组中所有字段名组成的元组。

my_data = ['East', 'North']
# 创建命名元组对象
p2 = Point._make(my_data)
print(p2)    # Point(x='East', y='North')
# 将命名元组对象转换成Orderdict对象
print(p2._asdict())    # OrderedDict([('x', 'East'), ('y', 'North')])
# 替换命名元组对象的字段值
p2._replace(y='South')
print(p2)    # Point(x='East', y='South')
# 输出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, grenn=255, blue=0)

(五) OrderedDict对象

OrderedDict是dict的子类,其最大特征是:可以维护添加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排序的OrderDict
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

OrderedDict中特有的方法:

● 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

九、函数相关模块

(一) 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']

三个生成无限迭代器的函数。

● count(start, [step]):生成start, start+step, start+2*step,……的迭代器,其中step默认为1。

● cycle(p):对序列p生成无线循环p0, p1, ……,p0, p1, ……的迭代器。

● repeat(elem [,n]):生成无线个elem元素重复的迭代器,如果指定了参数n,则只生成n个elem元素。

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 += 1if my_counter > 7:break
print('----------------------------')
# repeat用于生成n个元素重复的迭代器
for e in it.repeat('Python', 3):print(e)

其他常用的迭代器函数:

● accumulate(p[,func]):默认生成根据序列p元素累加的迭代器,p0,p0+p1,p0+p1+p2,……序列,如果指定了func函数,则用func函数来计算下一个元素的值。

● chain(p,q,……):将多个序列里的元素“链”在一起生成新的序列。

● compress(data, selectors):根据selectors序列的值对data序列的元素进行过滤。如果selector[0]为真,则保留data[0];如果selector[1]为真,则保留data[1]……以此类推。

● dropwhile(pred, seq):使用pred函数对seq序列进行过滤,从seq中第一个使用pred函数计算为False的元素开始,保留从该元素到序列结束的全部元素。

● takewhile(pred, seq):该函数和dropwhile相反。使用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(func, seq):使用func对seq序列的每个元素进行计算,将计算结果作为新的序列元素。当使用func计算序列元素时,支持序列解包。如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', '-')

用于生成排列组合的工具函数:

● 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---------------------')
# 使用一个序列,重复两次进行全排列
for e in it.product('AB', repeat=2):print(''.join(e), end=', ')    # AA, AB, BA, BB,
print('\n---------------------')
# 从序列中取两个元素进行排列
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---------------------')
# 从序列中取两个元素进行组合, 元素不允许重复
for e in it.combinations('ABCD', 2):print(''.join(e), end=', ')    # AB, AC, AD, BC, BD, CD,
print('\n---------------------')
# 从序列中取两个元素进行组合,元素允许重复
for e in it.combinations_with_replacement('ABCD', 2):print(''.join(e), end=', ')    # AA, AB, AC, AD, BB, BC, BD, CC, CD, DD,
print('\n---------------------')

(二)functools模块的功能函数

该模块包含了一些函数装饰器和便捷的功能函数。

>>> [e for e in dir(functools) if not e.startswith('_')]
['RLock', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', 'cmp_to_key', 'get_cache_token', 'lru_cache', 'namedtuple', 'partial', 'partialmethod', 'recursive_repr', 'reduce', 'singledispatch', 'total_ordering', 'update_wrapper', 'wraps']

常用函数装饰器和功能函数:

● functools.cmp_to_key(func):将老式的比较函数(func)转换为关键字函数(key function)。在Python3中比较大小、排序都是基于关键字函数的,Python3不支持老式的比较函数。

提示:比较函数接收两个参数,比较这两个参数并根据它们的大小关系返回负值(代表前者小于后者)、零或正值(代表前者大于后者);关键字函数则只需要一个参数,通过该参数可返回一个用于排序关键字的值。

● @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函数的部分参数指定参数值,从而得到一个转换后的函数,程序以后调用转换后的函数时,就可以少传入那些已经指定值的参数。

● functiools.partialmethod(func, *args, **keywords):该函数与上一个函数的含义完全相同,只不过该函数用于为类中的方法设置参数值。

● functools.reduce(function, iterable[, initializer]):将初始值(默认为0,可由initializer参数指定)、迭代器的当前元素传入function函数,将计算出来的函数结果作为下一次计算的初始值、迭代器的下一个元素再次调用function函数……以此类推,知道迭代器的最后一个元素。

● @functiools.singledispatch:该函数装饰器用于实现函数对多个类型进行重载。比如同样的函数名称,为不同的参数类型提供不同的功能实现。该函数的本质就是根据参数类型的变幻,将函数转向调用不同的函数。

● functools.update_wrapper(wrapper, wrapped, assigned=SRAPPER_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 1else:return n * factorial(n - 1)
​
​
# 只有这行会计算,然后会缓存5、4、3、2、1的阶乘
print(factorial(5))
print(factorial(3))
print(factorial(5))
print('-------------------------')
# int函数默认将实际值形式的字符串转换为整数
print(int('12345'))
# 为int函数的base参数指定参数值
basetwo = partial(int ,base=2)
basetwo.__doc__ = '将二进制形式的字符串转换为整数'
# 相当于执行base为2的int函数
print(basetwo('10010'))
print(int('10010', 2))

partialmethod()与partial()函数的作用基本相似,区别在于partial()函数用于为函数的部分参数绑定值;而partialmethod()函数则用于为类中方法的部分参数绑定值。

from functools import partialmethod
​
​
class Cell:def __init__(self):self._alive = False
​# @property装饰器指定该方法可以使用属性语法访问@propertydef alive(self):return self._alive
​def set_state(self, state):self._alive = bool(state)# 指定set_alive()方法,就是将set_state()方法的state参数指定为Trueset_alive = partialmethod(set_state, True)# 指定set_dead()方法,就是将set_state()方法的state参数指定为Falseset_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)
​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 *
from decimal import Decimal
​
​
@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函数的第一个参数为list类型的函数版本
@test.register(list)
def _(argb, verbose=False):if verbose:print("列表中所有元素为:")for i, elem in enumerate(argb):print(i, elem, end=" ")
​
​
# 定义一个函数,不使用函数装饰器修饰
def nothing(arg, verbose=False):print("~~None参数~~")
​
​
#限制test函数的第一个参数为float或Decimal类型的函数版本
@test.register(float)
@test.register(Decimal)
def test_num(arg, verbose=False):if verbose:print("参数的一半为:", end=" ")print(arg / 2)
​
​
test('Python', True)  # ①
# 调用第一个参数为int类型的版本
test(20, True)  # ②
# 调用第一个参数为List类型的版本
test([20, 10, 16, 30, 14], True)  # ③
print("\n")
# 当test函数的第一个参数为None类型时,专项调用nothing函数
test.register(type(None), nothing)
test(None, True)  # ④
print("\n")

使用@singledispatch装饰器之后的函数就有了register()方法,该方法用于为指定类型注册被转向调用的函数。也可以使用register(类型,被转向调用的函数)方法来执行绑定。也允许为参数的多个类型绑定同一个被转向调用的函数:只要使用多个@函数名.register()装饰器即可。

如果想访问@singledispatch函数所绑定的全部类型及对应的dispatch函数,可通过该函数的只读属性registry来实现,该属性相当于一个只读的dict对象。

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

@wraps(wrapped_func)函数装饰器与update_wrapper(wrapper, wrapped_func)函数的作用一样,都用于让包装函数看上去就像被包装函数(主要就是让包装函数的__name__、__doc__属性与被包装函数保持一致)。区别在于@wraps(wrapped_func)函数装饰器直接修饰包装函数,而update_wrapper(wrapper, wrapped_func)则需要同时传入包装函数、被包装函数作为参数。

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__)

本章习题:

1、提示用户输入自己的名字、年龄、身高,并将该用户信息以JSON 格式保存在文件中。再写一个程序读取刚刚保存的JSON 文件,恢复用户输入的信息。

import json
​
name = input('请输入姓名:')
age = input('请输入年龄:')
hight = input('请输入身高:')
with open('info.json', 'w') as f:s = json.dump(['info', {'姓名': name, '年龄:': age, '身高:': hight}], f)
​
# 读取json文件使用load方法
with open('info.json', 'r') as f:result = json.load(f)print(result)

2. 给定一个字符串,该宇符~只包含数字O ~9 、英文逗号、英文点号,请使用英文思号、英文点号将它们分割成多个子串。

import re
import sys
​
while True:string = input('请输入字符串(只能包含0-9以及,和.):')if string.lower() == 'exit':sys.exit(0)else:if not re.fullmatch(r'[0-9,\.]+', string):raise ValueError('您的输入只能包含0-9数字、英文逗号、英文点号')str_lst = re.findall('[0-9]+', string)print(str_lst)

3. 定义一个正则表达式,用于验证国内的所有手机号码。

import re
import sys
​
while True:phone_str = input('请输入手机号码:')if phone_str.lower() == 'exit':sys.exit(0)else:if re.fullmatch(r'^(1[358][0-9]|14[579]|16[6]|17[0135678]|19[89])\d{8}$',phone_str):print('手机号码有效!')else:print('手机号码无效!')

4. 提示用户输入一个字符串,程序使用正则表达式获取该字符串中第一次重复出现的英文字母( 包括大小写〉。

import re
import sys
​
while True:user_str = input('请输入一个字符串:')if user_str.lower() == 'exit':sys.exit(0)ch_lst = re.findall(r'[a-zA-Z]', user_str)for i in range(len(ch_lst)):repeat_ch = re.search(ch_lst[i], user_str[i+1:])if repeat_ch:print('第一次重复出现的英文字母为:', ch_lst[i])break
​

5 . 提示用户输入一个字符串和一个子串,打印出该子串在字符串中出现的start和end位置;如果没有出现则打印(-1, -1) 。例如用户输入:

aaadaa

aa

程序输出:

(0, 1)

(1, 2)

(4 ,5)

import re
​
user_str, sub_str = input('请输入字符串:'), input('请输入子字符串:')
matches = list(re.finditer(r'(?={})'.format(sub_str), user_str))
if matches:print('\n'.join(str((match.start(), match.start() + len(sub_str) - 1))for match in matches))
else:print('(-1, -1)')

6. 提示用户输入两行,第一行是所有学习Python 的学员编号(以逗号隔开),第二行是所有学习Java 的学员编号(以逗号隔开〕,计算所有只学Python 不学Java 的学员的数量。

python_student = input('请输入学习python的学员编号,以逗号分隔:')
java_student = input('请输入学习java的学员编号,以逗号分隔:')
python_student = python_student.split(',')
java_student = java_student.split(',')
py_sd_set = set(python_student)
java_sd_set = set(java_student)
diff = py_sd_set - java_sd_set
print('只学python不学java的学员有:', diff)
print('只学python不学java的学员有%d人' % len(diff))

7. 提示用户输入两行,第一行是所有学习Python 的学员编号(以逗号隔开),第二行是所有学习Java 的学员编号(以逗号隔开),计算既学Python 又学Java 的学员的数量。

python_student = input('请输入学习python的学员编号,以逗号分隔:')
java_student = input('请输入学习java的学员编号,以逗号分隔:')
python_student = python_student.split(',')
java_student = java_student.split(',')
py_sd_set = set(python_student)
java_sd_set = set(java_student)
diff = py_sd_set & java_sd_set
print('只学python不学java的学员有:', diff)
print('只学python不学java的学员有%d人' % len(diff))

8. 计算用户输入的两个带时区的时间戳字符串之间相差的秒数。例如用户输入:

Sun 10 May 2015 13:54 : 36 -0700

Sun 10 May 2015 13:54:36 - 0000

程序应该输出:

25200

from datetime import datetime as dt
​
time_str1 = input('请输入带时区的时间戳字符串:')
time_str2 = input('请再输入一个带时区的时间戳字符串:')
fmt = '%a %d %b %Y %H:%M:%S %z'
diff_time = int(abs((dt.strptime(time_str1, fmt) - dt.strptime(time_str2, fmt)).total_seconds()))
print('两个时间戳之间相差%d秒' % diff_time)
​
9. 提示用户输入一个字符串,程序要输出该字符串中出现次数最多的3 个字符,以及对应的出现次数。import sys
from collections import Counter
​
while True:user_str = input('请输入一个字符串:')if user_str.lower() == 'exit':sys.exit(0)user_counter = Counter(user_str)# print('字符串中出现最多的3个字符为:', user_counter.most_common(3))[print(t[0]) for t in user_counter.most_common(3)]
​

10. 定义一个fibonacci(n)函数,该函数返回包含n 个元素的斐波那契数列的列表。再使用lambda表达式定义一个平方函数,程序最终输出斐波那契数列的前11 个元素的平方值。

def fibonacci(n):result_lst = [1, 1][result_lst.append(result_lst[-1] + result_lst[-2]) for i in range(2, n)]return result_lst
​
​
print(fibonacci(10))
# 计算fibonacci数列的元素的平方值
result = map(lambda x: x * x, fibonacci(10))
print([e for e in result], end=' ')

疯狂Python讲义学习笔记(含习题)之 常见模块相关推荐

  1. 疯狂Python讲义学习笔记(含习题)之 类和对象

    Python支持面向对象的三大特征:封装.继承和多态. 一.类和对象 可以把类当成一种自定义类型,可以使用类来定义变量,也可以使用类来创建对象. (一)定义类 类是某一批对象的抽象,可以把类理解成某种 ...

  2. 疯狂python讲义学习笔记——中十章完结

    #第十一章 thinker import tkinter as tk print(help(tk.Button.__init__))#以按扭为例查看有什么属性 class myApplication( ...

  3. 疯狂Python讲义学习笔记(含习题)之 流程控制

    Python支持两种基本流程控制结构:分支结构和循环结构.分支结构用于实现根据条件来选择性地执行某段代码:循环结构用户实现根据循环条件重复执行某段代码. Python使用if语句提供分支支持,使用wh ...

  4. 疯狂python讲义学习笔记——前十章完结

    #第一章:绪论 #单行注释 ''' 多行注释 ''' """ 多行注释 """#dir列出指定类或模块的属性与方法,help查看某个函数或方 ...

  5. 疯狂python讲义学习笔记——后十章完结

    ''' numpy pandas torch keras tensorflow tushare sklearn opencv time kivy '''#第二十一章:numpy import nump ...

  6. 疯狂Android讲义 - 学习笔记(二)

    疯狂Android讲义 - 学习笔记(二) Android应用的用户界面编程 2.1 界面编程与视图(View)组件 Android应用的绝大部分UI组件放在android.widget.androi ...

  7. 李刚 疯狂Python讲义 读书笔记

    疯狂Python讲义 李刚 这不一定是最好的python入门书,我买它纯粹是因为遇到公众号的推销.对入门教材不需要太纠结,基础知识部分大家都是互相借鉴的,买最近出版的就行. 我是经历了很长时间的碎片化 ...

  8. 疯狂Kotlin讲义学习笔记04-05章:流程控制、数组和集合

    1.when分支取代swith分支 不在需要使用case关键字 case后面的冒号改为-> default改为更有意义的else 如果一个case里有多条语句,需要将多条语句用大括号括起来 wh ...

  9. 疯狂Python讲义学习日志01——变量和简单类型

    1.变量和简单类型 1.1python脚本注释 python提供了两种脚本注释的方式: 当行脚本的注释: 对于单行的python的脚本而言,可以在该行脚本所在的左侧添加"#"字符的 ...

最新文章

  1. json 反射java 实体_Java 将JSON反射到实体类
  2. Android短信发送流程之长短信发送(原)
  3. 30分钟3300%性能提升—python+memcached网页优化小记
  4. 在鹅厂,我作为一个Java 程序员每天都在摸鱼!!!
  5. Python:模块module
  6. kibana创建索引_ELK 索引生命周期管理
  7. cad 切图_CAD切图方法你知道吗
  8. 实时音频编解码之二编码学数学知识
  9. ACM:竞赛技巧(c++)
  10. PHP-利用阿里云邮件推送免费发邮件详细步骤
  11. “双态IT”成就业务“互联网+”转型
  12. 基于激光刻划技术的石墨烯器件
  13. java Io,读写文件拒绝访问
  14. 【学习笔记】C++STL和泛型编程-侯捷
  15. Kotlin 正则表达式
  16. 最新的计算机主板,最新主流电脑主板天梯图2020
  17. 常见的网站攻击以及如何防御自己的网站被恶意攻击
  18. 【MySQL笔记 第06章_多表查询】
  19. Mac 硬件驱动(.kext)安装方法
  20. 18.AtomicReference、AtomicStampReference底层原理。多个变量更新怎么保证原子性?CAS的ABA问题怎么解决?

热门文章

  1. Python爬虫实践:获取石家庄空气质量历史数据(13年至今)
  2. ZTE-H608B开路由设置方法
  3. redis命令和Spring 的RedisTemplate操作对应关系
  4. The Simpsons第一季简要总结
  5. java中怎么定义true或false_为什么Java中的布尔值只接受true或false? 为什么也不要1或0?...
  6. 瞎说CSS中的position
  7. 服务器数据监控、业务数据监控调研
  8. 佩奇(社会人)大学初探——联迪商用实习小结(2018-4至2018-6)
  9. 华为ENSP无线小案例
  10. 【AI折腾录】stable web ui基础【sd安装、lora vae embedding hyperwork等基础概念】