python - 从子类中的属性中删除属性(getter/setter)

标签 python properties

这可能在其他地方得到了回答,但我想知道是否有任何方法可以删除子类中用 @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/

相关文章:

python - 有没有办法更改 AWS lambda 函数的 'scratch' (/tmp) 空间位置?

python - 如何迭代 pandas 列中的列表以查找匹配项?

python - Django 中的自定义用户测试

python - 调用链表函数

WPF:如何在 VS2008 XAML 编辑器智能感知中显示枚举属性值?

java - NetBeans 平台 - 如何刷新节点的属性 TableView ?

c# - 我应该将多个项目之间共享的属性放在哪里?

java - 访问 Spring 3.1 应用程序 xml 中的 PropertySource

wpf - 将 UserControl 属性公开给 XAML

python - 如何将 Pandas 组变成 SparseDataFrame