@Pythonetc septembre 2019



Une nouvelle sélection de conseils et de programmation Python à partir de mon flux @pythonetc.

← Collections prĂ©cĂ©dentes


Dans asyncio n'est pas nĂ©cessaire de dĂ©marrer une boucle pour contenir des tĂąches. Vous pouvez crĂ©er et arrĂȘter des tĂąches mĂȘme lorsque le cycle est arrĂȘtĂ©. S'il est arrĂȘtĂ©, certaines tĂąches peuvent rester incomplĂštes.

 import asyncio async def printer(): try: try: while True: print('*') await asyncio.sleep(1) except asyncio.CancelledError: print('') finally: await asyncio.sleep(2) print('') # never happens loop = asyncio.get_event_loop() run = loop.run_until_complete task = loop.create_task(printer()) run(asyncio.sleep(1)) # printer works here print('||') run(asyncio.sleep(1)) # printer works here task.cancel() # nothing happens run(asyncio.sleep(1)) #  printed 

RĂ©sultat:

 * * || *  

Assurez-vous d'attendre que toutes les tĂąches soient terminĂ©es avant d'arrĂȘter le cycle. Si cela n'est pas fait, vous pouvez ignorer certains blocs finally et certains gestionnaires de contexte ne seront pas dĂ©sactivĂ©s.


Python vous permet de remplacer de nombreux opérateurs, y compris l'opérateur de décalage au niveau du bit. Voici un exemple de création d'une composition de fonctions à l'aide de cet opérateur. Les flÚches indiquent la direction du transfert de données:

 from collections import deque from math import sqrt class Compose: def __init__(self): self._functions = deque() def __call__(self, *args, **kwargs): result = None for f in self._functions: result = f(*args, **kwargs) args = [result] kwargs = dict() return result def __rshift__(self, f): self._functions.append(f) return self def __lshift__(self, f): self._functions.appendleft(f) return self compose = Compose sqrt_abs = (compose() << sqrt << abs) sqrt_abs2 = (compose() >> abs >> sqrt) print(sqrt_abs(-4)) # 2.0 print(sqrt_abs2(-4)) # 2.0 


Lors de la définition d'une classe, vous pouvez passer des arguments à sa métaclasse. La notation de class prend en charge les mots clés comme arguments: class Klass(Parent, arg='arg') . Le mot-clé de la métaclasse est réservé au choix d'une métaclasse et vous pouvez en utiliser d'autres à votre guise.

Voici un exemple de métaclasse qui crée une classe sans l'un des attributs. Le nom de l'attribut est fourni dans l'argument remove :

 class FilterMeta(type): def __new__(mcs, name, bases, namespace, remove=None, **kwargs): if remove is not None and remove in namespace: del namespace[remove] return super().__new__(mcs, name, bases, namespace) class A(metaclass=FilterMeta, remove='half'): def half(x): return x // 2 half_of_4 = half(4) half_of_100 = half(100) a = A() print(a.half_of_4) # 2 print(a.half_of_100) # 50 a.half # AttributeError 


Parfois, vous devez Ă©puiser le gĂ©nĂ©rateur, mais en mĂȘme temps, vous n'ĂȘtes pas intĂ©ressĂ© par les valeurs qu'il crĂ©e, mais par certains effets secondaires. Par exemple, une exception, l'Ă©criture dans un fichier, la modification d'une variable globale, etc.

Il existe un moyen pratique et populaire de le faire est list(gen()) . Cependant, cette mĂ©thode enregistre toutes les valeurs en mĂ©moire, puis les supprime immĂ©diatement. Cela peut ĂȘtre redondant. Si vous souhaitez Ă©viter ce comportement, vous pouvez utiliser deque avec une taille limite:

 from collections import deque def inversed(nums): for num in nums: yield 1 / num try: deque(inversed([1, 2, 0]), maxlen=0) except ZeroDivisionError: print('E') 

Par souci de précision sémantique, vous pouvez définir votre propre fonction d' exhaust :

 def exhaust(iterable): for _ in iterable: pass 


Supposons que vous ayez deux classes - l'enfant parent, l' User et l' Admin . Et vous avez également une fonction qui prend une liste d'utilisateurs comme argument. Pouvez-vous fournir une liste d'administrateurs? Non: la fonction peut ajouter un autre utilisateur à la liste des administrateurs, ce qui est erroné et viole les garanties fournies par la liste.

Cependant, vous pouvez fournir un type de Sequence car il est en lecture seule. Plus précisément, dans ce cas, la Sequence est covariante dans le type de participants.

Vous pouvez définir des types covariants en fournissant covariant=True comme argument à TypeVar :

 from typing import TypeVar, Generic T = TypeVar('T', covariant=True) class Holder(Generic[T]): def __init__(self, var: T): self._var: T = var def get(self) -> T: return self._var class User: pass class Admin(User): pass def print_user_from_holder(holder: Holder[User]) -> None: print(holder.get()) h: Holder[Admin] = Holder(Admin()) print_user_from_holder(h) 

Inversement, une fonction peut nécessiter un conteneur uniquement pour y placer des administrateurs. Ces conteneurs, disponibles uniquement pour l'enregistrement, sont contraires au type de participants:

 from typing import TypeVar, Generic T = TypeVar('T', contravariant=True) class Holder(Generic[T]): def __init__(self, var: T): self._var: T = var def change(self, x: T): self._var = x class User: pass class Admin(User): pass def place_admin_to_holder(holder: Holder[Admin]) -> None: holder.change(Admin()) h: Holder[User] = Holder(User()) place_admin_to_holder(h) 

Les classes qui ne sont ni covariantes ni contravariantes sont appelées invariantes .

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


All Articles