这是来自@pythonetc feed的第六个Python技巧和编程集合。
先前的选择:
非典型装饰
不需要函数装饰器仅返回新函数,它们可以返回任何其他值:
def call(*args, **kwargs): def decorator(func): return func(*args, **kwargs) return decorator @call(15) def sqr_15(x): return x * x assert sqr_15 == 225
这对于仅使用一种可重新定义的方法创建简单的类很有用:
from abc import ABCMeta, abstractmethod class BinaryOperation(metaclass=ABCMeta): def __init__(self, left, right): self._left = left self._right = right def __repr__(self): klass = type(self).__name__ left = self._left right = self._right return f'{klass}({left}, {right})' @abstractmethod def do(self): pass @classmethod def make(cls, do_function): return type( do_function.__name__, (BinaryOperation,), dict(do=do_function), ) class Addition(BinaryOperation): def do(self): return self._left + self._right @BinaryOperation.make def Subtraction(self): return self._left - self._right
__length_hint__
PEP 424允许没有特定预定大小的生成器和其他可迭代程序返回其近似长度。 例如,此生成器可能会返回大约50个元素:
(x for x in range(100) if random() > 0.5)
如果您编写了可迭代的东西并想返回一个近似的长度,请定义
__length_hint__
方法。 如果知道确切的长度,请使用
__len__
。 如果使用可迭代对象,并且想知道它可以持续多久,请使用
operator.length_hint
。
与发电机一起
in
运算符可以与生成器一起使用:
x in g
。 在这种情况下,Python将遍历
g
直到
x
或
g
结束。
>>> def g(): ... print(1) ... yield 1 ... print(2) ... yield 2 ... print(3) ... yield 3 ... >>> 2 in g() 1 2 True
range()
不过效果更好。 它具有不可思议的覆盖方法
__contains__
,因此
in
的计算复杂度等于O(1):
In [1]: %timeit 10**20 in range(10**30) 375 ns ± 10.7 ns per loop
请注意,这不适用于Python 2中的
xrange()
函数。
运算符+ =和+
Python有两个不同的运算符:
+=
和
+
。
__iadd__
和
__add__
方法分别负责它们的行为。
class A: def __init__(self, x): self.x = x def __iadd__(self, another): self.x += another.x return self def __add__(self, another): return type(self)(self.x + another.x)
如果
__iadd__
,则
a += b
将作为
a = a + b
起作用。
+=
和
+
之间的语义区别在于,第一个更改对象,而第二个创建新对象:
>>> a = [1, 2, 3] >>> b = a >>> a += [4] >>> a [1, 2, 3, 4] >>> b [1, 2, 3, 4] >>> a = a + [5] >>> a [1, 2, 3, 4, 5] >>> b [1, 2, 3, 4]
用作类的属性
您不能将函数存储为类属性,因为如果通过实例访问它,它将自动转换为方法:
>>> class A: ... CALLBACK = lambda x: x ** x ... >>> A.CALLBACK <function A.<lambda> at 0x7f68b01ab6a8> >>> A().CALLBACK <bound method A.<lambda> of <__main__.A object at 0x7f68b01aea20>> >>> A().CALLBACK(4) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: <lambda>() takes 1 positional argument but 2 were given
您可以将函数作弊并包装在一个简单的描述符中:
>>> class FunctionHolder: ... def __init__(self, f): ... self._f = f ... def __get__(self, obj, objtype): ... return self._f ... >>> class A: ... CALLBACK = FunctionHolder(lambda x: x ** x) ... >>> A().CALLBACK <function A.<lambda> at 0x7f68b01ab950>
您也可以使用类方法而不是属性来摆脱这种情况。
class A: @classmethod def _get_callback(cls): return lambda x: x ** x