
Salut J'exécute le flux @pythonetc avec des conseils sur Python en particulier et la programmation en général. À partir de ce mois-ci, nous lançons une série de sélections avec les meilleurs articles du mois traduits en russe.
Transfert de données de la chaîne d'appel
Lorsque vous souhaitez transférer des informations le long d'une chaîne d'appels, vous utilisez généralement la méthode la plus simple: transmettre des données sous forme d'arguments aux fonctions.
Mais parfois, il est très gênant de modifier toutes les fonctions de la chaîne, uniquement pour transférer une nouvelle donnée. Dans de tels cas, il est préférable de créer une sorte de contexte que les fonctions utiliseront. Comment faire
La solution la plus simple est une variable globale. En Python, vous pouvez utiliser des modules et des classes comme gardes de contexte, car, à proprement parler, ce sont aussi des variables globales. Peut-être le faites-vous de temps en temps, par exemple, lorsque vous créez des enregistreurs.
Si vous avez une application multithread, alors les variables globales ordinaires ne vous aideront pas, car elles ne sont pas thread-safe. En même temps, plusieurs chaînes d'appels peuvent être exécutées et chacune aura besoin de son propre contexte. Le module de threading
fournit un objet threading.local()
thread-safe. Enregistrez-y toutes les données en vous référant simplement aux attributs: threading.local().symbol = '@'
.
Cependant, les deux approches ne sont pas sécurisées par la concurrence, c'est-à-dire qu'elles ne fonctionneront pas dans les chaînes d'appels de coroutine dans lesquelles les coroutines ne peuvent pas appeler d'autres coroutines, mais les attendent. Si la coroutine est en état de veille, la boucle d'événement peut déclencher une autre coroutine d'une chaîne différente. Cette option ne fonctionnera pas:
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'), ))
Vous pouvez résoudre le problème en forçant la boucle d'événements à enregistrer et à restaurer le contexte chaque fois que vous revenez à coroutine. C'est aiotask_context
module aiotask_context
, qui, en utilisant loop.set_task_factory
change la façon dont vous créez des objets de tâche. Cette option fonctionnera:
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'), ))
Créer un SVG
SVG est un format graphique vectoriel qui stocke les informations d'image sous la forme de toutes les formes et nombres nécessaires pour le rendu en XML. Par exemple, un cercle orange peut être représenté comme suit:
<svg xmlns="http://www.w3.org/2000/svg"> <circle cx="125" cy="125" r="75" fill="orange"/> </svg>
Étant donné que SVG est un sous-ensemble de XML, il est assez facile de créer des fichiers SVG dans n'importe quelle langue. Y compris en Python, par exemple, en utilisant lxml. Mais il y a aussi le module svgwrite, créé juste pour créer SVG.
Voici un exemple de la façon dont vous pouvez afficher la séquence Recamann sous la forme d'un diagramme que vous avez vu au début de l'article.
Accès aux étendues externes
Lorsque vous utilisez une variable en Python, elle la recherche d'abord dans la portée actuelle. S'il ne le trouve pas, il recherche déjà dans la région un niveau plus haut. Et ainsi de suite, jusqu'à ce qu'il atteigne l'espace de noms global.
x = 1 def scope(): x = 2 def inner_scope(): print(x)
Mais l'affectation des variables fonctionne différemment. Une nouvelle variable est toujours créée dans la portée actuelle, sauf si global
ou nonlocal
:
x = 1 def scope(): x = 2 def inner_scope(): x = 3 print(x)
global
vous permet d'utiliser des variables d'espace de noms globales, et dans le cas de nonlocal
Python recherche une variable dans le contexte immédiat. Comparez:
x = 1 def scope(): x = 2 def inner_scope(): global x x = 3 print(x)
Exécution de script
python
prend en charge plusieurs façons d'exécuter un script. La commande python foo.py
habituelle python foo.py
exécute simplement foo.py
Vous pouvez également utiliser la construction python -m foo
. Si foo
pas un package, le système trouvera foo.py
dans sys.path
et s'exécutera. Si c'est le cas, Python exécutera foo/__init__.py
, puis foo/__main__.py
. Veuillez noter que la variable __name__
prend __name__
à l'exécution __init__.py
et __main__.py
à l'exécution __main__
.
Vous pouvez également utiliser le python dir.zip
python dir/
ou même python dir.zip
. Ensuite, python
recherchera dir/__main__.py
, et s'il le dir/__main__.py
, il s'exécutera.
$ 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__
Le nombre de secondes depuis le début d'une ère
Avant Python 3.3, il était difficile de convertir un objet datetime
en nombre de secondes depuis le début de l'ère Unix.
La manière la plus logique consiste à utiliser la méthode strftime
, qui peut formater datetime
. En prenant %s
comme format, vous pouvez obtenir un horodatage.
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
est exactement le même temps que utc_time
, mais enregistré sous la forme habituelle à New York:
# utc_time datetime.datetime(2018, 3, 31, 12, 0, tzinfo=<UTC>) # utc_time datetime.datetime(2018, 3, 31, 8, 0, tzinfo=<DstTzInfo 'US/Eastern' ...>)
Si l'heure est la même, les horodatages doivent être équivalents:
In : int(utc_time.strftime('%s')), int(ny_time.strftime('%s')) Out: (1522486800, 1522468800)
Euh quoi? Pourquoi sont-ils différents? Le fait est que vous ne pouvez pas utiliser strftime
pour résoudre ce problème. En Python, strftime
ne prend pas du tout en charge %s
comme argument, et cela ne fonctionne que parce que la fonction strftime()
de la plateforme de bibliothèque C est appelée à l'intérieur. Mais, comme vous pouvez le voir, le fuseau horaire de l'objet datetime
est complètement ignoré.
Le résultat correct peut être obtenu par une simple soustraction:
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
Et si vous utilisez Python 3.3+, vous pouvez résoudre le problème en utilisant la méthode d' timestamp
de la classe datetime
: utc_time.timestamp()
.