记录我学习《流畅的python》的过程
2021.1.31
1.获取关于参数的信息
在指定长度附近截断字符串的函数:
def clip(text, max_len=80):
"""在max_len前面或后面的第一个空格处截断文本"""
end = None
if len(text) > max_len:
space_before = text.rfind(' ', θ, max_len)
if space_before >= 0:
end = space_before
else:
space_after = text.rfind(' ', max_len)
if space_after >= 0:
end = space_after
if end is None: # 没有找到空格
end = len(text)
return text[:end].rstrip()
print(clip.__defaults__)
print(clip.__code__)
print(clip.__code__.co_varnames)
print(clip.__code__.co_argcount)
组织信息的方式并不是最便利的,使用inspect是是更好的方式:
from inspect import signature
sig = signature(clip)
print(sig)
print(str(sig))
for name,param in sig.parameters.items():
print(param.kind, ':', name, '=', param.default)
是一个有序映射。
其中有一个bind方法,它可以把任意个参数绑定到签名中的形参,所用的规则与实参到形参的匹配方式一样,框架可以使用这个方法在真正调用函数前验证参数。
import inspect
def tag(name, *content, cls=None, **attrs):
"""生成一个或多个HTML标签"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(' %s="%s"' % (attr, value)
for attr, value
in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join('<%s%s>%s</%s>' %
(name, attr_str, c, name) for c in content)
else:
return '<%s%s />' % (name, attr_str)
sig = inspect.signature(tag)
my_tag = {'name': 'img', 'title': 'Sunset Boulevard',
'src': 'sunset.jpg', 'cls': 'framed'}
bound_args = sig.bind(**my_tag)
print(bound_args)
for name, value in bound_args.arguments.items():
print(name, '=', value)
del my_tag['name']
bound_args = sig.bind(**my_tag)
2.函数注解
有注解的函数声明和提取函数签名中的注解:
from inspect import signature
def clip(text:str, max_len:'int > 0'=80) -> str:
"""在max_len前面或后面的第一个空格处截断文本"""
end = None
if len(text) > max_len:
space_before = text.rfind(' ', θ, max_len)
if space_before >= 0:
end = space_before
else:
space_after = text.rfind(' ', max_len)
if space_after >= 0:
end = space_after
if end is None: # 没有找到空格
end = len(text)
return text[:end].rstrip()
print(clip.__annotations__)
sig = signature(clip)
print(sig.return_annotation)
for param in sig.parameters.values():
note = repr(param.annotation).ljust(13)
print(note, ':', param.name, '=', param.default)
3.支持函数式编程的包
operator模块
使用reduce函数和一个匿名函数计算阶乘:
from functools import reduce
def fact(n):
return reduce(lambda a, b: a*b, range(1, n+1))
print(fact(10))
使用reduce和operator.mul函数计算阶乘:
from functools import reduce
from operator import mul
def fact(n):
return reduce(mul, range(1, n+1))
print(fact(10))
演示使用itemgetter排序一个元组列表,如果把多个参数传给itemgetter,它构建的函数会返回提取的值构成的元组:
metro_data = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.43333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
from operator import itemgetter
for city in sorted(metro_data, key=itemgetter(1)):
print(city)
cc_name = itemgetter(1, 0)
for city in metro_data:
print(cc_name(city))
attrgetter与itemgetter作用类似,它创建的函数根据名称提取对象的属性:
from collections import namedtuple
metro_data = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.43333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
Latlong = namedtuple('Latlong', 'lat long')
Metropolis = namedtuple('Metropolis', 'name cc pop coord')
metro_areas = [Metropolis(name, cc, pop, Latlong(lat, long))
for name, cc, pop, (lat, long) in metro_data]
print(metro_areas[0])
print(metro_areas[0].coord.lat)
from operator import attrgetter
name_lat = attrgetter('name', 'coord.lat')
for city in sorted(metro_areas, key=attrgetter('coord.lat')):
print(name_lat(city))
methodcaller与前两种类似,会自行创建函数。
from operator import methodcaller
s = 'The time has come'
upspace = methodcaller('upper')
print(upspace(s))
hiphenate = methodcaller('replace', ' ', '-')
print(hiphenate(s))
使用functools.partial冻结参数:
from operator import mul
from functools import partial
triple = partial(mul, 3)
print(triple(7))
print(list(map(triple, range(1, 10))))
使用partial构建一个便利的Unicode规范化函数:
import unicodedata, functools
nfc = functools.partial(unicodedata.normalize, 'NFC')
s1 = 'café'
s2 = 'cafe\u0301'
print(s1, s2)
print(s1 == s2)
print(nfc(s1) == nfc(s2))
把partial应用到tag函数上:
def tag(name, *content, cls=None, **attrs):
"""生成一个或多个HTML标签"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(' %s="%s"' % (attr, value)
for attr, value
in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join('<%s%s>%s</%s>' %
(name, attr_str, c, name) for c in content)
else:
return '<%s%s />' % (name, attr_str)
print(tag)
from functools import partial
picture = partial(tag, 'img', cls='pic-frame')
print(picture(src='wumpus.jpeg'))
print(picture)
print(picture.func)
print(picture.args)
print(picture.keywords)