【问题标题】:Combine @property with another decorator将@property 与另一个装饰器结合使用
【发布时间】:2018-04-14 11:18:41
【问题描述】:

我有这些与 @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

【问题讨论】:

  • 你确定这些工作完全符合预期吗?缩进似乎关闭并且它不返回内部函数......在我的计算机上抛出TypeError: 'NoneType' object is not callable
  • 我稍微编辑了代码。在我的电脑上,代码有效。
  • 如果它有效,它究竟返回了什么?

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


【解决方案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) 

希望这就是你要找的:)

【讨论】:

    【解决方案2】:

    您可以修改装饰器以返回 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}
    

    【讨论】:

    • 您的解决方案是我之前所做的。我定义了一个具有 android 和 ios 参数的函数。然后返回。然而,它很难阅读。这就是为什么我想出@property
    猜你喜欢
    • 1970-01-01
    • 2020-12-14
    • 2021-01-06
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 2016-05-03
    • 1970-01-01
    • 2015-02-22
    相关资源
    最近更新 更多