通过__setattr__分配时,Python属性设置不会分配正确的实例属性(前导下划线)

时间:2018-03-23 22:03:06

标签: python

在工厂方法中通过setattr设置属性时,我无法设置实例的正确属性。

给出以下代码,其中data是一个简单的dict,包含例如{ "age": "64", ...}

def factory(data):
    obj = MyClass()
    for k, v in data.items():
        setattr(obj, k, v)
    return obj

class MyClass(object):

    def __init__(self):

        self._age = None
        # more...


    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        some_validation(value)
        self._age = value


    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)

    def __getitem__(self, item):
        return self.__dict__.get(item, None)

    def __getattr__(self, item):
        self.__dict__[item] = None
        return None

    def __str__(self):
        return json.dumps(self, default=lambda o: o.__dict__)


c = factory(data)
print(c)

打印创建的对象时,我总是得到以下输出:

{"_age": "64", ...}

但我需要

{"age": "64", ...}

为什么setattr方法会指定前导下划线?

1 个答案:

答案 0 :(得分:2)

你想要实现的一些事情变得混乱,比如想要为可读表示打印__dict__,而是使用属性的私有属性。让我们从头开始,看看我们如何正确地实现你的课程。

您正在尝试实现一个类,可以将这些属性作为键和属性进行访问。这很好,可以用更简洁的方式完成。

class MyClass:
    ...

    def __getitem__(self, item):
        return self.__getattribute__(item)

    def __setitem__(self, key, value):
        return self.__setattr__(key, value)

当属性不存在时,您还希望返回None__getattr__涵盖了这一点,当属性不存在时,就会完全调用它。

    def __getattr__(self, _):
        return None

然后,您想要使用property为某些属性添加一些验证。这确实是正确的方法。

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        # some validation here
        self._age = value

最后,您希望能够为您的实例提供一个很好的字符串表示形式。我们必须要小心,因为我们必须添加一些我们不想打印的私有属性。

我们要做的是实施方法keys以允许转换为dict。此方法仅返回非私有属性或方法的键。

    def keys(self):
        return [k for k in dir(self) if not k.startswith('_') and not callable(self[k])]

    def __str__(self):
        return json.dumps(dict(self))

这是正确的。

obj = MyClass()

obj.age = 3

print(obj)
# prints: {"age": 3}