Python - 当我检查我返回的对象不是NoneType时,为什么这个方法返回NoneType?

时间:2012-12-31 05:25:26

标签: python

我有代码,其中所有对象都来自基础对象,我不打算直接实例化。在我的基础对象的__init__()方法中,我正在尝试执行一些魔术 - 我正在尝试装饰或包装正在初始化的对象的每个方法。但是当我调用结果方法时,我得到的结果让我感到困惑。以下是隔离问题的示例代码:

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()导致sis对象的类型为NoneType。这是输出:

-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'

为什么会这样?

4 个答案:

答案 0 :(得分: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方法正在调用正在被包装的方法,但不会返回该方法的返回值。

答案 1 :(得分: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

答案 2 :(得分:1)

问题在于您正在包装类的所有方法,包括get_sister。您可以像@Paul McGuire建议的那样将return添加到包装器中,但这意味着当您调用son.get_sister时可能会打印出“笑”消息,这可能不是您想要的。

你可能需要做的是在_adjust_methods中添加一些逻辑来准确地确定要包装的方法。您可以拥有一些命名约定,而不仅仅是检查callablenot startswith('_'),您可以使用或不希望用smile行为进行包装。但是,与仅手动装饰要装饰的方法相比,您执行的操作越多,自动装饰对您的益处就越少。有点难以理解为什么要使用你显然想要使用的结构(所有类方法,包装所有内容等)。也许如果你在这里解释了你的最终目标,那么有人可以建议一个更直接的设计。

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

答案 3 :(得分:1)

@ PaulMcGuire回复中遗漏的回复是该错误的原因。

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