2013-02-09 54 views
1

我成功地创建装饰任何类型的类一个装饰,增加了标准的接口,所有这些,为方便,集成等...装饰一堂课 - 装饰一次忘了吧?

我一直在使用元类的抵制,因为文学在这一点上说,这是一个矫枉过正的问题,大多数时候都可以被类装饰器所替代。我遇到的困难是:

def Decorator(somearg): 

    def wrapper(cls): 
     clsinit = cls.__init__ 
     cls.members = [] 

     def __init__(self, *args, **kwargs): 
      #do something with somearg... 
      self.__class__.members.append(self) 
      clsinit(self,*args,**kwargs) 

     cls.__init__ = clsinit 
     return cls 

    return wrapper 

@Decorator('thearg') 
class A(object): 
    pass 

a = A() 
b = A() 

使用python调试器,在导入时,类A立即用参数'thearg'装饰。但是每次我实例化A()时,实例都直接调用装饰器中定义的init,而不通过先前的层。这很好,因为我希望我的课程记录每个成员,并且不会在每次新实例实例化时都重置。但我不确定我明白为什么。

有人可以解释在这种特定情况下的python解释器的物理?

+1

您发布的代码有问题。它有一个SyntaxError,可能会触发'somearg'和'cls'的角色。它是你的真实代码的简化版本吗?请测试并修复。 – unutbu 2013-02-09 03:46:12

+0

我修复了代码 – 2013-02-09 09:25:08

+0

这实际上就是应该使用元类的东西。没有明确装饰每个子类,修饰器就无法处理它。 – 2013-02-09 19:17:46

回答

0

OK,指的是更正后的代码在你更新的问题,你问:

“但每次我实例A()时,实例调用直给init在装饰定义的 ,而不通过前 层”。

它这样做是因为当wrapper()类装饰功能,由Decorator()函数创建,适用于class A,它取代A__init__()有自己的动态定义wrapper.__init__()功能。 功能就是当实例化A时被调用的函数,它首先被写入做一些事情,然后在最后调用原始的A.__init__()

+0

我修复了代码 – 2013-02-09 09:25:30

2

如果你只是改变了类,而不是创建一个新的,没有必要为一个包装函数(本来应该是包装类):

def Decorator(cls): 
    clsinit = cls.__init__ 
    cls.members = [] 

    def __init__(self, *args, **kwargs): 
     clsinit(self, *args, **kwargs) 
     self.__class__.members.append(self) 

    cls.__init__ = __init__ 
    return cls 

BTW,你应该建立一个基类继承它:

class Base(object): 
    members = [] 
    def __init__(self): 
     self.__class__.members.append(self) 

class A(Base): 
    pass 

更清洁

+0

我在代码上犯了一个错误:我现在编辑它。我做了包装函数,因为我需要在装饰时使用参数来以某种方式来实例化咖啡。另外,我试图继承!但我的问题是,母类成为所有子类的容器,这是不受欢迎的行为。如果我装饰,所有装饰的课程成为他们自己的容器。 – 2013-02-09 08:55:17

0

感谢@JBernardo和@martineau的回答。我用这段代码的目标是实现类对象的容器类行为,以便更容易地访问它们的实例。但是我想一次访问相对于每个类的实例,而不是全部都在同一个继承类中。

实际工作的代码再次帮助我理解我的问题:

一)装饰被调用,其参数somearg

B)的下一行(A类的定义)的内容作为包装函数的参数。1)记住初始类init以避免无限递归 2)定义一个新的init来在实例级别进行更改 3)绑定类的新init方法 4)返回转换后的类对象

c)因为return是一个类对象,每次我实例化一个实例时,我会直接进入init,因为这是它在新类中的绑定方式。