Pykka与@property setter的行为

时间:2018-07-04 09:59:48

标签: python pykka

我正在玩pykka的演员模型,发现了一些有趣的行为。这是一个演示

  1. 推出演员
  2. 获取代理
  3. 设置其@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]

我在这里有几个问题。

  1. 为什么在调用supportThread.myVal()时在主线程的上下文中调用getter .proxy()
  2. 为什么supportProxy.myVal = <a new value>行会导致主线程等待actor完成?

对我来说,这似乎是个错误:我认为代理仅应在ThreadingFuture上调用.get()时阻止执行。还是打算这样做?

1 个答案:

答案 0 :(得分:1)

免责声明:我是Pykka的作者。

此外:Pykka还没有死,它的工作原理非常不错:为Mopidy music server及其扩展名提供并发抽象。

Pykka的属性行为不是最佳的,但是有一个原因就是这样。

  1. 要创建代理对象,Pykka必须自检目标对象的API。测试目标对象上可用的属性是可调用属性,属性还是“可遍历属性”时,getattr()在每个属性上均被调用一次。这将导致调用属性getter。参见Proxy._get_attributes()Actor._get_attribute_from_path()

  2. 由于Python中无法从属性设置器捕获返回值,因此Pykka代理上的属性设置采用安全的默认值,即等待设置器完成,以便可以重新引发设置器中引发的任何异常在mainThread()的呼叫站点上。另一种选择是不处理财产设定者提出的任何例外情况。有关详细信息,请参见Proxy.__setattr__()

总而言之,属性可以与Pykka一起使用,但是您可以通过使用方法调用来获得更多控制权,因为这样一来,您就可以重新获得未来,并可以决定是否需要等待结果。

无论您是否使用Pykka,我都认为这是个好习惯,不要在属性获取程序中进行任何昂贵的工作,而应使用适当的方法来进行“昂贵”的工作。

API设计直接影响您的用户使用API​​的方式:

  • 具有属性的对象邀请重复使用相同的属性,从而重复进行重新计算。使财产变得简单而便宜。
  • 暴露方法返回结果的对象通常会导致调用者将结果保留在变量中并重用相同的结果,而不是多次调用该方法。使用方法进行任何不重要的工作。如果确实很昂贵,请考虑使用get_以外的其他前缀,例如load_fetch_calculate_,进一步指示用户应保留结果的句柄,并重新使用它。

要亲自遵循这些原则,Mopidy的核心API早已从使用大量属性迁移到使用getter和setter。在下一个主要版本中,所有属性将从Mopidy的核心API中删除。由于代理创建的工作方式,此清理将大大减少Mopidy的启动时间。