@Pythonetc novembro de 2018


Esta é a sexta coleção de dicas e programação em Python do meu feed @pythonetc.

Seleções anteriores:



Decoradores atípicos


Os decoradores de funções não precisam retornar apenas novas funções, eles podem retornar qualquer outro valor:

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 

Isso pode ser útil para criar classes simples com apenas um método redefinível:

 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__


O PEP 424 permite que geradores e outros iteráveis ​​que não tenham um tamanho predeterminado específico retornem seu comprimento aproximado. Por exemplo, esse gerador provavelmente retornará cerca de 50 elementos:

 (x for x in range(100) if random() > 0.5) 

Se você escreve algo iterável e deseja retornar um comprimento aproximado, defina o método __length_hint__ . E se você souber o comprimento exato, use __len__ . Se você usa um objeto iterável e deseja saber quanto tempo pode demorar, use operator.length_hint .

com gerador


O operador in pode ser usado com geradores: x in g . Nesse caso, o Python iterará sobre g até que x ou até que g termine.

 >>> def g(): ... print(1) ... yield 1 ... print(2) ... yield 2 ... print(3) ... yield 3 ... >>> 2 in g() 1 2 True 

range() , no entanto, funciona um pouco melhor. Possui um método mágico substituído __contains__ , graças ao qual a complexidade computacional de in se torna igual a O (1):

 In [1]: %timeit 10**20 in range(10**30) 375 ns ± 10.7 ns per loop 

Observe que isso não funcionará com a função xrange() do Python 2.

Operadores + = e +


Python possui dois operadores diferentes: += e + . Os métodos __iadd__ e __add__ são responsáveis ​​por seu comportamento, respectivamente.

 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) 

Se __iadd__ não __iadd__ definido, a += b funcionará como a = a + b .

A diferença semântica entre += e + é que o primeiro altera o objeto e o segundo cria um novo:

 >>> 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] 

Funcionar como um atributo de uma classe


Você não pode armazenar uma função como um atributo de classe, porque ela será convertida automaticamente em um método se for acessada por meio de uma instância:

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

Você pode trapacear e agrupar a função em um descritor trivial:

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

Você também pode sair da situação usando o método de classe em vez do atributo

 class A: @classmethod def _get_callback(cls): return lambda x: x ** x 

Source: https://habr.com/ru/post/pt432628/


All Articles