@Pythonetc Junio ​​2018


Hola Ejecuto el feed @pythonetc con consejos sobre Python en particular y programación en general. A partir de este mes, estamos lanzando una serie de selecciones con las mejores publicaciones del mes traducidas al ruso.


Transferencia de datos de la cadena de llamadas


Cuando desea transferir cierta información a lo largo de una cadena de llamadas, generalmente utiliza la forma más simple: pasar datos en forma de argumentos a las funciones.


Pero a veces es muy inconveniente cambiar todas las funciones de la cadena, solo para transferir una nueva pieza de datos. En tales casos, es mejor crear un tipo de contexto que utilizarán las funciones. Como hacerlo


La solución más simple es una variable global. En Python, puede usar módulos y clases como guardianes de contexto, ya que, estrictamente hablando, también son variables globales. Quizás haga esto de vez en cuando, digamos, cuando cree registradores.


Si tiene una aplicación multiproceso, las variables globales comunes no serán de ayuda, ya que no son seguras para subprocesos. Al mismo tiempo, se pueden ejecutar varias cadenas de llamadas, y cada una necesitará su propio contexto. El módulo de threading proporciona un objeto threading.local() seguro para threading.local() . Guarde cualquier dato en él, solo refiriéndose a los atributos: threading.local().symbol = '@' .


Sin embargo, ambos enfoques no son seguros para la concurrencia, es decir, no funcionarán en cadenas de llamadas de rutina en las que las corutinas pueden no llamar a otras, pero sí las esperan. Si la corutina está en estado de espera, el bucle de eventos puede desencadenar otra corutina de una cadena diferente. Esta opción no funcionará:


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

Puede resolver el problema obligando al bucle de eventos a guardar y restaurar el contexto cada vez que regrese a la rutina. Esto es aiotask_context módulo aiotask_context , que, usando loop.set_task_factory cambia la forma de crear objetos de tarea. Esta opción funcionará:


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

Crear SVG


SVG es un formato de gráficos vectoriales que almacena información de imagen en forma de todas las formas y números necesarios para renderizar en XML. Por ejemplo, un círculo naranja se puede representar de la siguiente manera:


 <svg xmlns="http://www.w3.org/2000/svg"> <circle cx="125" cy="125" r="75" fill="orange"/> </svg> 

Como SVG es un subconjunto de XML, es bastante fácil crear archivos SVG en cualquier idioma. Incluyendo en Python, por ejemplo, usando lxml. Pero también está el módulo svgwrite, creado solo para crear SVG.


Aquí hay un ejemplo de cómo puede mostrar la secuencia de Recamann en forma de diagrama que vio al principio del artículo.


Acceso a ámbitos externos.


Cuando usa una variable en Python, primero la busca en el ámbito actual. Si no lo encuentra, busca ya en la región un nivel más alto. Y así sucesivamente, hasta que llegue al espacio de nombres global.


 x = 1 def scope(): x = 2 def inner_scope(): print(x) # prints 2 inner_scope() scope() 

Pero la asignación variable funciona de manera diferente. Siempre se crea una nueva variable en el ámbito actual, a menos que nonlocal global o 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 permite usar variables de espacio de nombres globales, y en el caso de nonlocal Python busca una variable en el contexto inmediato circundante. Compara:


 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 

Ejecución de script


python admite varias formas de ejecutar un script. El comando habitual de python foo.py solo ejecuta foo.py


También puede usar la construcción python -m foo . Si foo no foo un paquete, entonces el sistema encontrará foo.py en sys.path y se ejecutará. Si es así, Python ejecutará foo/__init__.py , y luego foo/__main__.py . Tenga en cuenta que la variable __name__ toma __name__ en tiempo de ejecución __init__.py y __main__.py en tiempo de ejecución __main__ .


También puede usar el python dir.zip python dir/ o incluso el python dir.zip . Luego, python buscará dir/__main__.py , y si lo dir/__main__.py , se ejecutará.


 $ 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__ 

El número de segundos desde el comienzo de una era.


Antes de Python 3.3, era difícil convertir un objeto de datetime y datetime a la cantidad de segundos desde el comienzo de la era de Unix.


La forma más lógica es usar el método strftime , que puede formatear datetime . Al tomar %s como formato, puede obtener una marca de tiempo.


 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 es exactamente la misma hora que utc_time , pero se registra en la forma habitual en Nueva 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 el tiempo es el mismo, las marcas de tiempo deben ser equivalentes:


 In : int(utc_time.strftime('%s')), int(ny_time.strftime('%s')) Out: (1522486800, 1522468800) 

Uh, que? ¿Por qué son diferentes? El hecho es que no puede usar strftime para resolver este problema. En Python, strftime no admite %s como argumento, y esto solo funciona porque la función strftime() de la plataforma de la biblioteca C se llama dentro. Pero, como puede ver, la zona horaria del objeto datetime se ignora por completo.


El resultado correcto se puede obtener con una simple resta:


 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 

Y si usa Python 3.3+, puede resolver el problema utilizando el método de timestamp de datetime clase de datetime : utc_time.timestamp() .

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


All Articles