我想使用类似普通lazy property装饰器的东西,但是由于TensorFlow如何工作以及我如何使用它,我需要在__init__
最新自动初始化所有惰性属性( TensorFlow部分不是问题的一部分,但请参阅here我的意思。通过"初始化"我的意思是调用getattr
来运行属性方法一次并缓存结果。
以下作品已经开始:
import functools
def graph_property(getter):
property_name = getter.__name__
attribute = '_cache_' + property_name
@property
@functools.wraps(getter)
def decorated(self):
if not hasattr(self, attribute):
setattr(self, attribute, getter(self))
self._graph.append(property_name) # for illustration
print('Initializing ' + property_name)
return getattr(self, attribute)
return decorated
class Test:
def __init__(self):
self._graph = []
self.inputs # DON'T LIKE TO DO THIS
self.do_stuff # AND THIS
@graph_property
def inputs(self):
return 42.0
@graph_property
def do_stuff(self):
return self.inputs + 1.0
if __name__ == '__main__':
t = Test()
print(t._graph)
然而,摆脱self.input
中self.do_stuff
和__init__
的手动调用会很快好 - 这很快就会变得乏味。
我正在考虑多种方式来记住"我认为,哪些属性是graph_property
在某个列表中的某个位置,但是所有属性都必须失败,因为在应用装饰器时,该类尚未知道(更不用说self
)。
我可以想象的一种方法是为返回的decorated
对象提供一些标记属性,并为Test
编写一个元类,查看所有方法,收集带有此标记的方法,并以某种方式创建他们的初始化者。我没有实现这个,因为我非常不熟悉元类,property
描述符不允许我添加属性。
所描述的方法是否可行(如果是,如何)?或者是否有更简单的方法(没有手动开销和同样好的语法)而我只是没有看到它?
答案 0 :(得分:1)
您可以添加一个简单的mixin并定义property
的子类,然后在mixin的__init__
方法中执行与此自定义属性相关的所有初始化。通过这种方式,您可以选择要在哪个班级初始化,以及何时不希望它们初始化。
import functools
class lazy_property(property):
"""
This class will help us in identifying our lazy properties, so that we
don't confuse them with normal properties.
"""
pass
def graph_property(getter):
property_name = getter.__name__
attribute = '_cache_' + property_name
@lazy_property
@functools.wraps(getter)
def decorated(self):
if not hasattr(self, attribute):
setattr(self, attribute, getter(self))
self._graph.append(property_name) # for illustration
print('Initializing ' + property_name)
return getattr(self, attribute)
return decorated
class InitializeLazyPropertiesMixin:
"""
This mixin does all of the work of initializing lazy properties
"""
def __init__(self):
cls = type(self)
fields = (k for k in dir(cls) if isinstance(getattr(cls, k), lazy_property))
for field in fields:
getattr(self, field)
class Test(InitializeLazyPropertiesMixin):
def __init__(self):
self._graph = []
# Whenever you're inheriting from this mixin make sure to call
# super `__init__` method.
super().__init__()
@graph_property
def inputs(self):
return 42.0
@graph_property
def do_stuff(self):
return self.inputs + 1.0
class Test1:
"""
Just another class that doesn't require initializing any of the lazy properties
"""
def __init__(self):
self._graph = []
@graph_property
def inputs(self):
return 42.0
@graph_property
def do_stuff(self):
return self.inputs + 1.0
演示输出:
>>> t = Test()
Initializing inputs
Initializing do_stuff
>>> print(t._graph)
['inputs', 'do_stuff']
>>> t = Test1()
>>> print(t._graph)
[]
>>> t.inputs
Initializing inputs
42.0
>>> t._graph
['inputs']
答案 1 :(得分:0)
由于您可以完全控制属性和类层次结构,因此只需标记您想要初始化的属性,并在基类__init__
方法中使用代码来调用所有属性。
首先,在装饰器中,在graph_property装饰器上设置一个变量,以便标记要初始化的方法。
由于property
对象与函数不同,因此无法为其分配任意属性,因此修复此问题是将Python的本机属性包装在用户定义的类中:
class MarcableProperty(property):
pass
def graph_property(getter):
property_name = getter.__name__
attribute = '_cache_' + property_name
@MarcableProperty
@functools.wraps(getter)
def decorated(self):
...
decorated._graph_initialize = True
return decorated
然后,在所有其他课程的Base或mixin课程上,执行以下操作:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for cls_member_name in dir(self.__class__):
# "dir" is good because it automatically looks
# at the superclasses as well
cls_member = getattr(self.__class__, cls_member_name)
if getattr(cls_member, "_graph_initialize", False):
# Fetch property, initializing its value:
getattr(self, cls_member_name)
那应该是它。