我希望将许多样板代码转换为使用装饰器,但我在弄清楚如何做到这一点上遇到了困难。
我当前的代码如下所示:
import time # for demonstration
class C(object):
def large_function(self, optional_param=[]):
"""Large remote query that takes some time"""
time.sleep(3)
# usually run without optional_param
return ['val'] + optional_param
@property
def shorthand(self):
"""Docstr..."""
if not hasattr(self, "_shorthand"):
setattr(self, "_shorthand", self.large_function())
return self._shorthand这就像我想要的那样工作,但是很明显,写很多这样的代码是很烦人的。一个简短的例子似乎也是这样做的:
import time # for demonstration
def lazy(self, name, func):
attr_name = "_" + name
if not hasattr(self, attr_name):
setattr(self, attr_name, func())
return getattr(self, attr_name)
class C(object):
def large_function(self, optional_param=[]):
"""Large remote query that takes some time"""
time.sleep(3)
# usually run without optional_param
return ['val'] + optional_param
@property
def shorthand(self):
"""Docstr..."""
return lazy(self, 'shorthand', self.large_function)然而,这看起来仍然非常冗长。最理想的情况是,我希望:
class C(object):
@add_lazy_property('shorthand')
def large_function(self, optional_param=[]):
"""Large remote query that takes some time"""
time.sleep(3)
# usually run without optional_param
return ['val'] + optional_param
c = C()
print(c.shorthand)
print(c.large_function(['add_param'])不幸的是,我发现的惰性装饰器完全掩盖了底层功能。我尝试了几个函数的setattr(),但它很快就变得混乱。有没有办法用一个装饰器'add_lazy_property‘做到这一点?如果我可以添加自己的docstring,或者至少复制函数的docstring,那就更好了。
发布于 2018-02-08 16:51:38
我能得到的最接近的结果如下:
def lazy_property(name):
internal_name = "_" + name
def method_decorator(method):
def wrapper(self, *args, **kwargs):
if not hasattr(self, internal_name):
setattr(self, internal_name, method(self, *args, **kwargs))
return getattr(self, internal_name)
return property(wrapper, doc=method.__doc__)
return method_decorator
class C(object):
def large_function(self, optional_param=[]):
"""Large remote query that takes some time"""
time.sleep(3)
# usually run without optional_param
return ['val'] + optional_param
shorthand = lazy_property("shorthand")(large_function)不幸的是,您仍然需要这一行额外的代码。问题是,该装饰器的外部两个函数不知道有关类或实例的任何信息,因此无法将结果绑定到该类或实例的成员。
如果您不关心内部名称是否与属性相同(这里我将方法名称作为基础),则不一定需要外部调用(带有名称):
def lazy_property(method):
internal_name = "_" + method.__name__
def wrapper(self, *args, **kwargs):
if not hasattr(self, internal_name):
setattr(self, internal_name, method(self, *args, **kwargs))
return getattr(self, internal_name)
return property(wrapper, doc=method.__doc__)
class C(object):
def large_function(self, optional_param=[]):
"""Large remote query that takes some time"""
time.sleep(3)
# usually run without optional_param
return ['val'] + optional_param
shorthand = lazy_property(large_function)或者,您可以使用str(uuid.uuid4())生成一个随机名称。
发布于 2018-02-08 17:44:51
我对this answer by Graipher进行了修改,允许在重命名存储的_value和自定义函数调用的同时进行调用(这样您就不必对其进行封装)。
from collections import Callable
def lazy_property(method_or_name=None, *args, **kwargs):
"""Defines a lazy named property.
If method_or_name is Callable, immediately wraps it.
Otherwise, returns a wrapper with a custom name.
*args and **kwargs are passed onto the wrapped function."""
name = method_or_name
is_callable = isinstance(name, Callable) # Check if property is callable
def method_decorator(method): # Actual work
if not is_callable: internal_name = ("_%s" % name)
else: internal_name = "_" + method.__name__
def wrapper(self):
if not hasattr(self, internal_name):
setattr(self, internal_name, method(self, *args, **kwargs))
return getattr(self, internal_name)
return property(wrapper, doc=method.__doc__)
if is_callable: return method_decorator(name) # Allows lazy_property(method)
return method_decorator # Allows lazy_property("name")(method)要演示,请执行以下操作:
import time
class C(object):
def large_function(self, optional_param=[]):
"""Large remote query that takes some time"""
time.sleep(3)
# usually run without optional_param
return ['val'] + optional_param
short1 = lazy_property(large_function)
short2 = lazy_property("short2")(large_function)
short3 = lazy_property("short3", optional_param=["foo"])(large_function)
pass
c = C()
print(c.short1)
print(c.short2)
print(c.short3)
print(c.__dict__)这就是我目前需要的所有功能,而且它看起来足够灵活。选择的method_or_name变量不太可能与任何kwargs使用一致(而不仅仅是name)。
https://stackoverflow.com/questions/48680398
复制相似问题