【问题标题】:python global decorator for class attributes用于类属性的python全局装饰器
【发布时间】:2016-09-11 04:01:08
【问题描述】:

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

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]

这显然行不通。 这甚至可能吗?

【问题讨论】:

    标签: python python-3.x properties decorator


    【解决方案1】:

    手动解决方案

    看起来像 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'
    

    【讨论】:

    • 忍者又来了。哦,好吧,我不妨发布我的代码。 :)
    • 那行得通。谢谢!但是,我正在考虑一个全局函数,该函数将在任何访问装饰属性(很像 getter)之前被调用,然后返回适当的值。属性装饰器可以接受一个属性作为参数吗?这样它将是全局的,可以重用。如果没有这个选项,我会接受这个答案。
    • “重用”是什么意思?我们这里只有一节课。你想在其他课程中做同样的事情吗?仅仅从A 继承应该会给你相当多的重用。
    • 我的意思是我必须为每个属性创建@property def <name>(self): return {parameters}[self.version]。有很多类似self.logout 的属性,所以我在徘徊,如果有一个全局getter 将接受一个属性(如self.logout)并根据self.version 返回适当的字符串。很像我的def by_version(self, attribute): 示例,并用上面的@by_version 装饰属性。这可能是不可能的,只是确保。
    • 添加了从字典生成属性的版本。
    【解决方案2】:

    您说“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'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-27
      • 2016-07-30
      • 2014-02-08
      • 2020-10-07
      • 2014-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多