类属性的python全局装饰器

时间:2016-05-15 07:37:31

标签: python python-3.x properties decorator

下面是一段抽象的代码,简化了我遇到的问题。 在这个例子中,我有一个具有登录和注销属性的程序。 登录与版本无关,注销与版本无关。

class A(class):
    def __init__(self):
        self.version = "1.0"

        self.login = "logged in"
        self.login_message = "hello logger"
        self.logout = {"1.0": "logged out",
                       "2.0": "logged out 2.0"}
        self.logout_message = {"1.0": "goodbye logger",
                               "2.0": "goodbye logger 2.0"}

    def perform(self, executor):
        executor.do(self.login)
        executor.do(self.logout)

executor是一个执行实际操作的外部接口,它应该接收一个字符串。 do函数无法更改。 该版本可以并且将在运行时更改,因此我正在寻找某种全局装饰器/属性,它将在访问装饰属性时调用函数。目标是在每个版本发送到executor.do之前选择正确的字符串。

显而易见的答案是将perform功能更改为executer.do(self.logout[self.version]),但不应以不同方式访问self.loginself.logout。遗传中self.logout只是一个字符串,perform是共享的。

我想的是:

self.version = "1.0"

self.login = "logged in"        
self.login_message = "hello logger"
@by_version
self.logout = {"1.0": "logged out",
               "2.0": "logged out 2.0"}
@by_version
self.logout_message = {"1.0": "goodbye logger",
                       "2.0": "goodbye logger 2.0"}

def by_version(self, attribute):
    return attribute[self.version]

这显然不起作用。 这甚至可能吗?

2 个答案:

答案 0 :(得分:4)

手动解决方案

看起来像property装饰器的用例:

class A(object):
    def __init__(self):
        self.version = "1.0"

        self.login = "logged in"
        self.login_message = "hello logger"

    @property    
    def logout(self):
        return {"1.0": "logged out", "2.0": "logged out 2.0"}[self.version]

    @property    
    def logout_message(self):
        return {"1.0": "goodbye logger", "2.0": "goodbye logger 2.0"}[self.version]

现在:

>>> a = A()
>>> a.login
'logged in'
>>> a.logout
'logged out'
>>> a.version = '2.0'
>>> a.logout
'logged out 2.0'     

自动化解决方案1 ​​

如果你有很多这样的属性,你可以自动化一下:

class A(object):
    def __init__(self):
        self.version = '1.0'
        self.login = 'logged in'
        self.login_message = 'hello logger'
        property_attrs = {'logout': {'1.0': 'logged out', 
                                     '2.0': 'logged out 2.0'},
                          'logout_message': {'1.0': 'goodbye logger',
                                             '2.0': 'goodbye logger 2.0'}}
        for name, value in property_attrs.items():
            setattr(self.__class__, name, property(lambda x: value[x.version]))

现在:

>>> a = A()
>>> a.login_message
'hello logger'
>>> a.logout
'goodbye logger'
>>> a.version = '2.0'
>>> a.logout
'goodbye logger 2.0'

自动化解决方案2

"自动化解决方案1"每次制作时都重新定义属性 A的新实例。这种解决方案避免了这种情况,但更多涉及。 它使用了类装饰器。

property_attrs = {'logout': {'1.0': 'logged out', '2.0': 'logged out 2.0'},
                  'logout_message': {'1.0': 'goodbye logger', '2.0': 'goodbye logger 2.0'}}

def add_properties(property_attrs):
    def decorate(cls):
        for name, value in property_attrs.items():
            setattr(cls, name, property(lambda self: value[self.version]))
        return cls
    return decorate

@add_properties(property_attrs)
class A(object):
    def __init__(self):
        self.version = '1.0'
        self.login = 'logged in'
        self.login_message = 'hello logger'

现在:

>>> a = A()
>>> a.logout
'goodbye logger'
>>> a.version = '2.0'
>>> a.logout
'goodbye logger 2.0'

答案 1 :(得分:0)

您说" self.loginself.logout不应以不同方式访问。下面的代码保留self.logout字典,但将其重命名为self.logouts,以便我们可以将其作为属性进行访问。类似的评论适用于self.logout_message

此代码在Python 2或3上运行。

from __future__ import print_function

class Executor(object):
    def do(self, s):
        print('Executing %r' % s)


class A(object):
    def __init__(self, version="1.0"):
        self.version = version

        self.login = "logged in"
        self.login_message = "hello logger"
        self.logouts = {
            "1.0": "logged out",
            "2.0": "logged out 2.0",
        }
        self.logout_messages = {
            "1.0": "goodbye logger",
            "2.0": "goodbye logger 2.0",
        }

    @property
    def logout(self):
        return self.logouts[self.version]

    @property
    def logout_message(self):
        return self.logout_messages[self.version]

    def perform(self, executor):
        executor.do(self.login)
        executor.do(self.logout)

executor = Executor()
executor.do('Tests')

#Test

a = A()
a.perform(executor)
print('msg', a.logout)
a.version = "2.0"
a.perform(executor)
print('msg', a.logout)
print()

b = A("2.0")
b.perform(executor)
print('msg', b.logout)
b.version = "3.0"
b.perform(executor)

<强>输出

Executing 'Tests'
Executing 'logged in'
Executing 'logged out'
msg logged out
Executing 'logged in'
Executing 'logged out 2.0'
msg logged out 2.0

Executing 'logged in'
Executing 'logged out 2.0'
msg logged out 2.0
Executing 'logged in'
Traceback (most recent call last):
  File "./qtest.py", line 69, in <module>
    b.perform(executor)
  File "./qtest.py", line 50, in perform
    executor.do(self.logout)
  File "./qtest.py", line 42, in logout
    return self.logouts[self.version]
KeyError: '3.0'
相关问题