推迟自定义对象中的计算直到数据可用

时间:2014-07-17 17:43:29

标签: python

我有一个如下所示的自定义类。正如命名所暗示的那样,这个想法是我想在解析器类型工具中评估令牌流。一旦解析出一堆构造并将其放入数据结构中,某些令牌序列将评估为int,但是当数据结构尚未可用时,函数只返回None。这种单一的复杂数据结构构建了#39;几乎无处不在程序中传播。

class Toks(list):
    def __init__(self, seq, constructs=Constructs()):
        list.__init__(self, seq)
        self.constructs = constructs
    @property
    def as_value(self):
        val = tokens_as_value(self, self.constructs)
        return val if val is not None else self

在代码中的点,我想将这个可计算的值分配给一个名称,例如:

mything.val = Toks(tokens[start:end], constructs).as_value

好吧,这给了mything.val一个实际的int值或一个有趣的东西,它允许我们稍后计算一个值。但是这需要稍后的传递来实际执行计算,类似于:

if not isinstance(mything.val, int):
    mything.val = mything.val.as_value

碰巧,我可以在我的程序中执行此操作。然而,我真正想要发生的是完全避免第二次传递,只是有权访问属性执行计算并给出计算值,如果它在那时可计算(并且可能评估为如果不可能计算,则为某些sentinal。

有什么想法吗?


澄清:根据具体情况,我得到"价值"不同的;实际代码更像是:

if tok.type == 'NUMBER':
    mything.val = tok.value  # A simple integer value
else:
    mything.val = Toks(tokens[start:end], constructs).as_value

还有其他情况,有时我知道我早知道实际价值,有时候我不确定我以后是否只会知道它。

我意识到我可以推迟呼叫(比@dana建议的更紧凑):

return val if val is not None else lambda: self.as_value

但是,这使得以后的访问权限在mything.valmything.val()之间不一致,因此我仍然需要使用if来保护它以查看要使用的样式。在类型检查后我是否需要回退到mything.val.as_valuemything.val()会带来同样的不便。

3 个答案:

答案 0 :(得分:0)

您可以轻松地执行以下操作:

class NaiveLazy(object):
    def __init__(self, ctor):
        self.ctor = ctor
        self._value = None
    @property
    def value(self):
        if self._value is None:
            self._value = ctor()
        return self._value
mything = NaiveLazy(lambda: time.sleep(5) and 10)

然后总是使用mything.value(示例评估示例):

print mything.value # will wait 5 seconds and print 10
print mything.value # will print 10

我已经看到一些实用程序库在ctor返回None的情况下为undefined创建一个特殊对象。如果你最终希望将代码扩展到整数以外,你应该考虑一下:

class Undefined(object): pass
UNDEFINED = Undefined()
#...
self._value = UNDEFINED
#...
if self._value is UNDEFINED: self._value = ctor()

具体为您的例子:

def toks_ctor(seq, constructs=Constructs()):
    return lambda l=list(seq): tokens_as_value(l, constructs) or UNDEFINED

mything = NaiveLazy(toks_ctor(tokens[start:end], constructs))

答案 1 :(得分:0)

如果您使用的是Python3.2 +,请考虑使用Future对象。此工具允许您在后台运行任意数量的计算。您可以等待单个未来完成,并使用其值。或者,您可以在完成后逐个“流式传输”结果。

答案 2 :(得分:0)

您可以从as_value返回一个可调用对象,这样您就可以自动检查实际返回值。一个缺点是您需要使用mything.val()而不是mything.val

def tokens_as_value(toks, constructs):
    if constructs.constructed:
        return "some value"
    else:
        return None

class Constructs(object):
    def __init__(self):
        self.constructed = False

class Toks(list):
    def __init__(self, seq, constructs=Constructs()):
        list.__init__(self, seq)
        self.constructs = constructs

    @property
    def as_value(self):
        return FutureVal(tokens_as_value, self, self.constructs)

class FutureVal(object):
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self._val = None
        self.args = args
        self.kwargs = kwargs

    def __call__(self):
        if self._val is None:
            self._val = self.func(*self.args, **self.kwargs)
        return self._val

仅出于示例的目的,Constructs只包含一个布尔值,指示是否应从tokens_as_value返回实际值。

用法:

>>> t = test.Toks([])
>>> z = t.as_value
>>> z
<test.FutureVal object at 0x7f7292c96150>
>>> print(z())
None
>>> t.constructs.constructed = True
>>> print(z())
our value