python - Pykka 对 @property 设置器的行为
问题描述
我在玩 pykka 的演员模型,发现了一些有趣的行为。这是一个演示
- 启动一个演员
- 获取它的代理
- 设置其@properties 之一
这是代码:
import time
import pykka
from sys import version_info as python_version
if python_version > (3, 0):
from _thread import get_ident
else:
from thread import get_ident
startTime = time.time()
def debug(msg, prefix='MSG'):
msgProc = "%s (thread #%s @ t = %.2fs): %s" % (prefix,get_ident(), time.time() - startTime, msg)
print(msgProc)
def mainThread():
debug('Launching support actor...', prefix='MAIN')
supportRef = supportThread.start()
debug('Getting support proxy...', prefix='MAIN')
supportProxy = supportRef.proxy()
debug('Getting myVal obj...', prefix='MAIN')
obj = supportProxy.myVal
debug(obj, prefix='MAIN')
debug('Setting myVal obj...', prefix='MAIN')
supportProxy.myVal = 2
debug('Setting myVal obj...', prefix='MAIN')
supportProxy.myVal = 3
supportProxy.stop()
class supportThread(pykka.ThreadingActor):
def __init__(self):
super(supportThread, self).__init__()
self._myVal = 0
@property
def myVal(self):
debug("Getting value", prefix='SUPPORT')
return self._myVal
@myVal.setter
def myVal(self, value):
debug("Setting value: processing for 1s...", prefix='SUPPORT')
time.sleep(1)
debug("Setting value: done", prefix='SUPPORT')
self._myVal = value
mainThread()
输出如下所示:
MAIN (thread #16344 @ t = 0.00s): Launching support actor...
MAIN (thread #16344 @ t = 0.00s): Getting support proxy...
SUPPORT (thread #16344 @ t = 0.00s): Getting value
MAIN (thread #16344 @ t = 0.00s): Getting myVal obj...
MAIN (thread #16344 @ t = 0.00s): <pykka.threading.ThreadingFuture object at 0x0000000002998518>
MAIN (thread #16344 @ t = 0.00s): Setting myVal obj...
SUPPORT (thread #16248 @ t = 0.00s): Getting value
SUPPORT (thread #16248 @ t = 0.00s): Setting value: processing for 1s...
SUPPORT (thread #16248 @ t = 1.00s): Setting value: done
MAIN (thread #16344 @ t = 1.00s): Setting myVal obj...
SUPPORT (thread #16248 @ t = 1.01s): Setting value: processing for 1s...
SUPPORT (thread #16248 @ t = 2.01s): Setting value: done
[Finished in 2.3s]
我在这里有几个问题。
- 为什么 getter
supportThread.myVal()
在被调用时会在主线程的上下文中.proxy()
被调用? - 为什么这些行会
supportProxy.myVal = <a new value>
导致主线程等待actor完成?
这对我来说似乎是一个错误:我认为只有.get()
在 ThreadingFuture 上调用代理时才应该阻止执行。或者这是故意的?
解决方案
免责声明:我是 Pykka 的作者。
顺便说一句:Pykka 并没有死,它只是为它的用途而工作得很好:为Mopidy 音乐服务器及其 100 多个扩展提供并发抽象。
Pykka 的属性行为并不是最优的,但它是有原因的。
要创建代理对象,Pykka 必须自省目标对象的 API。当测试目标对象上可用的属性是否是可调用的、属性或“可遍历的属性”时,
getattr()
在每个属性上调用一次。这会导致调用属性 getter。见Proxy._get_attributes()
和Actor._get_attribute_from_path()
为。由于 Python 中无法从属性设置器捕获返回值,因此 Pykka 代理上的属性设置采用等待设置器完成的安全默认设置,以便在设置器中引发的任何异常都可以在
mainThread()
. 另一种方法是不处理由属性设置器引发的任何异常。详情请参阅Proxy.__setattr__()
。
总而言之,属性与 Pykka 一起“工作”,但您可以通过使用方法调用获得更多控制权,因为您总是可以返回未来,并且可以自行决定是否需要等待结果。
无论您是否使用 Pykka,我都认为最好不要在属性 getter 中做任何昂贵的工作,而是使用适当的方法来做“昂贵”的工作。
API 设计直接影响用户使用 API 的方式:
- 具有属性的对象会导致重复使用相同的属性,从而重复重新计算。保持简单和便宜的属性。
- 公开返回结果的方法的对象通常会导致调用者将结果保存在变量中并重用相同的结果,而不是多次调用该方法。对任何不平凡的工作使用方法。如果它真的很昂贵,请考虑使用另一个前缀
get_
,例如load_
,fetch_
orcalculate_
,进一步表明用户应该保留对结果的句柄并重用它。
为了自己遵循这些原则,Mopidy 的核心 API 很久以前就从使用大量属性迁移到使用 getter 和 setter。在下一个主要版本中,所有属性将从 Mopidy 的核心 API 中删除。由于代理创建的工作方式,此清理将大大减少 Mopidy 的启动时间。
推荐阅读
- python - 循环张量并将函数应用于每个元素
- excel - 根据输入从两个单独的工作表中复制多个单元格
- visual-studio - 升级到 15.9.3 后未构建 Visual Studio 2017 C++ 解决方案
- python - python中的数组和相关函数声明的问题
- node.js - 是否可以通过 socket.io 显示来自 MongoDB 的实时数据?
- java - 如何在java项目中的gpu中加载张量流模型
- xml - 缺少带有根元素的 Web 方法返回数据表
- uwp - 亚克力画笔在不对焦时的不透明度
- kdb - kdb中一列中空字符列表的复杂列表
- linux - 如何结束 GNU sed 中的“i”命令?