
Hallo. Ich führe den @ pythonetc-Feed mit Tipps zu Python im Besonderen und zur Programmierung im Allgemeinen aus. Ab diesem Monat starten wir eine Reihe von Auswahlen mit den besten Posts des Monats, die ins Russische übersetzt wurden.
Datenübertragung in der Anrufkette
Wenn Sie einige Informationen entlang einer Aufrufkette übertragen möchten, verwenden Sie normalerweise den einfachsten Weg: Übergeben Sie Daten in Form von Argumenten an Funktionen.
Manchmal ist es jedoch sehr unpraktisch, alle Funktionen in der Kette zu ändern, nur um neue Daten zu übertragen. In solchen Fällen ist es besser, eine Art Kontext zu erstellen, den die Funktionen verwenden. Wie kann man das machen?
Die einfachste Lösung ist eine globale Variable. In Python können Sie Module und Klassen als Kontextbewahrer verwenden, da sie streng genommen auch globale Variablen sind. Vielleicht tun Sie dies von Zeit zu Zeit, wenn Sie beispielsweise Logger erstellen.
Wenn Sie eine Multithread-Anwendung haben, helfen normale globale Variablen nicht, da sie nicht threadsicher sind. Gleichzeitig können mehrere Aufrufketten ausgeführt werden, von denen jede ihren eigenen Kontext benötigt. Das threading
Modul bietet ein thread-sicheres threading.local()
-Objekt. Speichern Sie alle darin enthaltenen Daten unter Bezugnahme auf die Attribute: threading.local().symbol = '@'
.
Beide Ansätze sind jedoch nicht parallelitätssicher, dh sie funktionieren nicht in Coroutine-Aufrufketten, in denen Coroutinen keine anderen Coroutinen aufrufen können, sondern auf sie warten. Wenn sich die Coroutine im Standby-Zustand befindet, kann die Ereignisschleife eine andere Coroutine aus einer anderen Kette auslösen. Diese Option funktioniert nicht:
import asyncio import sys global_symbol = '.' async def indication(timeout): while True: print(global_symbol, end='') sys.stdout.flush() await asyncio.sleep(timeout) async def sleep(t, indication_t, symbol='.'): loop = asyncio.get_event_loop() global global_symbol global_symbol = symbol loop.create_task(indication(indication_t)) await asyncio.sleep(t) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather( sleep(1, 0.1, '0'), sleep(1, 0.1, 'a'), sleep(1, 0.1, 'b'), sleep(1, 0.1, 'c'), ))
Sie können das Problem lösen, indem Sie die Ereignisschleife zwingen, den Kontext jedes Mal zu speichern und wiederherzustellen, wenn Sie zur Coroutine zurückkehren. Dies ist aiotask_context
Moduls aiotask_context
, das mithilfe von loop.set_task_factory
die Art und Weise ändert, in der Sie Aufgabenobjekte erstellen. Diese Option funktioniert:
import asyncio import sys import aiotask_context as context async def indication(timeout): while True: print(context.get('symbol'), end='') sys.stdout.flush() await asyncio.sleep(timeout) async def sleep(t, indication_t, symbol='.'): loop = asyncio.get_event_loop() context.set(key='symbol', value=symbol) loop.create_task(indication(indication_t)) await asyncio.sleep(t) loop = asyncio.get_event_loop() loop.set_task_factory(context.task_factory) loop.run_until_complete(asyncio.gather( sleep(1, 0.1, '0'), sleep(1, 0.1, 'a'), sleep(1, 0.1, 'b'), sleep(1, 0.1, 'c'), ))
SVG erstellen
SVG ist ein Vektorgrafikformat, das Bildinformationen in Form aller Formen und Zahlen speichert, die zum Rendern in XML erforderlich sind. Beispielsweise kann ein orangefarbener Kreis wie folgt dargestellt werden:
<svg xmlns="http://www.w3.org/2000/svg"> <circle cx="125" cy="125" r="75" fill="orange"/> </svg>
Da SVG eine Teilmenge von XML ist, ist es ziemlich einfach, SVG-Dateien in einer beliebigen Sprache zu erstellen. Einschließlich in Python, zum Beispiel mit lxml. Es gibt aber auch das svgwrite-Modul, das nur zum Erstellen von SVG erstellt wurde.
Hier ist ein Beispiel, wie Sie die Recamann-Sequenz in Form eines Diagramms anzeigen können, das Sie am Anfang des Artikels gesehen haben.
Zugriff auf externe Bereiche
Wenn Sie eine Variable in Python verwenden, sucht sie zuerst im aktuellen Bereich danach. Wenn es nicht gefunden wird, sucht es bereits in der Region eine Ebene höher. Und so weiter, bis es den globalen Namespace erreicht.
x = 1 def scope(): x = 2 def inner_scope(): print(x)
Die Variablenzuweisung funktioniert jedoch anders. Eine neue Variable wird immer im aktuellen Bereich erstellt, sofern nicht global
oder nicht nonlocal
:
x = 1 def scope(): x = 2 def inner_scope(): x = 3 print(x)
global
können Sie globale Namespace-Variablen verwenden. Bei nonlocal
Variablen sucht Python im unmittelbar umgebenden Kontext nach einer Variablen. Vergleichen Sie:
x = 1 def scope(): x = 2 def inner_scope(): global x x = 3 print(x)
Skriptausführung
python
unterstützt verschiedene Möglichkeiten, ein Skript auszuführen. Der übliche python foo.py
Befehl python foo.py
führt nur foo.py
Sie können auch das Konstrukt python -m foo
verwenden. Wenn foo
kein Paket ist, findet das System foo.py
in sys.path
und führt es aus. Wenn dies der foo/__init__.py
ist, führt Python foo/__init__.py
und dann foo/__main__.py
. Beachten Sie, dass die Variable __main__.py
zur Laufzeit __main__
__name__
zur Laufzeit __init__.py
__main__.py
__main__
.
Sie können auch das python dir.zip
python dir/
oder sogar python dir.zip
. Dann sucht python
nach dir/__main__.py
und wird in diesem dir/__main__.py
ausgeführt.
$ ls foo __init__.py __main__.py $ cat foo/__init__.py print(__name__) $ cat foo/__main__.py print(__name__) $ python -m foo foo __main__ $ python foo/ __main__ $ python foo/__init__.py __main__
Die Anzahl der Sekunden seit Beginn einer Ära
Vor Python 3.3 war es schwierig, ein datetime
Objekt in die Anzahl der Sekunden seit Beginn der Unix-Ära zu konvertieren.
Am logischsten ist es, die strftime
Methode zu verwenden, mit der strftime
formatiert werden datetime
. Wenn Sie %s
als Format verwenden, können Sie einen Zeitstempel erhalten.
naive_time = datetime(2018, 3, 31, 12, 0, 0) utc_time = pytz.utc.localize(naive_time) ny_time = utc_time.astimezone( pytz.timezone('US/Eastern'))
ny_time
ist genau die gleiche Zeit wie utc_time
, wird jedoch in der in New York üblichen Form aufgezeichnet:
# utc_time datetime.datetime(2018, 3, 31, 12, 0, tzinfo=<UTC>) # utc_time datetime.datetime(2018, 3, 31, 8, 0, tzinfo=<DstTzInfo 'US/Eastern' ...>)
Wenn die Zeit gleich ist, sollten die Zeitstempel gleich sein:
In : int(utc_time.strftime('%s')), int(ny_time.strftime('%s')) Out: (1522486800, 1522468800)
Äh, was? Warum sind sie anders? Tatsache ist, dass Sie strftime
nicht verwenden strftime
, um dieses Problem zu lösen. In Python unterstützt strftime
%s
überhaupt nicht als Argument, und dies funktioniert nur, weil die Funktion strftime()
der C-Bibliotheksplattform im Inneren aufgerufen wird. Wie Sie sehen, wird die Zeitzone des datetime
Objekts jedoch vollständig ignoriert.
Das richtige Ergebnis kann mit einer einfachen Subtraktion erhalten werden:
In : epoch_start = pytz.utc.localize( datetime(1970, 1, 1)) In : (utc_time - epoch_start).total_seconds() Out: 1522497600.0 In : (utc_time - epoch_start).total_seconds() Out: 1522497600.0
Wenn Sie Python 3.3+ verwenden, können Sie das Problem mithilfe der timestamp
der datetime
Klasse utc_time.timestamp()
: utc_time.timestamp()
.