1

我试图创建动态对象来验证我的配置并将结果呈现为对象。我试图通过创建这样的类来实现这一点:

class SubConfig(object):
    def __init__(self, config, key_types):
        self.__config = config
        self.__values = {}
        self.__key_types = key_types

    def __getattr__(self, item):
        if item in self.__key_types:
            return self.__values[item] or None
        else:
            raise ValueError("No such item to get from config")

    def __setattr__(self, item, value):
        if self.__config._blocked:
            raise ValueError("Can't change values after service has started")

        if item in self.__key_types:
            if type(value) in self.__key_types[item]:
                self.__values[item] = value
            else:
                raise ValueError("Can't assing value in different type then declared!")
        else:
            raise ValueError("No such item to set in config")

SubConfig 是配置文件中部分的包装器。Config 可以在程序启动后取消更改值的可能性(您只能在初始化时更改值)。

问题是当我设置任何值时,它会在 getattr 中进入无限循环。正如我所读__getattr__的那样(首先获取现有的 attr,然后调用__getattr__)。我正在将我的代码与可用示例进行比较,但我什么也得不到。

我注意到所有问题都是我的构造函数产生的。

4

2 回答 2

3

问题是您的构造函数在初始化对象时调用__setattr__,然后调用,__getattr__因为__私有成员尚未初始化。

我可以想到两种方法来解决这个问题:

一种选择是调用 downobject.__setattr__从而避免您__setattr__或等效地使用super(SubConfig, self).__setattr__(...)in __init__。您也可以self.__dict__直接设置值。这里的一个问题是,因为您使用的是双下划线,所以您必须手动修改属性名称(因此'__config'变为'_SubConfig__config'):

def __init__(self, config, key_types):
    super(SubConfig, self).__setattr__('_SubConfig__config', config)
    super(SubConfig, self).__setattr__('_SubConfig__values', {})
    super(SubConfig, self).__setattr__('_SubConfig__key_types', key_types)

另一种方法是__setattr__检测并通过访问以_ie开头的属性名称

if item.startswith('_')
    return super(SubConfig, self).__setattr__(item, value)

这更像是 Pythonic,因为如果有人有充分的理由访问您的对象的内部,您没有理由尝试阻止他们。

于 2012-06-26T11:50:45.060 回答
2

Cf ecatmur 对根本原因的回答——记住这__setattr__不是对称的__getattr__——每次尝试绑定对象的属性时都会无条件地调用它。覆盖__setattr__很棘手,如果您不清楚利弊,则不应这样做。

现在为您的用例提供一个简单实用的解决方案:重写您的初始化程序以避免触发setattr调用:

class SubConfig(object):
    def __init__(self, config, key_types):
        self.__dict__.update(
            _SubConfig__config=config,
            _SubConfig__values={},
            _SubConfig__key_types=key_types
            )

请注意,我重命名了您的属性以模拟使用双前导下划线命名方案时发生的名称修改。

于 2012-06-26T12:19:25.593 回答