
Oi Eu corro o feed @pythonetc com dicas sobre Python em particular e programação em geral. A partir deste mês, lançamos uma série de seleções com as melhores postagens do mês traduzidas para o russo.
Transferência de dados da cadeia de chamadas
Quando você deseja transferir algumas informações ao longo de uma cadeia de chamadas, geralmente usa a maneira mais simples: passar dados na forma de argumentos para funções.
Mas, às vezes, é muito inconveniente alterar todas as funções da cadeia, apenas para transferir um novo dado. Nesses casos, é melhor criar um tipo de contexto que as funções usarão. Como fazer isso?
A solução mais simples é uma variável global. No Python, você pode usar módulos e classes como mantenedores de contexto, pois, estritamente falando, eles também são variáveis globais. Talvez você faça isso de vez em quando, por exemplo, ao criar loggers.
Se você tiver um aplicativo multiencadeado, as variáveis globais comuns não ajudarão, pois não são seguras. Ao mesmo tempo, várias cadeias de chamadas podem ser executadas e cada uma precisará de seu próprio contexto. O módulo threading
fornece um objeto threading.local()
seguro para threading.local()
. Salve todos os dados nele, apenas consultando os atributos: threading.local().symbol = '@'
.
No entanto, ambas as abordagens não são seguras para simultaneidade, ou seja, elas não funcionarão em cadeias de chamadas de corotina nas quais as corotinas podem não chamar outras corotinas, mas esperam por elas. Se a corotina estiver no modo de espera, o loop de eventos poderá acionar outra corotina de uma cadeia diferente. Esta opção não irá 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'), ))
Você pode resolver o problema forçando o loop de eventos a salvar e restaurar o contexto toda vez que retornar à corotina. É aiotask_context
módulo aiotask_context
, que, usando loop.set_task_factory
altera a maneira como você cria objetos de tarefas. Esta opção irá 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'), ))
Criar SVG
SVG é um formato de gráficos vetoriais que armazena informações da imagem na forma de todas as formas e números necessários para renderização em XML. Por exemplo, um círculo laranja pode ser representado da seguinte maneira:
<svg xmlns="http://www.w3.org/2000/svg"> <circle cx="125" cy="125" r="75" fill="orange"/> </svg>
Como SVG é um subconjunto de XML, é bastante fácil criar arquivos SVG em qualquer idioma. Inclusive no Python, por exemplo, usando lxml. Mas há também o módulo svgwrite, criado apenas para criar SVG.
Aqui está um exemplo de como você pode exibir a sequência Recamann na forma de um diagrama que você viu no início do artigo.
Acesso a escopos externos
Quando você usa uma variável no Python, ela primeiro a procura no escopo atual. Se não o encontrar, ele já pesquisa na região um nível acima. E assim por diante, até atingir o espaço para nome global.
x = 1 def scope(): x = 2 def inner_scope(): print(x)
Mas a atribuição de variáveis funciona de maneira diferente. Uma nova variável é sempre criada no escopo atual, a menos que nonlocal
global
ou nonlocal
:
x = 1 def scope(): x = 2 def inner_scope(): x = 3 print(x)
global
permite que você use variáveis globais de espaço para nome e, no caso nonlocal
Python procura uma variável no contexto circundante imediato. Compare:
x = 1 def scope(): x = 2 def inner_scope(): global x x = 3 print(x)
Execução de script
python
suporta várias maneiras de executar um script. O comando python foo.py
usual python foo.py
apenas executa foo.py
Você também pode usar a construção python -m foo
. Se foo
não foo
um pacote, o sistema encontrará foo.py
no sys.path
e executará. Se for, o Python executará foo/__init__.py
e depois foo/__main__.py
. Observe que a variável __name__
é __main__.py
no tempo de execução __init__.py
e __main__.py
no tempo de execução __main__
.
Você também pode usar o python dir.zip
python dir/
ou mesmo python dir.zip
. Então, o python
procurará dir/__main__.py
e, se o dir/__main__.py
, será executado.
$ 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__
O número de segundos desde o início de uma era
Antes do Python 3.3, era difícil converter um objeto datetime
no número de segundos desde o início da era do Unix.
A maneira mais lógica é usar o método strftime
, que pode formatar datetime
e datetime
. Tomando %s
como o formato, você pode obter um carimbo de data / hora.
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
é exatamente o mesmo tempo que utc_time
, mas registrado no formato habitual em Nova 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' ...>)
Se a hora for a mesma, os registros de data e hora deverão ser equivalentes:
In : int(utc_time.strftime('%s')), int(ny_time.strftime('%s')) Out: (1522486800, 1522468800)
O que? Por que eles são diferentes? O fato é que você não pode usar o strftime
para resolver esse problema. No Python, o strftime
não suporta %s
como argumento, e isso só funciona porque a função strftime()
da plataforma da biblioteca C é chamada dentro. Mas, como você pode ver, o fuso horário do objeto datetime
é completamente ignorado.
O resultado correto pode ser obtido com uma simples subtração:
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
E se você usa o Python 3.3+, pode resolver o problema usando o método timestamp
da classe datetime
: utc_time.timestamp()
.