如何创建一个能够包装实例、类和静态方法的Python类装饰器?

2024-01-04

我想创建一个Python类装饰器(*) 这将能够无缝包装类可能具有的所有方法类型:实例、类和静态。

这是我现在的代码,其中破坏它的部分已注释:

def wrapItUp(method):
    def wrapped(*args, **kwargs):
        print "This method call was wrapped!"
        return method(*args, **kwargs)
    return wrapped

dundersICareAbout = ["__init__", "__str__", "__repr__"]#, "__new__"]

def doICareAboutThisOne(cls, methodName):
    return (callable(getattr(cls, methodName))
            and (not (methodName.startswith("__") and methodName.endswith("__"))
            or methodName in dundersICareAbout))

def classDeco(cls):
    myCallables = ((aname, getattr(cls, aname)) for aname in dir(cls) if doICareAboutThisOne(cls, aname))
    for name, call in myCallables:
        print "*** Decorating: %s.%s(...)" % (cls.__name__, name)
        setattr(cls, name, wrapItUp(call))
    return cls

@classDeco
class SomeClass(object):

    def instanceMethod(self, p):
        print "instanceMethod: p =", p

    @classmethod
    def classMethod(cls, p):
        print "classMethod: p =", p

    @staticmethod
    def staticMethod(p):
        print "staticMethod: p =", p


instance = SomeClass()
instance.instanceMethod(1)
#SomeClass.classMethod(2)
#instance.classMethod(2)
#SomeClass.staticMethod(3)
#instance.staticMethod(3)

我在尝试完成这项工作时遇到两个问题:

  • 当迭代所有可调用对象时,如何确定它是实例、类还是静态类型?
  • 如何使用针对每种情况正确调用的正确包装版本来覆盖该方法?

目前,此代码生成不同的TypeError取决于未注释的注释片段,例如:

  • TypeError: unbound method wrapped() must be called with SomeClass instance as first argument (got int instance instead)
  • TypeError: classMethod() takes exactly 2 arguments (3 given)

(*):如果你是,同样的问题会简单得多直接修饰方法 https://stackoverflow.com/questions/1987919/why-can-decorator-not-decorate-a-staticmethod-or-a-classmethod.


由于方法是函数的包装器,因此要在构造类后将装饰器应用于类上的方法,您必须:

  1. 使用其方法从方法中提取底层函数im_func属性。
  2. 装饰功能。
  3. 重新贴上包装纸。
  4. 用包装的、修饰的函数覆盖该属性。

很难区分一个classmethod一旦从常规方法@classmethod装饰器已被应用;两种方法都是类型instancemethod。不过,您可以检查im_self属性并查看是否是None。如果是这样,那么它是一个常规实例方法;否则它是一个classmethod.

静态方法是简单的函数(@staticmethod装饰器只是阻止应用常用的方法包装器)。所以看起来你不需要为这些做任何特别的事情。

所以基本上你的算法看起来像这样:

  1. 获取属性。
  2. 可以调用吗?如果没有,则继续处理下一个属性。
  3. Is its type types.MethodType? If so, it is either a class method or an instance method.
    • If its im_self is None,它是一个实例方法。通过提取底层函数im_func属性,装饰它,然后重新应用实例方法:meth = types.MethodType(func, None, cls)
    • If its im_self is not None,它是一个类方法。通过提取底层函数im_func并装饰它。现在您必须重新申请classmethod装饰器,但你不能,因为classmethod()不参加课程,因此无法指定它将附加到哪个课程。相反,您必须使用实例方法装饰器:meth = types.MethodType(func, cls, type)。请注意,type这是实际的内置,type.
  4. 如果它的类型不是types.MethodType那么它是一个静态方法或其他非绑定可调用方法,因此只需装饰它即可。
  5. 将新属性设置回类中。

这些在 Python 3 中有所改变——未绑定的方法是那里的函数,IIRC。无论如何,这可能需要彻底重新考虑。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何创建一个能够包装实例、类和静态方法的Python类装饰器? 的相关文章

随机推荐