如何制作装饰器来定义类的必需属性?

时间:2017-11-07 06:03:17

标签: python python-3.6 python-decorators

我试着写一个装饰器(关闭内存,原谅代码中的任何问题):

def required(fn):
    def wrapped(self):
        self.required_attributes += [fn.__name__]
        fn(self)

    return wrapped

我用它来装饰类中的@property属性,例如:

@property
@required
def some_property(self):
    return self._some_property

......所以我可以这样做:

def validate_required_attributes(instance):
    for attribute in instance.required_attributes:
        if not hasattr(instance, attribute):
            raise ValueError(f"Required attribute {attribute} was not set!")

现在我忘了这不会起作用,因为为了使用属性的名称更新required_attributes,我必须首先检索属性。所以本质上,当我在类中执行 init 时,我可以只做一个self.propertyname来添加它...但是这个解决方案根本不好,我不妨创建一个列表 init 中必需的属性名称。

据我所知,装饰器是在编译时应用的,所以在定义包装函数之前我无法修改required_attributes。有没有其他方法可以让这项工作?我只想要一个漂亮,优雅的解决方案。

谢谢!

2 个答案:

答案 0 :(得分:0)

我认为attrs library可以满足您的需求。您可以定义这样的类,其中xy是必需的,z是可选的。

from attr import attrs, attrib

@attrs
class MyClass:
    x = attrib()
    y = attrib()
    z = attrib(default=0)

测试出来:

>>> instance = MyClass(1, 2)
>>> print(instance)
MyClass(x=1, y=2, z=0)

答案 1 :(得分:0)

这是我用类装饰器和方法装饰器做的事情。使用元类可能是一种更好的方法(很好的是API而不是实现;)。)

def requiredproperty(f):
    setattr(f, "_required", True)
    return property(f)


def hasrequiredprops(cls):
    props = [x for x in cls.__dict__.items() if isinstance(x[1], property)]
    cls._required_props = {k for k, v in props if v.fget._required}
    return cls


@hasrequiredprops
class A(object):

    def __init__(self):
        self._my_prop = 1

    def validate(self):
        print("required attributes are", ",".join(self._required_props))

    @requiredproperty
    def my_prop(self):
        return self._my_prop

这应该使验证成功,而不需要调用者首先触摸属性:

>>> a = A()
>>> a.validate()
required attributes are my_prop
>>> a.my_prop
1

类装饰器需要确保它具有实例化所需的属性名称。 requiredproperty函数只是一种根据需要标记属性的方法。

话虽如此,我并不完全确定你在这里想要实现的目标。也许验证属性应返回的实例属性值?