2016-01-22 39 views
4

我有这个元类装饰我写申请一个装饰的实现问题:元类,其装饰全部采用类方法两种不同的装饰实现

def decorateAll(decorator): 
    class MetaClassDecorator(type): 

     def __new__(meta, classname, supers, classdict): 
      for name, elem in classdict.items(): 
       if type(elem) is FunctionType: 
        classdict[name] = decorator(classdict[name]) 
      return type.__new__(meta, classname, supers, classdict) 
    return MetaClassDecorator 

这是我所使用的类元类:

class Account(object, metaclass=decorateAll(Counter)): 

    def __init__(self, initial_amount): 
     self.amount = initial_amount 

    def withdraw(self, towithdraw): 
     self.amount -= towithdraw 

    def deposit(self, todeposit): 
     self.amount += todeposit 

    def balance(self): 
     return self.amount 

一切似乎作品伟大的,当我传递给装饰元类是这样实现的装饰:

def Counter(fun): 
    fun.count = 0 
    def wrapper(*args): 
     fun.count += 1 
     print("{0} Executed {1} times".format(fun.__name__, fun.count)) 
     return fun(*args) 
    return wrapper 

但是当我使用这种方式来实现一个装饰:

class Counter(): 

    def __init__(self, fun): 
     self.fun = fun 
     self.count = 0 

    def __call__(self, *args, **kwargs): 
     print("args:", self, *args, **kwargs) 
     self.count += 1 
     print("{0} Executed {1} times".format(self.fun.__name__, self.count)) 
     return self.fun(*args, **kwargs) 

我得到这个错误:

line 32, in __call__ 
return self.fun(*args, **kwargs) 
TypeError: __init__() missing 1 required positional argument: 'initial_amount' 

为什么?使用这两个装饰器实现与其他功能不会显示我的问题。我认为这个问题与我试图装饰的方法是类方法有关。我错过了什么吗?

回答

2

您需要实施Counter作为可调用描述符。当在描述符上执行__get__时,您会模拟将描述符绑定到传递给它的实例。 Plus以每个方法/对象为基础存储计数。

此代码:

import collections 
import functools 
import types 


def decorateAll(decorator): 
    class MetaClassDecorator(type): 

     def __new__(meta, classname, supers, classdict): 
      for name, elem in classdict.items(): 
       if type(elem) is types.FunctionType: 
        classdict[name] = decorator(classdict[name]) 
      return type.__new__(meta, classname, supers, classdict) 
    return MetaClassDecorator 


class Counter(object): 
    def __init__(self, fun): 
     self.fun = fun 
     self.cache = {None: self} 
     self.count = collections.defaultdict(int) 

    def __get__(self, obj, cls=None): 
     if obj is None: 
      return self 

     try: 
      return self.cache[obj] 
     except KeyError: 
      pass 

     print('Binding {} and {}'.format(self.fun, obj)) 
     cex = self.cache[obj] = functools.partial(self.__call__, obj) 
     return cex 

    def __call__(self, obj, *args, **kwargs): 
     print("args:", obj, *args, **kwargs) 
     self.count[obj] += 1 
     print("{0} Exec {1} times".format(self.fun.__name__, self.count[obj])) 
     return self.fun(obj, *args, **kwargs) 


class Account(object, metaclass=decorateAll(Counter)): 

    def __init__(self, initial_amount): 
     self.amount = initial_amount 

    def withdraw(self, towithdraw): 
     self.amount -= towithdraw 

    def deposit(self, todeposit): 
     self.amount += todeposit 

    def balance(self): 
     return self.amount 


a = Account(33.5) 

print(a.balance()) 

产生以下输出:

Binding <function Account.__init__ at 0x000002250BCD8B70> and <__main__.Account object at 0x000002250BCE8BE0> 
args: <__main__.Account object at 0x000002250BCE8BE0> 33.5 
__init__ Exec 1 times 
Binding <function Account.balance at 0x000002250BCD8D90> and <__main__.Account object at 0x000002250BCE8BE0> 
args: <__main__.Account object at 0x000002250BCE8BE0> 
balance Exec 1 times 
33.5 

它调用描述符的__call__方法,通过模拟的结合创建的对象存储在每个方法的计输入functools.partial

+0

您认为何时使用这种方法更方便? – Nikaidoh

+0

在你的情况下,这是适当的,因为你正在装饰正规**方法**(那些把'self'作为第一个参数,即:它们在实例化时与实例绑定)。另一种方法是返回包含对实例('obj')的引用并链接到描述符的另一个对象(而不是'functools.partial') – mementum