簡體   English   中英

python類屬性的全局裝飾器

[英]python global decorator for class attributes

下面是一段抽象的代碼,它簡化了我遇到的問題。 在此示例中,我有一個具有登錄和注銷屬性的程序。 登錄名與版本無關,而注銷則與版本有關。

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]

那顯然是行不通的。 這有可能嗎?

手動解決方案

看起來像是一個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

每次創建A的新實例時,“自動解決方案1”都會重新定義屬性。 該解決方案避免了這種情況,但是涉及更多。 它利用了一個類裝飾器。

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'

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM