我已经实现了用于延迟评估转储到JSON的配置的类。没问题,只需扩展编码器以使用特定协议(固定方法/属性)主动评估类。
class DeferredCall(object):
"""Call that is evaluated lazyly"""
def __init__(self, func, *func_args, **func_kwargs):
self.func = func
self.func_args = func_args
self.func_kwargs = func_kwargs
def resolve(self): # called by JSON encoder
return self.func(*self.func_args, **self.func_kwargs)
a = DeferredCall(lambda: 1)
a # gives <[module].DeferredCall at 0x1e99410>
a.resolve() # gives 1
现在,功能强大的用户需要更多功率。即,直接对类进行操作而不是它们所代表的值。根据{{3}},这应该像实现魔术方法一样简单,例如__add__
,__len__
等。
添加
def __add__(self, other):
return self.resolve() + other
或
def __add__(self, other):
return self.resolve().__add__(other)
会正确地给我a + 3 == 4
。
实现所有魔术方法有点过分了。所以我尝试使用__getattr__
def __getattr__(self, item):
return getattr(self.resolve(), item)
适用于a.__mul__(3) == 3
,但a * 3 == 3
与TypeError: unsupported operand type(s) for *: 'DeferredCall' and 'int'
一起爆炸。
那么有没有其他方法将运算符转发到包装值?理想情况下,没有冒险以编程方式编写代码或__getattribute__
的麻烦。
答案 0 :(得分:1)
在任何其他人需要的情况下发布我的解决方案。大多数&#34;内置&#34; */1 * * * * aide --check | echo "Start timestamp: `date +'%Y-%m-%d %H:%M:%S'`" > /var/log/aide/aide2.log
或*
等操作在设计时不会使用len
。
我已经决定以编程方式创建方法
__getattr[ibute]__
基本上,魔术方法必须分为两类:自足方法和上下文方法。
直接创建自包含的,解析调用并执行魔术方法。例如,class DeferredCall(object):
def __init__(self, func, *func_args, **func_kwargs):
self.func = func
self.func_args = func_args
self.func_kwargs = func_kwargs
def resolve(self):
return self.func(*self.func_args, **self.func_kwargs)
# magic must be resolved explicitly
# self other - resolve in reflected order to allow other to resolve as well
for so_magic in [("lt", "gt"), ("le", "ge"), ("eq", "eq"), ("ne", "ne"), ("add", "radd"), ("sub", "rsub"), ("mul", "rmul"), ("div", "rdiv"), ("truediv", "rtruediv"), ("floordiv", "rfloordiv"), ("mod", "rmod"), ("divmod", "rdivmod"), ("pow", "rpow"), ("lshift", "rlshift"), ("rshift", "rrshift"), ("and", "rand"), ("xor", "rxor"), ("or", "ror")]:
for func_name, refl_name in [(so_magic[0], so_magic[1]), (so_magic[1], so_magic[0])]:
exec("def __%(fname)s__(self, other):\n\ttry:\n\t\tres = other.__%(rname)s__(self.resolve())\n\t\tif res == NotImplemented:\n\t\t\traise AttributeError\n\texcept AttributeError:\n\t\tres = self.resolve().__%(fname)s__(other)\n\treturn res" % {"fname": func_name, "rname": refl_name})
# regular magic - immutable only
for magic in ("str", "nonzero", "unicode", "getattr", "call", "len", "getitem", "missing", "iter", "reversed", "contains", "getslice", "neg", "pos", "abs", "invert", "complex", "int", "long", "float", "oct", "hex", "index"):
exec("def __%(fname)s__(self, *args, **kwargs):\n\treturn self.resolve().__%(fname)s__(*args, **kwargs)" % {"fname": magic})
被解析为:
len
上下文必须反转呼叫,例如&#34;大于&#34; def __len__(self, *args, **kwargs):
return self.resolve().__len__(*args, **kwargs)
实际检查other
是否小于&#34; other
。如果两个对象都是延迟调用,则这是必需的,允许self
自行解析;否则,许多方法都会引发other
。仅当TypeError
没有倒置版本时才使用直接评估。
other
有些调用可能会更有效地实现,因为python使用了一些技巧(这正是我的问题首先出现的地方)。例如,乘法可以利用交换性:
def __gt__(self, other):
try:
res = other.__lt__(self.resolve())
if res == NotImplemented:
raise AttributeError
except AttributeError:
res = self.resolve().__gt__(other)
return res