情况
类似于this question , 我想更换一个属性。与那个问题不同,我不想在子类中覆盖它。我想在 init 和属性本身中替换它以提高效率,这样它就不必在每次调用属性时调用计算值的函数。
我有一个类,它有一个属性。构造函数可以获取属性的值。如果它传递了值,我想用值替换属性(不仅仅是设置属性)。这是因为属性本身会计算值,这是一项昂贵的操作。同样的,我想把属性计算出来的值替换成属性计算出来的值,这样以后调用属性就不用重新计算了:
class MyClass(object):
def __init__(self, someVar=None):
if someVar is not None: self.someVar = someVar
@property
def someVar(self):
self.someVar = calc_some_var()
return self.someVar
问题
上面的代码不起作用,因为 self.someVar = 没有替换 someVar 函数。它会尝试调用未定义的属性的 setter。
可能的解决方案
我知道我可以通过如下略微不同的方式实现相同的目的:
class MyClass(object):
def __init__(self, someVar=None):
self._someVar = someVar
@property
def someVar(self):
if self._someVar is None:
self._someVar = calc_some_var()
return self._someVar
这会略微降低效率,因为每次调用该属性时都必须检查是否为 None。该应用程序对性能至关重要,因此这可能不够好,也可能不够好。
问题
有没有办法替换类实例的属性?如果我能够这样做(即避免 None 检查和函数调用),效率会提高多少?
最佳答案
您要找的是Denis Otkidach优秀的 CachedAttribute:
class CachedAttribute(object):
'''Computes attribute value and caches it in the instance.
From the Python Cookbook (Denis Otkidach)
This decorator allows you to create a property which can be computed once and
accessed many times. Sort of like memoization.
'''
def __init__(self, method, name=None):
# record the unbound-method and the name
self.method = method
self.name = name or method.__name__
self.__doc__ = method.__doc__
def __get__(self, inst, cls):
# self: <__main__.cache object at 0xb781340c>
# inst: <__main__.Foo object at 0xb781348c>
# cls: <class '__main__.Foo'>
if inst is None:
# instance attribute accessed on class, return self
# You get here if you write `Foo.bar`
return self
# compute, cache and return the instance's attribute value
result = self.method(inst)
# setattr redefines the instance's attribute so this doesn't get called again
setattr(inst, self.name, result)
return result
可以这样使用:
def demo_cache():
class Foo(object):
@CachedAttribute
def bar(self):
print 'Calculating self.bar'
return 42
foo=Foo()
print(foo.bar)
# Calculating self.bar
# 42
请注意,随后访问 foo.bar
不会调用 getter 函数。 (Calculating self.bar
未打印。)
print(foo.bar)
# 42
foo.bar=1
print(foo.bar)
# 1
从 foo.__dict__
中删除 foo.bar
会重新公开 Foo
中定义的属性。
因此,再次调用 foo.bar
会再次重新计算该值。
del foo.bar
print(foo.bar)
# Calculating self.bar
# 42
demo_cache()
装饰器发表于 Python Cookbook也可以在 ActiveState 上找到.
这是高效的,因为虽然该属性存在于类的 __dict__
中,但在计算之后,在实例的 __dict__
中创建了一个同名的属性。 Python 的属性查找规则优先考虑实例的 __dict__
中的属性,因此类中的属性实际上被覆盖了。
关于python - 替换属性以获得性能增益,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7388258/