Dies ist die dritte Sammlung von Python-Tipps und -Programmierungen aus meinem 
@ pythonetc-Feed .
Vorherige Auswahl:
Fabrikmethode
Wenn Sie neue Objekte in 
__init__ , ist es zweckmäßiger, sie als Argumente zu übergeben und das Objekt mit der Factory-Methode zu erstellen. Dadurch wird die Geschäftslogik von der technischen Implementierung der Erstellung von Objekten getrennt.
In diesem Beispiel akzeptiert 
__init__ host und 
port als Argumente zum Erstellen einer Datenbankverbindung:
 class Query: def __init__(self, host, port): self._connection = Connection(host, port) 
Refactoring-Option:
 class Query: def __init__(self, connection): self._connection = connection @classmethod def create(cls, host, port): return cls(Connection(host, port)) 
Dieser Ansatz hat mindestens die folgenden Vorteile:
- Die Bereitstellung ist einfach. In Tests können Sie eine Query(FakeConnection()).
- Eine Klasse kann so viele Factory-Methoden haben, wie Sie möchten. Sie können eine Verbindung nicht nur über hostundporterstellen, sondern auch eine andere Verbindung klonen, die Konfigurationsdatei lesen, die Standardverbindung verwenden usw.
- Ähnliche Factory-Methoden können in asynchrone Funktionen umgewandelt werden, was mit __init__absolut unmöglich ist.
super oder weiter
Mit der Funktion 
super() können Sie auf die Basisklasse verweisen. Dies kann sehr nützlich sein, wenn die abgeleitete Klasse der Implementierung der Methode etwas hinzufügen möchte, anstatt sie vollständig zu überschreiben.
 class BaseTestCase(TestCase): def setUp(self): self._db = create_db() class UserTestCase(BaseTestCase): def setUp(self): super().setUp() self._user = create_user() 
Der Name Super bedeutet nichts „Super“. In diesem Zusammenhang bedeutet es "höher in der Hierarchie" (zum Beispiel wie im Wort "Superintendent"). Gleichzeitig verweist 
super() nicht immer auf die Basisklasse, sondern kann leicht eine untergeordnete Klasse zurückgeben. Es wäre also korrekter, den Namen 
next() , da die nächste Klasse gemäß dem MRO zurückgegeben wird.
 class Top: def foo(self): return 'top' class Left(Top): def foo(self): return super().foo() class Right(Top): def foo(self): return 'right' class Bottom(Left, Right): pass  
Denken Sie daran, dass 
super() unterschiedliche Ergebnisse liefern kann, je nachdem, woher die Methode ursprünglich aufgerufen wurde.
 >>> Bottom().foo() 'right' >>> Left().foo() 'top' 
Benutzerdefinierter Namespace zum Erstellen einer Klasse
Eine Klasse wird in zwei großen Schritten erstellt. Zunächst wird der Hauptteil der Klasse wie der Hauptteil einer Funktion ausgeführt. Im zweiten Schritt wird der resultierende Namespace (der von 
locals() ) von der Metaklasse verwendet (der Standardwert ist 
type ), um ein Objekt der Klasse zu erstellen.
 class Meta(type): def __new__(meta, name, bases, ns): print(ns) return super().__new__( meta, name, bases, ns ) class Foo(metaclass=Meta): B = 2 
Dieser Code zeigt 
{'__module__': '__main__', '__qualname__':'Foo', 'B': 3} .
Offensichtlich, wenn Sie etwas wie 
B = 2; B = 3 einführen 
B = 2; B = 3 B = 2; B = 3 , dann sieht die Metaklasse nur 
B = 3 , da nur dieser Wert in 
ns . Diese Einschränkung ergibt sich aus der Tatsache, dass die Metaklasse erst nach Ausführung des Körpers zu arbeiten beginnt.
Sie können jedoch in die Ausführungsprozedur eingreifen, indem Sie Ihren eigenen Namespace verschieben. Standardmäßig wird ein einfaches Wörterbuch verwendet. Sie können jedoch ein eigenes Objekt bereitstellen, das einem Wörterbuch ähnelt, wenn Sie die Methode 
__prepare__ aus der Metaklasse verwenden.
 class CustomNamespace(dict): def __setitem__(self, key, value): print(f'{key} -> {value}') return super().__setitem__(key, value) class Meta(type): def __new__(meta, name, bases, ns): return super().__new__( meta, name, bases, ns ) @classmethod def __prepare__(metacls, cls, bases): return CustomNamespace() class Foo(metaclass=Meta): B = 2 B = 3 
Ergebnis der Codeausführung:
 __module__ -> __main__ __qualname__ -> Foo B -> 2 B -> 3 
Somit ist 
enum.Enum vor 
Vervielfältigung geschützt.
matplotlib
matplotlib ist eine komplexe und flexible Python-Bibliothek für Diagramme. Viele Produkte unterstützen dies, einschließlich Jupyter und Pycharm. Hier ist ein Beispiel für das Rendern eines einfachen Fraktals mit 
matplotlib : 
https://repl.it/@VadimPushtaev/myplotlib (siehe Titelbild dieser Veröffentlichung).
Zeitzonenunterstützung
Python bietet eine leistungsstarke 
datetime zum Arbeiten mit Datums- und 
datetime . Es ist merkwürdig, dass 
datetime Objekte eine spezielle Schnittstelle zur Unterstützung von Zeitzonen haben (nämlich das 
tzinfo Attribut), aber dieses Modul unterstützt die erwähnte Schnittstelle nur begrenzt, sodass ein Teil der Arbeit anderen Modulen zugewiesen wird.
Der beliebteste von ihnen ist 
pytz . Tatsache ist jedoch, dass 
pytz die 
tzinfo Schnittstelle nicht vollständig 
tzinfo . Dies steht ganz am Anfang der 
pytz Dokumentation: „Diese Bibliothek unterscheidet sich von der dokumentierten Python-API für tzinfo-Implementierungen.“
Sie können keine 
pytz als 
tzinfo . Wenn Sie dies versuchen, riskieren Sie ein völlig verrücktes Ergebnis:
 In : paris = pytz.timezone('Europe/Paris') In : str(datetime(2017, 1, 1, tzinfo=paris)) Out: '2017-01-01 00:00:00+00:09' 
Beachten Sie den Offset +00: 09. Pytz sollte wie folgt verwendet werden:
 In : str(paris.localize(datetime(2017, 1, 1))) Out: '2017-01-01 00:00:00+01:00' 
Darüber hinaus müssen Sie nach arithmetischen Operationen eine 
normalize auf Ihre 
datetime Objekte 
datetime , um zu vermeiden, dass sich Offsets ändern (z. B. am Rand der Sommerzeit).
 In : new_time = time + timedelta(days=2) In : str(new_time) Out: '2018-03-27 00:00:00+01:00' In : str(paris.normalize(new_time)) Out: '2018-03-27 01:00:00+02:00' 
Wenn Sie Python 3.6 haben, wird in der Dokumentation empfohlen, 
dateutil.tz anstelle von 
pytz . Diese Bibliothek ist vollständig kompatibel mit 
tzinfo . Sie kann als Attribut übergeben werden und Sie müssen 
normalize nicht verwenden. Es stimmt, es funktioniert langsamer.
Wenn Sie wissen möchten, warum 
pytz die 
datetime API nicht unterstützt, oder weitere Beispiele 
datetime möchten, lesen Sie 
diesen Artikel.
Magic StopIteration
Bei jedem Aufruf von 
next(x) ein neuer Wert vom Iterator 
x bis eine Ausnahme ausgelöst wird. Wenn sich herausstellt, dass es sich um 
StopIteration , ist der Iterator erschöpft und kann keine Werte mehr bereitstellen. Wenn der Generator iteriert wird, wird am Ende des Körpers automatisch 
StopIteration :
 >>> def one_two(): ... yield 1 ... yield 2 ... >>> i = one_two() >>> next(i) 1 >>> next(i) 2 >>> next(i) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 
StopIteration kann automatisch von Tools verarbeitet werden, die 
next aufrufen:
 >>> list(one_two()) [1, 2] 
Das Problem ist jedoch, dass jede offensichtlich nicht erwartete StopIteration, die im Generatorkörper aufgetreten ist, stillschweigend als Zeichen für das Ende des Generators akzeptiert wird und nicht wie jede andere Ausnahme als Fehler:
 def one_two(): yield 1 yield 2 def one_two_repeat(n): for _ in range(n): i = one_two() yield next(i) yield next(i) yield next(i) print(list(one_two_repeat(3))) 
Hier ist die letzte 
yield ein Fehler: Eine ausgelöste 
StopIteration Ausnahme stoppt die Iteration der 
list(...) . Wir erhalten das Ergebnis 
[1, 2] . In Python 3.7 hat sich dieses Verhalten jedoch geändert. Alien 
StopIteration durch 
StopIteration ersetzt:
 Traceback (most recent call last): File "test.py", line 10, in one_two_repeat yield next(i) StopIteration The above exception was the direct cause of the following exception: Traceback (most recent call last): File "test.py", line 12, in <module> print(list(one_two_repeat(3))) RuntimeError: generator raised StopIteration 
Sie können 
__future__ import generator_stop , um dasselbe Verhalten seit Python 3.5 zu aktivieren.