Below is an abstracted piece of code that simplifies an issue that I have. In this example, I have a program that has a login and logout attributes. The login is version-independent and logout is version-dependent.
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
is an external interface that performs the actual action, and it should receive a string. The do
function cannot be altered. The version can and will change at run-time, so I was looking for some sort of global decorator/property that will call a function when the decorated attribute is accessed. The objective is to select the correct string per version before it is sent to executor.do
.
The obvious answer is to change the perform
function to executer.do(self.logout[self.version])
, but self.login
and self.logout
should not be accessed differently. There are inheritances in which self.logout
is only a string, and perform
is shared.
I was thinking of something like:
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]
That obviously doesn't work. Is this even possible?
Looks like a use case for a property
decorator:
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]
Now:
>>> a = A()
>>> a.login
'logged in'
>>> a.logout
'logged out'
>>> a.version = '2.0'
>>> a.logout
'logged out 2.0'
If you have lot such properties, you can automate a bit:
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]))
Now:
>>> a = A()
>>> a.login_message
'hello logger'
>>> a.logout
'goodbye logger'
>>> a.version = '2.0'
>>> a.logout
'goodbye logger 2.0'
The "Automated Solution 1" redefines the properties every time you make a new instance of A
. This solutions avoids this but is bit more involved. It makes use of a class decorator.
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'
Now:
>>> a = A()
>>> a.logout
'goodbye logger'
>>> a.version = '2.0'
>>> a.logout
'goodbye logger 2.0'
You say that " self.login
and self.logout
should not be accessed differently. The code below keeps the self.logout
dictionary but renames it to self.logouts
so that we can access it as a property. Similar remarks apply to self.logout_message
.
This code runs on Python 2 or 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)
output
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'
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.