Python-我可以从类实例中以编程方式修饰类方法吗?

2024-06-28 20:35:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个对象层次结构,其中几乎所有的方法都是类方法。看起来如下:

class ParentObject(object):
    def __init__(self):
        pass

    @classmethod
    def smile_warmly(cls, the_method):
        def wrapper(kls, *args, **kwargs):
            print "-smile_warmly - "+kls.__name__
            the_method(*args, **kwargs)
        return wrapper

    @classmethod
    def greetings(cls):
        print "greetings"

class SonObject(ParentObject):
    @classmethod
    def hello_son(cls):
        print "hello son"

    @classmethod
    def goodbye(cls):
        print "goodbye son"

class DaughterObject(ParentObject):
    @classmethod
    def hello_daughter(cls):
        print "hello daughter"

    @classmethod
    def goodbye(cls):
        print "goodbye daughter"

if __name__ == '__main__':
    son = SonObject()
    son.greetings()
    son.hello_son()
    son.goodbye()
    daughter = DaughterObject()
    daughter.greetings()
    daughter.hello_daughter()
    daughter.goodbye()

给定的代码输出如下:

greetings
hello son
goodbye son
greetings
hello daughter
goodbye daughter

我希望代码输出以下内容:

-smile_warmly - SonObject
greetings
-smile_warmly - SonObject
hello son
-smile_warmly - SonObject
goodbye son
-smile_warmly - DaughterObject
greetings
-smile_warmly - DaughterObject
hello daughter
-smile_warmly - DaughterObject
goodbye daughter

但是我不想在每个方法之前添加行@smile_warmly(当我在上面的代码中尝试这样做时,我会得到错误消息TypeError: 'classmethod' object is not callable)。相反,我希望每个方法的修饰都以编程方式在__init__()方法中进行。

在Python中可以编程地修饰方法吗?

编辑:找到似乎有用的东西--请参阅下面的我的答案。多亏了布伦巴恩。


Tags: 方法hellodefclassclsprintclassmethodson
2条回答

这个解决方案产生了我想要的输出:

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:
            self._adjust_methods(base)

    def _smile_warmly(self, the_method):
        def _wrapped(self, *args, **kwargs):
            print "-smile_warmly - " +self.__name__
            the_method(self, *args, **kwargs)
        cmethod_wrapped = classmethod(_wrapped)
        # cmethod_wrapped.adjusted = True
        return cmethod_wrapped

    def greetings(self):
        print "greetings"

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

    def goodbye(self):
        print "goodbye son"

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

    def goodbye(self):
        print "goodbye daughter"

if __name__ == '__main__':
    son = SonObject()
    son.greetings()
    son.hello_son()
    son.goodbye()
    daughter = DaughterObject()
    daughter.greetings()
    daughter.hello_daughter()
    daughter.goodbye()

输出为:

-smile_warmly - SonObject
greetings
-smile_warmly - SonObject
hello son
-smile_warmly - SonObject
goodbye son
-smile_warmly - DaughterObject
greetings
-smile_warmly - DaughterObject
hello daughter
-smile_warmly - DaughterObject
goodbye daughter

decorator所做的只是返回一个新函数。这:

@deco
def foo():
    # blah

与此相同:

def foo():
    # blah
foo = deco(foo)

你可以随时做同样的事情,不需要@语法,只要用你喜欢的东西替换函数。所以在__init__或者其他地方,您可以循环遍历所有方法,对于每个方法,用smilewarmly(meth)替换它。

但是,与其在__init__中执行,不如在创建类时执行。您可以使用一个元类,或者更简单地使用一个类装饰器:

def smileDeco(func):
    def wrapped(*args, **kw):
        print ":-)"
        func(*args, **kw)
    return classmethod(wrapped)

def makeSmiley(cls):
    for attr, val in cls.__dict__.iteritems():
        if callable(val) and not attr.startswith("__"):
            setattr(cls, attr, smileDeco(val))
    return cls

@makeSmiley
class Foo(object):
    def sayStuff(self):
        print "Blah blah"

>>> Foo().sayStuff()
:-)
Blah blah

在本例中,我将classmethod装饰放在我的smileDeco装饰器中。你也可以把它放在makeSmiley中,这样makeSmiley返回smileDeco(classmethod(val))。(你想用哪种方法来实现它取决于smile decorator与classmethods的关系有多紧密)这意味着你不必在类中使用@classmethod

当然,在makeSmiley的循环中,您可以包括您想要决定(例如,基于方法名)是否用微笑行为包装它的任何逻辑。

请注意,如果您真的想在类中手动使用@classmethod,那么就必须更加小心,因为通过类__dict__访问的类方法是不可调用的。因此,您必须特别检查对象是否是classmethod对象,而不是仅仅检查它是否可调用。

相关问题 更多 >