最近,我遇到了一个类似于这个问题的问题:
Accessing the class that owns a decorated method from the decorator
我的代表不够高,不能在那里发表评论,因此我开始提出一个新问题,以解决对该问题的答案的一些改进。
这就是我需要的:
def original_decorator(func):
# need to access class here
# for eg, to append the func itself to class variable "a", to register func
# or say, append func's default arg values to class variable "a"
return func
class A(object):
a=[]
@classmethod
@original_decorator
def some_method(self,a=5):
''' hello'''
print "Calling some_method"
@original_decorator
def some_method_2(self):
''' hello again'''
print "Calling some_method_2"
解决方案需要同时使用类方法和实例方法,从装饰器返回的方法应该工作并且如果它未修饰则表现相同,即方法签名应该是保留
该问题的accepted answer从装饰器返回了一个类,并且元类标识了该特定的类,并进行了"类访问"操作。
答案确实提到了一个粗略的解决方案,但显然它有一些警告:
我试图提出一个更好的解决方案,记录在答案中。
答案 0 :(得分:0)
以下是解决方案。
它使用装饰器(适用于"类访问"装饰器)和元类,这将满足我的所有要求和地址答案的问题。可能最好的优势是"类访问"装饰者可以只是访问该类,甚至不会触及元类。
# Using metaclass and decorator to allow class access during class creation time
# No method defined within the class should have "_process_meta" as arg
# Potential problems: Using closures, function.func_globals is read-only
from functools import partial
import inspect
class meta(type):
def __new__(cls, name, base, clsdict):
temp_cls = type.__new__(cls, name, base, clsdict)
methods = inspect.getmembers(temp_cls, inspect.ismethod)
for (method_name, method_obj) in methods:
tmp_spec = inspect.getargspec(method_obj)
if "__process_meta" in tmp_spec.args:
what_to_do, main_func = tmp_spec.defaults[:-1]
f = method_obj.im_func
f.func_code, f.func_defaults, f.func_dict, f.func_doc, f.func_name = main_func.func_code, main_func.func_defaults, main_func.func_dict, main_func.func_doc, main_func.func_name
mod_func = what_to_do(temp_cls, f)
f.func_code, f.func_defaults, f.func_dict, f.func_doc, f.func_name = mod_func.func_code, mod_func.func_defaults, mod_func.func_dict, mod_func.func_doc, mod_func.func_name
return temp_cls
def do_it(what_to_do, main_func=None):
if main_func is None:
return partial(do_it, what_to_do)
def whatever(what_to_do=what_to_do, main_func=main_func, __process_meta=True):
pass
return whatever
def original_classmethod_decorator(cls, func):
# cls => class of the method
# appends default arg values to class variable "a"
func_defaults = inspect.getargspec(func).defaults
cls.a.append(func_defaults)
func.__doc__ = "This is a class method"
print "Calling original classmethod decorator"
return func
def original_method_decorator(cls, func):
func_defaults = inspect.getargspec(func).defaults
cls.a.append(func_defaults)
func.__doc__ = "This is a instance method" # Can change func properties
print "Calling original method decorator"
return func
class A(object):
__metaclass__ = meta
a = []
@classmethod
@do_it(original_classmethod_decorator)
def some_method(cls, x=1):
''' hello'''
print "Calling original class method"
@do_it(original_method_decorator)
def some_method_2(self, y=2):
''' hello again'''
print "Calling original method"
# signature preserved
print(inspect.getargspec(A.some_method))
print(inspect.getargspec(A.some_method_2))
对这种方法是否有任何内容持开放态度。