【问题标题】:python __getattribute__ override and @property decoratorpython __getattribute__ 覆盖和@property 装饰器
【发布时间】:2013-01-19 08:02:51
【问题描述】:

我必须编写一个覆盖__getattribute__ 的类。 基本上我的类是一个容器,它将每个用户添加的属性保存到self._meta 这是一个字典。

class Container(object):
    def __init__(self, **kwargs):
        super(Container, self).__setattr__('_meta', OrderedDict())
        #self._meta = OrderedDict()
        super(Container, self).__setattr__('_hasattr', lambda key : key in self._meta)

        for attr, value in kwargs.iteritems():
            self._meta[attr] = value

    def __getattribute__(self, key):
        try:
            return super(Container, self).__getattribute__(key)
        except:
            if key in self._meta : return self._meta[key]
            else:
                raise AttributeError, key

    def __setattr__(self, key, value):
        self._meta[key] = value
#usage:
>>> a = Container()
>>> a
<__main__.Container object at 0x0000000002B2DA58>
>>> a.abc = 1 #set an attribute
>>> a._meta
OrderedDict([('abc', 1)]) #attribute is in ._meta dictionary

我有一些类继承了Container 基类,它们的一些方法有@property 装饰器。

class Response(Container):
    @property
    def rawtext(self):
        if self._hasattr("value") and self.value is not None:
            _raw = self.__repr__()
            _raw += "|%s" %(self.value.encode("utf-8"))

            return _raw

问题是.rawtext 不可访问。 (我得到属性错误。)._meta 中的每个键都是可访问的,object 基类的 __setattr__ 添加的每个属性都是可访问的,但 @property 装饰器的方法到属性不是。我认为这与我在Container 基类中覆盖__getattribute__ 的方式有关。我应该怎么做才能使来自@property 的属性可以访问?

【问题讨论】:

  • 你真的需要使用__getattribute__而不是__getattr__吗?
  • @mgilson 我该如何处理__getattr__
  • __getattr__ 仅在实例上找不到该属性时才被调用。

标签: python class attributes


【解决方案1】:

我认为您可能应该考虑在这里查看__getattr__ 而不是__getattribute__。区别在于:__getattribute__ 如果存在则无条件调用 -- __getattr__ 仅在 python 无法通过其他方式找到该属性时才调用。

【讨论】:

  • 我明白了,所以我不应该碰__getattribute__而只修改__getattr__让python协议完成这项工作?
  • @thkang -- 这就是我的提议,是的:)。以你的__setattr__ 的方式,它应该做我认为正确的事情。 (即,它应该将属性打包到元排序字典中,但仍然允许属性和方法!在子类上)
【解决方案2】:

我完全同意 mgilson。如果您想要一个与您的代码等效但可以很好地使用属性的示例代码,您可以尝试:

class Container(object):
    def __init__(self, **kwargs):
        self._meta = OrderedDict()
        #self._hasattr = lambda key: key in self._meta  #???
        for attr, value in kwargs.iteritems():
            self._meta[attr] = value

    def __getattr__(self, key):
        try:
            return self._meta[key]
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key, value):
        if key in ('_meta', '_hasattr'):
            super(Container, self).__setattr__(key, value)
        else:
            self._meta[key] = value

我真的不明白你的_hasattr 属性。你把它作为一个属性,但它实际上是一个可以访问self的函数...不应该是一个方法吗? 其实我认为你应该简单地使用内置函数hasattr

class Response(Container):
    @property
    def rawtext(self):
        if hasattr(self, 'value') and self.value is not None:
            _raw = self.__repr__()
            _raw += "|%s" %(self.value.encode("utf-8"))

            return _raw

请注意,hasattr(container, attr) 也会为 _meta 返回 True

让我困惑的另一件事是你为什么使用OrderedDict。我的意思是,您迭代kwargs,并且迭代具有随机顺序,因为它是正常的dict,并在OrderedDict 中添加项目。现在你有了_meta,其中包含随机顺序的值。 如果您不确定是否需要特定订单,只需使用dict 并最终切换到OrderedDict

顺便说一句:永远不要永远在不指定要捕获的异常的情况下使用try: ... except:。在您的代码中,您实际上只想捕获AttributeErrors,所以您应该这样做:

try:
    return super(Container, self).__getattribute__(key)
except AttributeError:
    #stuff

【讨论】:

  • 感谢您的努力。我明白你对 kwargs 的看法——我在那里遗漏了一些关键的东西。正如你所说,不需要kwargs:D
  • 问题 - 如何在这里实现删除方法?
猜你喜欢
  • 1970-01-01
  • 2017-07-12
  • 2017-03-18
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 2016-03-14
  • 2017-01-25
相关资源
最近更新 更多