Ceci est la sixiĂšme collection de conseils et de programmation Python de mon flux @pythonetc.
Sélections précédentes:
Décorateurs atypiques
Les décorateurs de fonctions ne sont pas tenus de renvoyer uniquement de nouvelles fonctions, ils peuvent renvoyer toute autre valeur:
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
Cela peut ĂȘtre utile pour crĂ©er des classes simples avec une seule mĂ©thode redĂ©finissable:
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__
Le PEP 424 permet aux générateurs et autres itérables qui n'ont pas de taille prédéterminée spécifique de renvoyer leur longueur approximative. Par exemple, ce générateur retournera probablement environ 50 éléments:
(x for x in range(100) if random() > 0.5)
Si vous écrivez quelque chose d'itérable et que vous souhaitez renvoyer une longueur approximative, définissez la méthode
__length_hint__
. Et si vous connaissez la longueur exacte, utilisez
__len__
. Si vous utilisez un objet itĂ©rable et que vous voulez savoir combien de temps il peut ĂȘtre, utilisez
operator.length_hint
.
avec générateur
L'opérateur
in
peut ĂȘtre utilisĂ© avec des gĂ©nĂ©rateurs:
x in g
. Dans ce cas, Python itérera sur
g
jusqu'Ă ce que
x
ou jusqu'Ă ce que
g
se termine.
>>> def g(): ... print(1) ... yield 1 ... print(2) ... yield 2 ... print(3) ... yield 3 ... >>> 2 in g() 1 2 True
range()
, cependant, fonctionne légÚrement mieux. Il a une méthode magique
__contains__
, grùce à laquelle la complexité de calcul de
in
devient égale à O (1):
In [1]: %timeit 10**20 in range(10**30) 375 ns ± 10.7 ns per loop
Notez que cela ne fonctionnera pas avec la fonction
xrange()
de Python 2.
Opérateurs + = et +
Python a deux opérateurs différents:
+=
et
+
. Les méthodes
__iadd__
et
__add__
sont respectivement responsables de leur comportement.
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)
Si
__iadd__
pas défini, alors
a += b
fonctionnera comme
a = a + b
.
La différence sémantique entre
+=
et
+
est que le premier change l'objet, et le second en crée un nouveau:
>>> 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]
Fonction comme attribut d'une classe
Vous ne pouvez pas stocker une fonction en tant qu'attribut de classe, car elle sera automatiquement convertie en méthode si elle est accessible via une instance:
>>> 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
Vous pouvez tricher et encapsuler la fonction dans un descripteur 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>
Vous pouvez également sortir de la situation en utilisant la méthode de classe au lieu de l'attribut.
class A: @classmethod def _get_callback(cls): return lambda x: x ** x