从方法装饰器访问拥有该方法的类的更好解决方案



最近,我遇到了一个类似的问题:

从装饰器中访问拥有被装饰方法的类

我的代表不够高,不能在那里评论,所以我开始一个新的问题来解决一些改进这个问题的答案。

这就是我需要的:

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"

解决方案需要与类方法和实例方法一起工作,从装饰器返回的方法应该以未修饰的方式工作和行为,即方法签名应该保留

这个问题的可接受答案是从装饰器返回一个Class,元类标识这个特定的Class,并执行"类访问"操作。

答案确实提到自己是一个粗略的解决方案,但显然它有一些注意事项:

  1. 装饰器返回了一个不可调用的类。显然,它可以很容易地被调用,但是返回值仍然是一个类——它在调用时的行为方式是一样的,但是它的属性和行为是不同的。从本质上讲,它的工作方式与未修饰的方法不同。
  2. 它强制装饰器返回一个自定义类型的类,并且所有的"类访问"代码都直接放在元类中。这是不太好的,编写装饰器不应该强制直接触摸元类。

我试图想出一个更好的解决方案,在答案中记录。

这是解决方案。

它使用装饰器(它将用于"类访问"装饰器)一个元类,这将满足我的所有需求并解决该答案的问题。可能最好的优点是,"类访问"修饰符可以直接访问类,甚至不需要触及元类。

# 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))

对这种方法是否有任何缺陷的建议持开放态度。

最新更新