创建一个类级别的装饰器来自动添加属性

3 投票
2 回答
2083 浏览
提问于 2025-04-17 17:28

我想创建一个类级别的装饰器,这个装饰器可以自动为对象添加一个属性,同时包括合适的获取器和设置器方法,以及一个后备变量。举个例子:

@autoproperty("foo", can_get=True, can_set=True, allow_null=False, default_value=0)
@autoproperty("baz", can_get=True, can_set=False, allow_null=True, default_value=0)
@autoproperty("bar")
class SomeNonTrivialClass(object):
   def __init__(self):
      #lots of stuff going on here

   def discombobulate(self):
      #this is obviously a very trivial example
      local_foo = self.foo;

      if (local_foo > 10):
         raise RuntimeError("Foo can never be more than 10")
      else:
         #do whatever with foo

if __name__ == '__main__':
   test = SomeNonTrivialClass()

   test.foo = 5
   test.discombobulate()
   test.foo = 11
   test.discombobulate()

我发现自己经常需要创建很多“半复杂”的获取器和设置器(其实可以用简单的属性来实现,但它们需要默认值和空值保护)。我希望能有一个装饰器,能够帮我轻松创建这些属性,适用于类的新实例。

如果我的这个想法不太对,我也愿意接受其他可行的方法。

任何帮助都非常感谢。

我正在使用Python 3.X和Python 2.7,所以如果能在这两者上都能用就更好了,但不是必须的。

更新:我添加了一些我想要的更多细节。总的来说,我需要能够创建很多这样的简单自动属性(就像C#的自动属性,但需要更多灵活性)。我不一定想暴露后备存储,但我希望确保在检查实例化的对象(不一定是类)时,可以看到已经创建的属性。

2 个回答

1

这里有一种更直接的方法,它不需要给类加装饰器。

class SomeNonTrivialClass(object):
   def __init__(self):
      #lots of stuff going on here

   foo = autoproperty("foo", can_get=True, can_set=True, allow_null=False, default_value=0)

   def discombobulate(self):
      #this is obviously a very trivial example
      local_foo = self.foo;

class autoproperty(property):
    def __init__(self, name, can_get, can_set, allow_null, default_value):
        ...
8

下面这个类装饰器可以实现这个功能:

def autoproperty(name, can_get=True, can_set=True, allow_null=False, default_value=0):
    attribute_name = '_' + name
    def getter(self):
        return getattr(self, attribute_name, default_value)
    def setter(self, value):
        if not allow_null and value is None:
            raise ValueError('Cannot set {} to None'.format(name))
        setattr(self, attribute_name, value)

    prop = property(getter if can_get else None, setter if can_set else None)

    def decorator(cls):
        setattr(cls, name, prop)
        return cls

    return decorator

不过你也可以创建一个属性工厂:

def autoproperty(attribute_name, can_get=True, can_set=True, allow_null=False, default_value=0):
    def getter(self):
        return getattr(self, attribute_name, default_value)
    def setter(self, value):
        if not allow_null and value is None:
            raise ValueError('Cannot set {} to None'.format(name))
        setattr(self, attribute_name, value)
    return property(getter if can_get else None, setter if can_set else None)

然后在类中用以下方式设置:

class SomeNonTrivialClass(object):
    # ...

    foo = autoproperty('_foo', can_get=True, can_set=True, allow_null=False, default_value=0)

如果你需要创建多个属性(可能还会相互依赖),那么使用类装饰器会更合适。

撰写回答