@ Pythonetc September 2019



Eine neue Auswahl an Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.

Frühere Sammlungen


In asyncio muss keine Schleife gestartet werden, um Aufgaben zu enthalten. Sie können Aufgaben erstellen und stoppen, auch wenn der Zyklus gestoppt ist. Wenn es gestoppt wird, bleiben einige Aufgaben möglicherweise unvollständig.

 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 

Ergebnis:

 * * || *  

Stellen Sie sicher, dass Sie warten, bis alle Aufgaben abgeschlossen sind, bevor Sie den Zyklus beenden. Wenn dies nicht getan wird, können Sie einige finally Blöcke überspringen und einige Kontextmanager werden nicht deaktiviert.


Mit Python können Sie viele Operatoren überschreiben, einschließlich des bitweisen Verschiebungsoperators. Hier ist ein Beispiel für die Erstellung einer Funktionszusammensetzung mit diesem Operator. Die Pfeile geben die Richtung der Datenübertragung an:

 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 


Beim Definieren einer Klasse können Sie Argumente an ihre Metaklasse übergeben. Die class unterstützt Schlüsselwörter als Argumente: class Klass(Parent, arg='arg') . Das Schlüsselwort metaclass ist für die Auswahl einer Metaclass reserviert, und Sie können andere nach Belieben verwenden.

Hier ist ein Beispiel für eine Metaklasse, die eine Klasse ohne eines der Attribute erstellt. Der Attributname wird im 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 


Manchmal müssen Sie den Generator entleeren, aber gleichzeitig interessieren Sie sich nicht für die Werte, die er erzeugt, sondern für einige Nebenwirkungen. Zum Beispiel eine Ausnahme, Schreiben in eine Datei, Ändern einer globalen Variablen usw.

Es gibt eine bequeme und beliebte Möglichkeit, dies zu tun: list(gen()) . Diese Methode speichert jedoch alle Werte im Speicher und löscht sie dann sofort. Dies kann redundant sein. Wenn Sie dieses Verhalten vermeiden möchten, können Sie deque mit einer Größenbeschränkung verwenden:

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

Aus Gründen der semantischen Genauigkeit können Sie Ihre eigene exhaust definieren:

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


Angenommen, Sie haben mehrere Klassen - das übergeordnete Kind, User und Admin . Außerdem haben Sie eine Funktion, die eine Liste von Benutzern als Argument verwendet. Können Sie eine Liste der Administratoren bereitstellen? Nein: Die Funktion kann einen weiteren Benutzer zur Liste der Administratoren hinzufügen. Dies ist fehlerhaft und verstößt gegen die in der Liste enthaltenen Garantien.

Sie können jedoch einen Sequence angeben, da dieser schreibgeschützt ist. Genauer gesagt ist in diesem Fall die Sequence in der Art der Teilnehmer kovariant.

Sie können kovariante Typen definieren, indem Sie covariant=True als Argument für 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) 

Umgekehrt kann eine Funktion erfordern, dass ein Container nur Administratoren darin platziert. Solche Container, die nur zur Aufzeichnung verfügbar sind, sind je nach Art der Teilnehmer kontravariant :

 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) 

Klassen, die weder kovariant noch kontravariant sind, werden als invariant bezeichnet .

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


All Articles