
你好 我运行@pythonetc feed ,特别是有关Python和一般编程的技巧。 从本月开始,我们将启动一系列精选,将当月最佳帖子翻译成俄语。
呼叫链数据传输
当您希望通过调用链传递某些信息时,通常使用最简单的方法:以参数形式将数据传递给函数。
但是有时更改链中的所有功能,仅传输新数据非常不便。 在这种情况下,最好创建一种函数将使用的上下文。 怎么做?
最简单的解决方案是全局变量。 在Python中,可以将模块和类用作上下文保持器,因为严格来说,它们也是全局变量。 例如,也许在创建记录器时会不时这样做。
如果您有多线程应用程序,那么普通的全局变量将无济于事,因为它们不是线程安全的。 同时,可以执行多个调用链,每个调用链都需要自己的上下文。 threading
模块提供了一个线程安全的threading.local()
对象。 仅引用以下属性即可将所有数据保存在其中: threading.local().symbol = '@'
。
但是,这两种方法都不是并发安全的,也就是说,它们无法在协程调用链中工作,因为协程不能调用其他协程,而是要等待它们。 如果协程处于待机状态,则事件循环可能会触发来自不同链的另一个协程。 此选项将不起作用:
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
模块的aiotask_context
,它使用loop.set_task_factory
更改了创建任务对象的方式。 此选项将起作用:
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
SVG是一种矢量图形格式,以XML呈现所需的所有形状和数字的形式存储图像信息。 例如,橙色圆圈可以表示如下:
<svg xmlns="http://www.w3.org/2000/svg"> <circle cx="125" cy="125" r="75" fill="orange"/> </svg>
由于SVG是XML的子集,因此以任何语言创建SVG文件都相当容易。 以Python为例,例如,使用lxml。 但是,还有svgwrite模块,仅用于创建SVG而创建。
这是一个示例,显示了如何以在本文开头看到的图表形式显示Recamann序列。
访问外部范围
在Python中使用变量时,它将首先在当前作用域中查找它。 如果找不到,则表示已在上一级区域中搜索。 依此类推,直到到达全局名称空间。
x = 1 def scope(): x = 2 def inner_scope(): print(x)
但是变量分配的工作原理不同。 除非nonlocal
global
或nonlocal
,否则始终在当前范围内创建一个新变量:
x = 1 def scope(): x = 2 def inner_scope(): x = 3 print(x)
global
允许您使用全局名称空间变量,对于nonlocal
名称空间nonlocal
Python在紧邻的上下文中搜索变量。 比较:
x = 1 def scope(): x = 2 def inner_scope(): global x x = 3 print(x)
脚本执行
python
支持多种运行脚本的方法。 通常的python foo.py
命令python foo.py
仅执行foo.py
您还可以使用python -m foo
构造。 如果foo
不是软件包,则系统将在sys.path
找到foo.py
并执行。 如果是这样,Python将执行foo/__init__.py
,然后执行foo/__main__.py
。 请注意, __name__
变量在运行时__init__.py
和__main__.py
在运行时__main__
。
您还可以使用python dir/
甚至python dir.zip
。 然后, python
将查找dir/__main__.py
,如果dir/__main__.py
,它将执行。
$ 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__
自一个时代开始以来的秒数
在Python 3.3之前,很难将datetime
对象转换为自Unix时代开始以来的秒数。
最合乎逻辑的方法是使用strftime
方法,该方法可以格式化datetime
。 通过将%s
作为格式,您可以获得时间戳记。
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
与ny_time
完全相同,但以纽约习惯的格式记录:
# 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)
嗯,什么? 他们为什么不同? 事实是您不能使用strftime
解决此问题。 在Python中, strftime
根本不支持%s
作为参数,这仅是有效的,因为C库平台的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
而且,如果您使用Python 3.3+,则可以使用datetime
类的timestamp
方法utc_time.timestamp()
解决问题。