Хабрахабр

Питонячий дайджест, выпуск 1

Я веду канал @pythonetc с советами про Python в частности и про программирование в целом. Привет. С этого месяца мы запускаем серию дайджестов с лучшими постами за месяц в переводе на русский.

Когда вы хотите передать какую-то информацию по цепочке вызовов, то обычно используете самый простой способ: передаете данные в виде аргументов функций.

В таких случаях лучше создать своеобразный контекст, который будут использовать функции. Но иногда очень неудобно изменять все функции в цепочке, только чтобы передать новую порцию данных. Как это сделать?

В Python в качестве хранителей контекста можно использовать модули и классы, поскольку они, строго говоря, тоже глобальные переменные. Простейшее решение — глобальная переменная. Возможно, вы периодически так и делаете, скажем, когда создаете логгеры.

Одновременно у вас могут исполняться несколько цепочек вызовов, и каждой понадобится свой контекст. Если у вас многопоточное приложение, то обычные глобальные переменные не помогут, поскольку не потокобезопасны (thread-safe). Сохраняйте в нем любые данные, просто обращаясь к атрибутам: threading.local().symbol = '@'. Модуль threading предоставляет потокобезопасный объект threading.local().

Если корутина в состоянии ожидания, event loop может запустить другую корутину из другой цепочки. Однако оба подхода не concurrency-safe, то есть они не будут работать в цепочках вызовов корутин, в которых корутины могут не вызывать другие корутины, а делать на них await. Этот вариант не будет работать:

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

Так поступает модуль aiotask_context, который с помощью loop.set_task_factory меняет способ создания объектов задач. Решить проблему можно, заставив event loop сохранять и восстанавливать контекст при каждом возвращении к корутине. Вот такой вариант сработает:

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 — векторный графический формат, хранящий информацию об изображении в виде всех форм и чисел, необходимых для отрисовки в XML.

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

В том числе и на Python, например, с помощью lxml. Поскольку SVG является подмножеством XML, можно довольно легко создавать SVG-файлы на любом языке. Но есть и модуль svgwrite, созданный как раз для создания SVG.

Вот пример, как можно отобразить последовательность Рекамана в виде диаграммы, которую вы видели в начале статьи.

Если не находит, то ищет уже в области на уровень выше. Когда вы используете переменную в Python, он сначала ищет ее в текущей области видимости. И так до тех пор, пока не дойдет глобального пространства имен.

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

Новая переменная всегда создается в текущей области видимости, если только не указано global или 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 позволяет использовать переменные глобального пространства имен, а в случае с nonlocal Python ищет переменную в ближайшем объемлющем контексте.

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

Обычная команда python foo.py просто исполняет foo.py. python поддерживает несколько способов запуска скрипта.

Если foo не является пакетом, то система найдет foo.py в sys.path и выполнит. Также можно использовать конструкцию python -m foo. Обратите внимание, что переменная __name__ в ходе выполнения __init__.py принимает значение foo, а в ходе выполнения __main__.py__main__. Если же является, Python выполнит foo/__init__.py, а потом foo/__main__.py.

Тогда python будет искать dir/__main__.py, и если найдет — выполнит. Также можно использовать форму python dir/ или даже python dir.zip.

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

3 трудно было преобразовать объект datetime в количество секунд с начала эпохи Unix. До появления Python 3.

Взяв %s в качестве формата, можно получить timestamp. Логичнее всего использовать метод strftime, который умеет форматировать datetime.

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 is the exact the same moment as utc_time, but written as New Yorkers see it: # utc_time
datetime.datetime(2018, 3, 31, 12, 0, tzinfo=<UTC>)
# utc_time
datetime.datetime(2018, 3, 31, 8, 0, tzinfo=<DstTzInfo 'US/Eastern' ...>)

Если моменты одинаковы, то и таймстемпы должны быть эквивалентны:

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

Они совершенно разные. Эээ, что? В Python strftime вообще не поддерживает %s в качестве аргумента, а работает это лишь потому, что внутри вызывается функция strftime() платформы С-библиотеки. На самом деле, использовать strftime для решения этой задачи нельзя. Но, как видите, часовой пояс объекта datetime полностью проигнорирован.

Правильный результат можно получить с помощью простого вычитания:

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

3+, то можете решить проблему с помощью timestamp класса datetime: utc_time.timestamp(). А если вы используете Python 3.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»