首页 > 解决方案 > 是否有等效于属性的部分方法?

问题描述

我有一个包含许多参数的类,这些参数存储在字典中,如下所示(实际上,它有点复杂,但这给出了一个好主意)。我可以使用以下方法“自动生成”获取和设置方法partialmethod

for a_name in ['x', 'y', 'z']:
  locals()["get_" + a_name] = partialmethod(_get_arg, 
                                            arg_name=a_name) 
  locals()["set_" + a_name] = partialmethod(_set_arg, 
                                            arg_name=a_name) 

理想情况下,我想使用@property,但前提是我可以“自动生成@property@setter@deleter. 这有没有可能?第二个片段显示了“手动”添加的属性;我正在研究使用等价物partialmethod来避免重复和不可维护的代码。

class C:
    def __init__(self):
        self.kwargs = {'x' : 0, 'y' : 3, 'z' : True}

    def __get_arg(self, arg_name):
        assert arg_name in self.kwargs
        return self.kwargs[arg_name]

    def __set_arg(self, arg_name, value):
        assert arg_name in self.kwargs
        self.kwargs[arg_name] = value

    def __del_arg(self, arg_name):
        assert arg_name in self.kwargs
        del self.kwargs[arg_name]

    @property
    def x(self):
        return self.__get_arg('x')

    @x.setter
    def x(self, value):
        self.__set_arg('x', value)

    @x.deleter
    def x(self):
        self.__del_arg('x')

    @property
    def y(self):
        return self.__get_arg('y')

    @y.setter
    def y(self, value):
        self.__set_arg('y', value)

    @y.deleter
    def y(self):
        self.__del_arg('y')

    @property
    def z(self):
        return self.__get_arg('z')

    @x.setter
    def z(self, value):
        self.__set_arg('z', value)

    @x.deleter
    def z(self):
        self.__del_arg('z')


c = C()
c.x = 'foo'  # setter called
foo = c.x    # getter called
del c.x      # deleter called

标签: pythonpropertiespython-descriptors

解决方案


对于您的特定示例,执行此操作的更简单方法可能是子类 dict化然后定义魔术方法__getattr__, __setattr__,并且 __delattr__在属性不存在时分别调用这些方法。然后你可以调用super()dict类继承行为。

class AttrDict(dict):
    attrs = ("x", "y", "z")
    def __getattr__(self, name):
        if not name in self.attrs:
            raise AttributeError(name)

        try:
            return super().__getitem__(name)
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if not name in self.attrs:
            raise AttributeError(name)

        try:
            return super().__setitem__(name, value)
        except KeyError:
            raise AttributeError(name)

    def __delattr__(self, name):
        if not name in self.attrs:
            raise AttributeError(name)

        try:
            return super().__delitem__(name)
        except KeyError:
            raise AttributeError(name)
>>> obj = AttrDict(x=24)
>>> obj.x
24

>>> obj.a = 1
AttributeError: a

>>> obj.y = 25
>>> obj.y
25

>>> del obj.y
>>> obj.y
AttributeError: y

>>> obj.kwargs["z"] = 26
>>> obj.z
26

如果您不关心限制特定属性,那就更容易了。您可以简单地将每个__*attr__方法分配给其对应的 dict.__*item__方法,这就是订阅操作所调用的方法。(即obj[key]

class AttrDict(dict):
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
>>> obj = AttrDict({"a": 1})
>>> obj.a
1

>>> obj["b"] = 2
>>> obj.b
2

>>> del obj.b
>>> obj.b
KeyError

推荐阅读