Python __getattr__如果它不存在则创建attr并返回它

时间:2016-08-06 17:24:09

标签: python django

一点背景:你会注意到我的评论描述了我稍后会经历的内容。假设我有以下对象......

#!/usr/bin/python
import os
import sys

class ContainerField(object):
    ''' An attribute/object storage device '''
    def __init__(self, field=None, value=None):
        self.m_field = field
        self.m_value = value

    def __getattr__(self, key):
        '''
        What can we do here that runs the .get() command but -only- if the key
        does not exist.
        '''
        # super(ContainerField, self).__getattr__(key)

    def __call__(self):
        return self.get()

    def value(self):
        return self.m_value

    def setValue(self, value):
        self.m_value = value

    def _recurseSetAttr(self, attr, values):
        '''Generate our attributes/objects and store them succinctly.'''
        # Container
        #    \_Container
        #    \_Container
        #         \_Container...
        for field, value in values.items():
            if not hasattr(attr, field):
                setattr(attr,
                        field,
                        # field type is known from model caching
                        ContainerField(value=value, field=field_type(field)))

            fdbf = getattr(attr, field)
            if isinstance(value, dict):
                self._recurseSetAttr(fdbf, value)
            else:
                fdbf.setValue(value)

    def get(self):
        # Create the new object from scratch and proliferate it's
        # attributes recursively. 'values' come in the form of a
        # dictionary that we can then use to setattr().
        # So... Create container, set value, find keys for this
        # and create containers that hold the values of those keys
        # and repeate...
        self._recurseSetAttr(self, attr, values)

现在,在生成对象时,我可以看到类似这样的字典:{"myContainer" : { "id" : 2, "foo" : { "id" : 3, "bar" : 1 } }},一旦创建,就可以这样调用:myContainer.foo.id.value()

在场景中有self.m_field告诉应用程序对象的实际数据类型。这是关于Django模型的引用,但任何python都可以应用。

作为实例化的一部分,所有容器都将具有id(或pk)密钥。这是强制性的。

摩擦

理想情况下,我们会填充顶级属性,并且只有当用户请求其下方的属性时,我们才会根据id值和field类型构建它们。

最后,假设myContainer.foo.bar属性具有外键字段类型。如果我们调用myContainer.foo.bar.newkey.value()应用程序应该理解'newkey'属性不存在,请查询我们的django实例,将bar属性存储为现在填充更多的Container,并返回{{1已被存入记忆的价值。

Python陷阱

我希望它是一个简单的newkey,但Python似乎只使用hasattr()和默认的getattr()(递归是真的!)。我在使None工作时也遇到了麻烦。

在我写这篇文章时,我意识到由于依赖于try: except:getattr()的递归属性设置,可能会有多复杂。任何建议都会非常感激。 - 干杯

3 个答案:

答案 0 :(得分:1)

您可以考虑将@property装饰器与私有内部字段一起使用。这个想法是这样的:

class ContainerField(object):
    def __init__(self, field=None, value=None):
        self._m_field = field
        self._m_value = value

    @property
    def m_field(self):
        if self._m_field is None:
            self._m_field = self.function_to_populate_m_field()
        return self._m_field

    @property
    def m_value(self):
        if self._m_value is None:
            self._m_value = self.function_to_populate_m_value()
        return self._m_value

    ...

答案 1 :(得分:1)

所以回答问题的第一部分:如果仅在未定义属性时如何调用__getattr__ self.get()。 python类中有两个属性访问方法:__getattribute____getattr__。每次尝试进行属性查询时都会调用第一个,第二个仅在普通属性查找系统失败时调用(包括超类中的查找)。由于您定义的__getattr__仅在属性尚未存在时调用,因此您只需将其代理为对.get的调用即可。在遇到递归问题的情况下,如果您尝试在self内部查找__getattr__的另一个属性,该属性也不存在。避免这种情况的方法是获得需要特殊处理的密钥列表,并检查所请求的当前属性是否是其中之一。这通常仅在实施__getattribute__时才需要。

请注意,您的.get方法存在问题:attrvalues未定义。如果我知道__getattr__ .getattr想要什么值,我会在values中给出一个更具体的答案。

答案 2 :(得分:0)

检查出来:

class Test(object):
    def __init__(self):
        self.a = 5
        self.b = 6

    def __getattr__(self, key): 
        return 'created a new key: {}'.format(key)


obj = Test()

print(obj.a, obj.b)
print(obj.c)

此处,您不是返回'created a new key...',而是创建新属性并将其返回。