@Pythonetc juin 2018


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) # prints 2 inner_scope() scope() 

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) # prints 3 inner_scope() print(x) # prints 2 scope() print(x) # prints 1 

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) # prints 3 inner_scope() print(x) # prints 2 scope() print(x) # prints 3 x = 1 def scope(): x = 2 def inner_scope(): nonlocal x x = 3 print(x) # prints 3 inner_scope() print(x) # prints 3 scope() print(x) # prints 1 

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() .

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


All Articles