如何检查方法是元类中的类方法还是静态方法?

时间:2017-02-17 15:05:27

标签: python static-methods metaclass class-method

这是一个非常简单的Base类,它包含一个静态方法和一个类方法:

class Base():

    @staticmethod
    def f():
        print("Base.f")

    @classmethod
    def g(cls):
        print("Base.g")

    def h(self):
        print("Base.h")

如果要从Base派生一个类并覆盖fg,则需要再次使用staticmethodclassmethod装饰器关于最重要的方法。

class A(Base):

    @staticmethod
    def f():
        print("A.f")

class B(Base):

    @classmethod
    def g(cls):
        print("B.g")

所以,起初我以为我会创建一个自动使f成为staticmethodg成为staticmethod的元类。

class BaseMeta(type):

    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        if 'f' in namespace: cls.f = staticmethod(cls.f)
        if 'g' in namespace: cls.g = classmethod(cls.g)

现在其余的课程不需要明确使用staticmethodclassmethod

class Base(metaclass=BaseMeta):

    def f():
        print("Base.f")

    def g(cls):
        print("Base.g")

    def h(self):
        print("Base.h")

class A(Base):

    def f():
        print("A.f")

class B(Base):

    def g(cls):
        print("B.g")

这样可行,但我不喜欢它的样子。现在,我意识到staticmethodclassmethod修饰符应该显式使用(毕竟,显式优于隐式,不是吗?)

所以我认为我可以保留元类,但这次不是强制执行装饰器,我应该检查它们是否已被使用并抛出异常如果他们没有。

class BaseMeta(type):

    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        # check if cls.f is a static method
        if not inspect.isfunction(cls.f):
            raise Exception("f should be a static method")
        # check if cls.g is a static method
        if not (inspect.ismethod(cls.g) and cls.g.__self__ == cls):
            raise Exception("g should be a class method")

不幸的是,这不起作用。似乎在metaclasse的__init__中,一切都被认为只是一个函数(只需打印cls.fcls.g就可以了。)

我在这里找不到什么东西?

3 个答案:

答案 0 :(得分:2)

此:

if not (inspect.ismethod(cls.g) and cls.g.__self__ == cls):
    raise Exception("g should be a class method")

工作正常,但是这个:

if not inspect.isfunction(cls.f):
    raise Exception("f should be a static method")

没有,因为在Python 3上,cls.f将是f函数,无论是否应用了staticmethod装饰器。 (在Python 2上,它将是一个没有装饰器的未绑定方法对象。)

而不是访问cls.fcls.g并尝试根据描述符协议的结果计算出您经历的描述符类型,绕过描述符协议并访问类的原始内容定义的命名空间:

if 'f' in namespace and not isinstance(namespace['f'], staticmethod):
    whatever()
if 'g' in namespace and not isinstance(namespace['g'], classmethod):
    whatever()

答案 1 :(得分:1)

好的,所以似乎尝试检查cls.fcls.g是否为静态或类别中的类方法是没有意义的,它们似乎不是绑定然而。

但是,在方法上使用staticmethodclassmethod装饰器肯定会在其上留下标记。我玩了一遍,最终发现我原本想要做的事情可以实现如下:

class BaseMeta(type):

    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        # check if cls.f is a static method
        if 'f' in namespace and not isinstance(namespace['f'], staticmethod):
            raise Exception(cls.__name__ + ".f should be a static method")
        # check if cls.g is a class method
        if 'g' in namespace and not isinstance(namespace['g'], classmethod):
            raise Exception(cls.__name__ + ".g should be a class method")

所以,原始问题的答案是:

  

通过从命名空间中检索方法并检查它是否为实例,检查方法是否已在元类中使用staticmethodclassmethod进行修饰'staticmethod''classmethod'

答案 2 :(得分:0)

对于Python 3.2+,您可以使用inspect.getattr_static

无需通过描述符协议 getattr ()或 getattribute ()触发动态查找即可获取属性。

示例:

import inspect


class A:
    @staticmethod
    def f():
        pass

    @classmethod
    def g(cls):
        pass

    def r():
        pass


a = A()
print(isinstance(inspect.getattr_static(a, "f"), staticmethod))
print(isinstance(inspect.getattr_static(A, "f"), staticmethod))
print(isinstance(inspect.getattr_static(a, "g"), classmethod))
print(isinstance(inspect.getattr_static(A, "g"), classmethod))
print(isinstance(inspect.getattr_static(a, "r"), classmethod))
print(isinstance(inspect.getattr_static(A, "r"), staticmethod))

将输出:

True
True
True
True
False
False