【问题标题】:Better solution to access class that owns the method from a method decorator从方法装饰器访问拥有该方法的类的更好解决方案
【发布时间】:2014-06-04 09:39:19
【问题描述】:

最近遇到一个和这个问题类似的问题:

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 从装饰器返回一个类,元类识别该特定类,并执行“类访问”操作。

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

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

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

【问题讨论】:

    标签: python python-2.7 python-3.x


    【解决方案1】:

    这是解决方案。

    它使用 装饰器(适用于“类访问”装饰器)元类,这将满足我的所有要求并解决该答案的问题.可能最大的优势是“类访问”装饰器可以访问该类,甚至无需接触元类。

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

    欢迎就这种方法是否有任何限制提出建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多