这可能在其他地方得到了回答,但我想知道是否有任何方法可以删除子类中用 @property
修饰的属性/方法。
例子:
from datetime import datetime
class A():
def __init__(self, num):
self._num = num
@property
def id(self):
return self._num * datetime.now().timestamp()
class B(A):
def __init__(self, id, num):
super().__init__(num)
self.id = id
如果您尝试创建类 B
的实例,则上述代码不会运行。 AttributeError: 无法设置属性
基类使用一个属性,因为它需要动态评估其 ID,而我的子类在创建时能够知道其 ID。 id
属性经常被访问,我发现性能受到了显着影响,因为我必须使用一个属性来为该属性提供服务,而不是直接访问它。 (据我所知,属性将访问时间增加了 5 倍)。我的应用程序目前花费大约 10% 的运行时间来获取此属性。
有什么方法可以使子类中的属性短路?
最佳答案
我将在这里讨论几种可能性。他们中的一些人会按照您的字面意思去做。其中一些没有,但无论如何它们可能是更好的选择。
首先,由于时间的推移,您的示例基类在每次访问时都会更改 obj.id
的值。这真的很奇怪,看起来不像是一个有用的“ID”概念。如果您的实际用例具有稳定的 obj.id
返回值,那么您可以缓存它以避免重新计算的开销:
def __init__(self):
...
self._id = None
@property
def id(self):
if self._id is not None:
return self._id
retval = self._id = expensive_computation()
return retval
这可以减轻属性(property)的开支。如果您需要更多缓解措施,请寻找您重复访问 id
的地方,而是访问它一次并将其保存在变量中。无论属性如何实现,局部变量查找都优于属性访问。 (当然,如果你真的有奇怪的时变 ID,那么这种重构可能是无效的。)
其次,您不能用“常规”属性覆盖 property
,但您可以创建自己的 property
版本可以这样覆盖。你的 property
阻止属性设置,并且优先于“常规”属性,即使你强制进入实例 __dict__
,因为 property
有一个__set__
方法(即使您不编写 setter)。在没有 __set__
的情况下编写您自己的描述符将允许覆盖。您可以使用通用的 LowPriorityProperty
来实现:
class LowPriorityProperty(object):
"""
Like @property, but no __set__ or __delete__, and does not take priority
over the instance __dict__.
"""
def __init__(self, fget):
self.fget = fget
def __get__(self, instance, owner=None):
if instance is None:
return self
return self.fget(instance)
class Foo(object):
...
@LowPriorityProperty
def id(self):
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
或者使用特定于角色的描述符类:
class IDDescriptor(object):
def __get__(self, instance, owner=None):
if instance is None:
return self
# Remember, self is the descriptor. instance is the object you're
# trying to compute the id attribute of.
return whatever(instance)
class Foo(object):
id = IDDescriptor()
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
特定于角色的描述符比通用的 LowPriorityProperty
表现更好,但两者的表现都比 property
差,因为在 Python 而不是 C 中实现了更多逻辑。
最后,您不能用“常规”属性覆盖 property
,但您可以用另一个描述符覆盖它,例如另一个 property
,或者例如为 __slots__
创建的描述符。如果您真的非常需要性能,__slots__
可能比您可以手动实现的任何描述符都更高效,但是 __slots__
和属性之间的交互是奇怪和模糊的你可能想发表评论来解释你在做什么。
class Foo(object):
@property
def id(self):
...
class Bar(Foo):
__slots__ = ('id',)
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
关于python - 从子类中的属性中删除属性(getter/setter),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54113468/