将@property与另一个装饰器结合起来

时间:2017-11-02 02:39:32

标签: python python-3.x properties python-decorators

我有这些与@property

一起使用的装饰器
def android(**params):
    def deco(f):
        def wrap(*args, **kwargs):
            if is_android:
                return params
            return f()
        return wrap
    return deco

def ios(**params):
    def deco(f):
        def wrap(*args, **kwargs):
            if not is_android:
                return params
            return f()
        return wrap
    return deco

定义了类:

class Foo:
    @property
    @android(foo=1)
    @ios(foo=2)
    def foo(self):
        pass

f = Foo()
print(f.foo)

每次我想使用时,我都要一次又一次地重复@property。有没有办法将@property合并到@android@ios?我的意思是只需使用@android@ios而不@property,但我可以致电f.foo

2 个答案:

答案 0 :(得分:1)

您可以修改装饰器以返回property个对象。但是,除非您只想为它们支持单个特定的顺序,否则您需要使它们的逻辑更复杂,以便它们即使应用它们的“函数”已经是属性对象也能工作。这是我认为应该做的一个未经测试的装饰器更新:

def android(**params):
    def deco(f):
        if isinstance(f, property):
            prop = f.getter
            f = f.fget
        else:
            prop = property
        def wrap(*args, **kwargs):
            if is_android:
                return params
            return f()
        return prop(wrap)
    return deco

但我不确定这是不是最好的方法。您的代码不会使用您装饰的foo函数。其中一个装饰者总是拦截这个电话。如果您的真实代码是真的,我建议编写您自己的描述符类型,而不是打扰property或装饰器。这是一个相当简单的自定义描述符示例,它将值在android和IOS中作为参数返回(并且它在类中使用):

class MyDescriptor:
    def __init__(self, android_value, ios_value):
        self.android_value = android_value
        self.ios_value = ios_value

    def __get__(self, instance, owner)
        if is_android:
            return self.android_value
        else:
            return self.ios_value

    def __set__(self, instance, value):
        raise NotImplementedError()

class Foo:
    foo = MyDescriptor({"foo": 1}, {"foo": 2})

如果is_android值是常量,并且在定义类时您将知道其值,您甚至可以进一步简化事物。在这种情况下,您可以只分配一个普通的类变量,使用条件逻辑根据需要修改值:

class Foo:
    foo = {"foo": 1 if is_android else 2}

答案 1 :(得分:1)

我认为您需要使用类装饰器来修改您的类,使属性成为类字段。

is_android = True


def android(**params):
    def deco(f):
        def wrap(*args, **kwargs):
            if is_android:
                return params
            return f()

        wrap.__decorated_by_mobile__ = True
        return wrap

    return deco


def ios(**params):
    def deco(f):
        f.wrapped = True

        def wrap(*args, **kwargs):
            if not is_android:
                return params
            return f()

        wrap.__decorated_by_mobile__ = True
        return wrap

    return deco


def class_deco(cls):
    for name, method in cls.__dict__.items():
        if callable(method):
            if hasattr(method, '__decorated_by_mobile__'):
                setattr(cls, name, method())
    return cls


@class_deco
class Foo:
    @android(a=1)
    @ios(a=2)
    def foo(self):
        pass


f = Foo()
print(f.foo) 

希望这是你正在寻找的东西:)