首页 文章

如何更换/绕过类属性?

提问于
浏览
1

我想有一个具有属性 attr 的类,当第一次访问时,它运行一个函数并返回一个值,然后变为该值(其类型更改等) .

可以通过以下方式获得类似的行为:

class MyClass(object):
    @property
    def attr(self):
        try:
            return self._cached_result
        except AttributeError:
            result = ...
            self._cached_result = result
            return result

obj = MyClass()
print obj.attr  # First calculation
print obj.attr  # Cached result is used

但是, .attr 在执行此操作时不会成为初始结果 . 如果它这样做会更有效率 .

难点在于 obj.attr 设置为属性后,无法轻松设置其他内容,因为无限循环会自然出现 . 因此,在上面的代码中, obj.attr 属性没有setter,因此无法直接修改它 . 如果定义了setter,则在此setter中替换 obj.attr 会创建一个无限循环(从setter中访问setter) . 我还想到了第一次删除setter以便能够使用 del self.attr 进行常规 self.attr = … ,但这会调用属性删除器(如果有的话),这会重新创建无限循环问题( self.attr 的修改通常会通过 property 规则) .

那么,有没有办法绕过属性机制并在 MyClass.attr.__getter__ 内用任何东西替换绑定属性 obj.attr

2 回答

  • 3

    这看起来有点像过早优化:您希望通过使描述符自身更改来跳过方法调用 .

    这是完全可能的,但它必须是合理的 .

    要修改属性中的描述符,您必须编辑您的类,这可能不是您想要的 .

    我认为实现这一目标的更好方法是:

    • 不定义 obj.attr

    • 覆盖 __getattr__ ,如果参数为"attr", obj.attr = new_value ,否则引发 AttributeError

    设置 obj.attr 后,将不再调用 __getattr__ ,因为只有在该属性不存在时才会调用它 . ( __getattribute__ 是一直被调用的那个 . )

    与初始提议的主要区别在于第一个属性访问速度较慢,因为 __getattr__ 的方法调用开销,但它将与常规 __dict__ 查找一样 .

    Example :

    class MyClass(object):
    
        def __getattr__(self, name):
            if name == 'attr':
                self.attr = ...
                return self.attr
            raise AttributeError(name)
    
    obj = MyClass()
    print obj.attr  # First calculation
    print obj.attr  # Cached result is used
    

    EDIT : 请参阅the other answer,特别是如果您使用的是Python 3.6或更高版本 .

  • 1

    对于使用描述符协议的新式类,您可以通过创建自己的自定义描述符类来完成此操作,该类最多只能调用一次 __get__() 方法 . 当发生这种情况时,然后通过创建类方法具有相同名称的实例属性来缓存结果 .

    这就是我的意思 .

    from __future__ import print_function
    
    class cached_property(object):
        """Descriptor class for making class methods lazily-evaluated and caches the result."""
        def __init__(self, func):
            self.func = func
    
        def __get__(self, inst, cls):
            if inst is None:
                return self
            else:
                value = self.func(inst)
                setattr(inst, self.func.__name__, value)
                return value
    
    class MyClass(object):
        @cached_property
        def attr(self):
            print('doing long calculation...', end='')
            result = 42
            return result
    
    obj = MyClass()
    print(obj.attr)  # -> doing long calculation...42
    print(obj.attr)  # -> 42
    

相关问题