Python-我可以以编程方式装饰类实例中的类方法吗?
我有一个对象层次结构,其中几乎所有方法都是类方法。看起来像以下内容:
I have an object hierarchy in which almost all of the methods are class methods. It looks like the following:
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()
给定的代码输出以下内容:
The code as given outputs the following:
greetings
hello son
goodbye son
greetings
hello daughter
goodbye daughter
我希望代码输出以下内容:
I would like the code to output the following:
-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'ob ject不可调用
)。相反,我希望每种方法的修饰都可以通过 __ init __()
方法以编程方式进行。
But I don't want to add the line @smile_warmly
before each method (and when I try to do that in the code above, I get error message TypeError: 'classmethod' object is not callable
). Rather, I would like the decoration of each method to take place programmatically in the __init__()
method.
是
EDIT :找到了一些行之有效的方法-请参阅下面的答案。
EDIT: Found something that seems to work -- see my answer below. Thanks to BrenBarn.
装饰器所做的全部工作就是返回一个新函数。
All a decorator does is return a new function. This:
@deco
def foo():
# blah
与此相同:
def foo():
# blah
foo = deco(foo)
您可以在不使用 @
语法的情况下随时执行相同的操作,只需将函数替换为所需的内容即可。因此,在 __ init __
或其他任何地方,您都可以遍历所有方法,并用 smilewarmly(meth)
替换每个方法>。
You can do the same thing whenever you like, without the @
syntax, just by replacing functions with whatever you like. So in __init__
or wherever else, you could loop through all the methods and for each one replace it with smilewarmly(meth)
.
但是,与其在 __ init __
中进行操作,不如在上课时这样做被建造。您可以使用元类,或更简单地使用类装饰器来做到这一点:
However, instead of doing it in __init__
, it would make more sense to do it when the class is created. You could do this with a metaclass, or more simply with a class decorator:
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))
。 (您要采用哪种方式取决于微笑装饰器与类方法之间的紧密联系。)这意味着您不必在内部使用 @classmethod
In this example I put the classmethod decoration inside my smileDeco
decorator. You could also put it in makeSmiley
so that makeSmiley
returned smileDeco(classmethod(val))
. (Which way you want to do it depends on how closely linked the smile-decorator is to the things being classmethods.) This means you don't have to use @classmethod
inside the class.
当然,在 makeSmiley
的循环中,您可以包括任何您想要决定的逻辑(例如,基于该方法的名称)是否用微笑行为将其包装。
Also, of course, in the loop in makeSmiley
you can include whatever logic you like to decide (for instance, based on the method's name) whether to wrap it with the smile behavior or not.
请注意,如果您必须谨慎一点我真的想在类中手动使用 @classmethod
,因为通过类 __ dict __
访问的类方法是不可调用的。因此,您必须专门检查该对象是否为类方法对象,而不仅仅是检查它是否可调用。
Note that you'd have to be a little more careful if you really want to manually use @classmethod
inside the class, because classmethods as accessed via the class __dict__
aren't callable. So you'd have to specifically check whether the object is a classmethod object, instead of just checking whether it's callable.