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

Python 如何检查方法是类方法还是元类中的静态方法?,python,static-methods,metaclass,class-method,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派生类并重写f或g,则需要在重写方法上再次使用staticmethod和cl

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

class Base():

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

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

    def h(self):
        print("Base.h")
如果要从
Base
派生类并重写
f
g
,则需要在重写方法上再次使用
staticmethod
classmethod
装饰器

class A(Base):

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

class B(Base):

    @classmethod
    def g(cls):
        print("B.g")
所以,一开始我想我会创建一个元类,自动生成
f
a
staticmethod
g
a
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)
现在剩下的类不需要显式地使用
staticmethod
classmethod

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")
这行得通,但我不喜欢它的样子。现在,我意识到应该显式使用
staticmethod
classmethod
装饰器(毕竟显式比隐式好,不是吗?)

所以我想我可以保留元类,但是这次我不应该强制使用装饰器,我应该检查它们是否被使用,如果没有,则抛出一个异常

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")
不幸的是,这不起作用。似乎在元类的
\uuuu init\uuuu
中,一切都被认为只是一个函数(只需打印
cls.f
cls.g
就可以看出这一点)


这里有什么我遗漏的吗?

好的,因此,试图检查
cls.f
cls.g
是否是元类中的静态或类方法似乎是毫无意义的,它们似乎还没有绑定

然而,在一个方法上使用了
staticmethod
classmethod
装饰器肯定已经在其上留下了印记。我到处玩,最终发现我原本想做的事情可以实现如下:

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")
因此,原来问题的答案是:

通过从名称空间检索方法并检查它是否是
'staticmethod'
'classmethod'
的实例,可以在元类中检查方法是否已用
staticmethod
'classmethod'
修饰

这:

工作正常,但这:

if not inspect.isfunction(cls.f):
    raise Exception("f should be a static method")
不会,因为在Python 3上,
cls.f
将是
f
函数,无论是否应用了
staticmethod
装饰器。(在Python2上,如果没有decorator,它将是一个未绑定的方法对象。)


与其访问
cls.f
cls.g
并试图根据描述符协议的结果确定所使用的描述符类型,不如绕过描述符协议并访问类定义命名空间的原始内容:

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

对于Python 3.2+,您可以使用:

通过描述符协议、getattr()或getattribute()检索属性而不触发动态查找

例如:

导入检查
A类:
@静力学方法
def():
通过
@类方法
def g(cls):
通过
def r():
通过
a=a()
打印(isinstance(inspect.getattr_static(a,“f”),staticmethod))
打印(isinstance(inspect.getattr_static(A,“f”),staticmethod))
打印(isinstance(inspect.getattr_static(a,“g”),classmethod))
打印(isinstance(inspect.getattr_static(A,“g”),classmethod))
打印(isinstance(inspect.getattr_static(a,“r”),classmethod))
打印(isinstance(inspect.getattr_static(A,“r”),staticmethod))
将输出:

True
True
True
True
False
False

您不需要查看
\uuuuu名称
isinstance(无论什么,staticmethod)
isinstance(无论什么,classmethod)
都可以。谢谢,这当然更优雅了。我将进行相应的编辑。我测试了
inspect.isfunction
,它返回了所有方法的
True
f
g
h
)。我还测试了
inspect.ismethod
,对于所有这些方法,它都返回了
False
。如果您在创建类之后尝试这些测试,那么这些测试实际上是有效的,但是它们在元类中似乎不起作用。@GeorgeBoukeas:你能澄清一下
XX
应该在那里表示什么吗?@GeorgeBoukeas:
ismethod
返回类方法所期望的
True
。在Python中,显式并不一定比隐式好。考虑到EAFP(比许可更容易请求宽恕)被认为是Pythic的,更高层次的思维被鼓励作为计算细节的例子。
True
True
True
True
False
False