如何检查方法是 class 方法还是 metaclass 中的静态方法?
How can I check whether a method is a class method or a static method in a metaclass?
这里是一个非常简单的Base
class,其中包含一个静态方法和一个class方法:
class Base():
@staticmethod
def f():
print("Base.f")
@classmethod
def g(cls):
print("Base.g")
def h(self):
print("Base.h")
如果要从 Base
派生出 class 并覆盖 f
或 g
,则 staticmethod
和 classmethod
需要在覆盖方法上再次使用装饰器。
class A(Base):
@staticmethod
def f():
print("A.f")
class B(Base):
@classmethod
def g(cls):
print("B.g")
所以,一开始我以为我会创建一个元class,自动使 f
成为 staticmethod
和 g
成为 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)
现在 class 的其余部分不需要明确使用 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,但这次我应该检查[=52而不是强制装饰器=] 是否使用过,如果没有使用则抛出异常。
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.f
和 cls.g
就可以看出这一点)。
我在这里遗漏了什么吗?
好的,所以看起来试图检查 cls.f
或 cls.g
是静态的还是元 class 中的 class 方法是没有意义的,它们不是似乎 绑定 了。
然而,在方法上使用了 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")
所以,原问题的答案是:
Checking whether a method has been decorated with staticmethod
or classmethod
is possible in a metaclass, by retrieving the method from the namespace and checking whether it is an instance of 'staticmethod'
or 'classmethod'
.
这个:
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 上,无论是否应用 staticmethod
装饰器,cls.f
都将是 f
函数。 (在 Python 2 上,它会是一个没有装饰器的未绑定方法对象。)
与其访问 cls.f
或 cls.g
并尝试根据描述符协议的结果计算出你经历了哪种描述符,不如绕过描述符协议并访问原始内容class 定义的命名空间:
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+,您可以使用inspect.getattr_static
:
Retrieve attributes without triggering dynamic lookup via the descriptor protocol, getattr() or 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
这里是一个非常简单的Base
class,其中包含一个静态方法和一个class方法:
class Base():
@staticmethod
def f():
print("Base.f")
@classmethod
def g(cls):
print("Base.g")
def h(self):
print("Base.h")
如果要从 Base
派生出 class 并覆盖 f
或 g
,则 staticmethod
和 classmethod
需要在覆盖方法上再次使用装饰器。
class A(Base):
@staticmethod
def f():
print("A.f")
class B(Base):
@classmethod
def g(cls):
print("B.g")
所以,一开始我以为我会创建一个元class,自动使 f
成为 staticmethod
和 g
成为 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)
现在 class 的其余部分不需要明确使用 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,但这次我应该检查[=52而不是强制装饰器=] 是否使用过,如果没有使用则抛出异常。
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.f
和 cls.g
就可以看出这一点)。
我在这里遗漏了什么吗?
好的,所以看起来试图检查 cls.f
或 cls.g
是静态的还是元 class 中的 class 方法是没有意义的,它们不是似乎 绑定 了。
然而,在方法上使用了 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")
所以,原问题的答案是:
Checking whether a method has been decorated with
staticmethod
orclassmethod
is possible in a metaclass, by retrieving the method from the namespace and checking whether it is an instance of'staticmethod'
or'classmethod'
.
这个:
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 上,无论是否应用 staticmethod
装饰器,cls.f
都将是 f
函数。 (在 Python 2 上,它会是一个没有装饰器的未绑定方法对象。)
与其访问 cls.f
或 cls.g
并尝试根据描述符协议的结果计算出你经历了哪种描述符,不如绕过描述符协议并访问原始内容class 定义的命名空间:
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+,您可以使用inspect.getattr_static
:
Retrieve attributes without triggering dynamic lookup via the descriptor protocol, getattr() or 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