零散笔记
1
函数式语言通常会提供 map、filter 和 reduce 三个高阶函数(有时使用不同的名称)。在 Python 3 中,map 和 filter 还是内置函数,但是由于引入了列表推导和生成器表达式,它们变得没那么重要了。列表推导或生成器表达式具有 map 和 filter 两个函数的功能,而且更易于阅读。
2
仅限关键字参数是 Python 3 新增的特性。定义函数时若想指定仅限关键字参数,要把它们放到前面有 * 的参数后面。如果不想支持数量不定的定位参数,但是想支持仅限关键字参数,在签名中放一个 *,如下所示:
>>> def f(a, *, b):
... return a, b
...
>>> f(1, b=2)
(1, 2)
注意,仅限关键字参数不一定要有默认值,可以像上例中 b 那样,强制必须传入实参。
下面看一个实例,生成HTML标签
def tag(name, *content, cls=None, **attrs):
if cls is not None:
attrs['class'] = cls
# 将attrs里的内容拼接
if attrs:
attrs_str = ' '.join('%s="%s"' % attr for attr in attrs.items() )
else:
attrs_str = ''
# 组装一个完整的html标签
if content:
return '\n'.join('<%s %s>%s</%s>' % (name, attrs_str, c, name) for c in content)
else:
return '</ %s>' % name
# cls是仅限关键字参数,赋值时必须以 参数名=值 的形式
print(tag('a', '采纳', '点赞', cls='btn', href='www.baidu.com'))
"""
<a class="btn" href="www.baidu.com">采纳</a>
<a class="btn" href="www.baidu.com">点赞</a>
"""
3
要想创建可散列的类型,不一定要实现特性,也不一定要保护实例属性。只需正确地实现__hash__
和 __eq__
方法即可。但是,实例的散列值绝不应该变化,因此我们借机提到了只读特性。
4
如果使用得当,__slots__
能显著节省内存,不过有几点要注意。
-
每个子类都要定义
__slots__
属性,因为解释器会忽略继承的__slots__
属性。 -
实例只能拥有
__slots__
中列出的属性,除非把__dict__
加入__slots__
中(这样做就失去了节省内存的功效)。 -
如果不把
__weakref__
加入__slots__
,实例就不能作为弱引用的目标。
5
属性查找失败后,解释器会调用 __getattr__
方法。简单来说,对 my_obj.x
表达式,Python 会检查 my_obj
实例有没有名为 x
的属性;如果没有,到类(my_obj.__class__)
中查找;如果还没有,顺着继承树继续查找。 如果依旧找不到,调用 my_obj
所属类中定义的 __getattr__
方法,传入 self
和属性名称的字符串形式(如 'x'
)
6
使用 reduce
函数时最好提供第三个参数,reduce(function, iterable, initializer)
,这样能避免这个异常:TypeError: reduce() of empty sequence with no initial value
(这个错误消息很棒,说明了问题,还提供了解决方法)。如果序列为空,initializer
是返回的结果;否则,在归约中使用它作为第一个参数,因此应该使用恒等值。比如,对 +
、|
和 ^
来说, initializer
应该是 0
;而对 *
和 &
来说,应该是 1
。
7
如果没有 __iter__
和 __contains__
方法,Python 会调用 __getitem__
方法,设法让迭代和 in 运算符可用。
8
猴子补丁
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
FrenchDeck类还不能使用random.shuffle
函数随意打乱集合中元素的位置,因为FrenchDeck 只实现了不可变的序列协议。可变的序列还必须提供 __setitem__
方法。
下面在控制台进行修正
>>> def set_card(deck, position, card):
... deck._cards[position] = card
...
>>> FrenchDeck.__setitem__ = set_card
>>> shuffle(deck)
>>> deck[:5]
[Card(rank='3', suit='hearts'), Card(rank='4', suit='diamonds'), Card(rank='4',
suit='clubs'), Card(rank='7', suit='hearts'), Card(rank='9', suit='spades')]
特殊方法 __setitem__
的签名在 Python 语言参考手册的“3.3.6. Emulating container types”中定义。语言参考中使用的参数是 self
、key
和 value