2012-12-31 105 views
0

我有代码中的所有对象从基础对象下降,我不打算直接实例化。在我的基础对象的__init__()方法中,我试图执行一些魔术 - 我试图装饰或换行,初始化对象的每个方法。但是当我调用结果方法时,我得到的结果令我困惑。下面是示例代码隔离问题:Python - 为什么当我检查到我返回的对象不是NoneType时,此方法返回NoneType?

class ParentObject(object): 
    def __init__(self): 
     self._adjust_methods(self.__class__) 

    def _adjust_methods(self, cls): 
     for attr, val in cls.__dict__.iteritems(): 
      if callable(val) and not attr.startswith("_"): 
       setattr(cls, attr, self._smile_warmly(val)) 
     bases = cls.__bases__ 
     for base in bases: 
      if base.__name__ != 'object': 
       self._adjust_methods(base) 

    def _smile_warmly(self, the_method): 
     def _wrapped(cls, *args, **kwargs): 
      print "\n-smile_warmly - " +cls.__name__ 
      the_method(self, *args, **kwargs) 
     cmethod_wrapped = classmethod(_wrapped) 
     return cmethod_wrapped 

class SonObject(ParentObject): 
    def hello_son(self): 
     print "hello son" 

    def get_sister(self): 
     sis = DaughterObject() 
     print type(sis) 
     return sis 

class DaughterObject(ParentObject): 
    def hello_daughter(self): 
     print "hello daughter" 

    def get_brother(self): 
     bro = SonObject() 
     print type(bro) 
     return bro 

if __name__ == '__main__': 
    son = SonObject() 
    son.hello_son() 

    daughter = DaughterObject() 
    daughter.hello_daughter() 

    sis = son.get_sister() 
    print type(sis) 
    sis.hello_daughter() 

    bro = sis.get_brother() 
    print type(bro) 
    bro.hello_son() 

程序崩溃,但是 - 线sis = son.get_sister()产生具有类型NoneType的sis对象。这里是输出:

-smile_warmly - SonObject 
hello son 

-smile_warmly - DaughterObject 
hello daughter 

-smile_warmly - SonObject 
<class '__main__.DaughterObject'> 
<type 'NoneType'> 
Traceback (most recent call last): 
    File "metaclass_decoration_test.py", line 48, in <module> 
    sis.hello_daughter() 
AttributeError: 'NoneType' object has no attribute 'hello_daughter' 

这是怎么发生的?

回答

3

尝试改变:

def _wrapped(cls, *args, **kwargs): 
     print "\n-smile_warmly - " +cls.__name__ 
     the_method(self, *args, **kwargs) 

def _wrapped(cls, *args, **kwargs): 
     print "\n-smile_warmly - " +cls.__name__ 
     return the_method(self, *args, **kwargs) 

_wrapped方法调用被包装的方法,而不是返回该方法的返回值。

+0

...相反,它实际上是返回'无',因为它没有'return '声明。 – martineau

2

好了,我真的不希望,甚至触摸,在该代码是怎么回事疯狂,但你的错误特别是因为你的“装饰”是不是从包装的函数返回任何东西:

def _smile_warmly(self, the_method): 
    def _wrapped(cls, *args, **kwargs): 
     print "\n-smile_warmly - " +cls.__name__ 
     return the_method(self, *args, **kwargs) # return here 
    cmethod_wrapped = classmethod(_wrapped) 
    return cmethod_wrapped 
1

问题是你正在包装所有你的课程的方法,包括get_sister。您可以像@Paul McGuire建议的那样去做,并将return添加到包装中,但这意味着您打电话给son.get_sister时会显示“微笑”消息,这可能不是您想要的。

您可能需要做的是在_adjust_methods内部添加一些逻辑来准确决定要包装哪些方法。您不必检查callablenot startswith('_'),您可以使用某种命名约定来处理您或您不想使用smile行为进行换行的命名约定。然而,你做这件事越多,自动装饰越少,相比之下,只是手动装饰你想装饰的方法。这很难理解为什么你要使用你显然想要使用的结构(所有的classmethods,包装所有的东西等等)。也许如果你解释了你的最终目标是什么,那么有人可以提出一个更直接的设计。

而且,即使你加return或包装用额外的逻辑,你仍然有我在你的其他问题中提到的问题:因为你在做__init__包装,它会发生一次实例化时一个类,所以你将继续添加越来越多的包装。这就是为什么我在那里建议你应该使用类装饰器,或者如果你必须使用元类。使用__init__中的类属性(包括方法)不是一个好主意,因为它们会一遍又一遍地混淆,对于您创建的每个实例都会产生一次。

+0

我明白你在说什么,但我已经检查过,方法不会一遍又一遍地重复。如果我创建了五个子对象,那么如果我在最后创建的对象上调用了hello_son(),但是没有发生,我会希望“-smile_warmly - SonObject”被打印五次。它只打印一次。 – jononomo

+0

我想包装我的课程的所有方法。在这个例子中,它没有多少意义,但是在实际上我正在处理的代码中更有意义。我没有打印“-smile_warmly”,而是检查某个条件并在没有满足时抛出异常 - 并且需要为该类中的每种方法进行此检查。 – jononomo

+0

@JonCrowell:啊,它没有包装它们,因为'classmethod'对象本身不可调用。但是,每次创建实例时,包装代码仍然被不必要地调用。如果你在这些类上创建了很多这些类或实例或方法,那可能会造成性能下降。 – BrenBarn

1

@ PaulMcGuire的回复缺少回报是错误的原因。

在更高层次上,它看起来像你试图通过继承来做什么可能更“常见”(这不是一种常见的方法)通过元类来完成。也许something like this discussion of metaclasses会指出你在一个更易于管理的方向。

相关问题