I have a class consisting of a "list" of static methods, A
. I want to change its behavior with a class-decorator, Meta
, which acts on a specific static method, in this example content
, by performing the method m
.
My original attempt, CASE=2
, didn't work as expected, so I started I case study. I introduced a new class B
, which has slightly different implementation of an other method, info
but raised a funny error, and a new class C
just without the method, info
.
d[i] = classmethod(lambda cls, *args: mcs.m( getattr(target_cls, i)(*args)) )
it doesn't work properly, maybe too many nested dynamic expressions?
o = getattr(target_cls, i)
d[i] = classmethod(lambda cls, *args: mcs.m(o(*args)))
Here the code
class Meta:
def __new__(mcs, target_cls):
if CASE == 1:
print('case 1')
d = {}
for i in dir(target_cls):
if i == 'content':
o = getattr(target_cls, i)
d[i] = classmethod(lambda cls, *args: mcs.m(o(*args)))
if CASE == 2:
print('case 2')
d = {}
for i in dir(target_cls):
if i == 'content':
d[i] = classmethod(lambda cls, *args: mcs.m( getattr(target_cls, i)(*args)) )
return type('AAA', (target_cls,), d)
@classmethod
def m(mcs, p):
return '--> ', p
class A:
@staticmethod
def content(response):
return 'static_method', response
@staticmethod
def info(response):
return response
class B:
@staticmethod
def content(response):
return 'static_method', response
@staticmethod
def info(response):
response.sort()
return response
class C:
@staticmethod
def content(response):
return 'static_method', response
# call the "content" class-method of each class for all different cases
for cls in (A, B, C):
print(cls.__name__)
for case in range(1,3):
CASE = case
R = Meta(cls)
try:
print(R.content('ppp'))
except Exception as e: print(e)
print()
Output
A
case 1
('--> ', ('static_method', 'ppp'))
case 2
('--> ', 'ppp') # no decoration
B
case 1
('--> ', ('static_method', 'ppp'))
case 2
'str' object has no attribute 'sort' # <- complained about the other method
C # <- this is ok BUT I removed the other method!
case 1
('--> ', ('static_method', 'ppp'))
case 2
('--> ', ('static_method', 'ppp')) # <- here the decoration took place
The question is why case 2 doesn't work, if it is a limitation of the language then of what kind?
Extra question: how to explain the error of class B
case 2
I guess that the issue is caused by the loop and the origin is the fact that each statement has not its own scope (in the loop). By passing i
as a key parameter of the lambda
fixed the problem.
class Meta:
def __new__(mcs, target_cls):
d = {}
for i in dir(target_cls):
if i == 'content':
d[i] = classmethod(lambda cls, *args, m_name=i: mcs.m( getattr(target_cls, m_name)(*args)) )
return type('AAA', (target_cls,), d)
@classmethod
def m(mcs, p):
return '--> ', p
class A:
@staticmethod
def content(response):
return 'static_method', response
@staticmethod
def info(response):
return response
print(A.content)
print(Meta(A).content)
print(Meta(A).content('a'))
print(Meta(A).info)
Output
<function A.content at 0x7f04500740d0> # original static method
<bound method Meta.__new__.<locals>.<lambda> of <class '__main__.AAA'>> # class method
('--> ', ('static_method', 'a'))
<function A.info at 0x7f0450074040>