如何确定用C编写的stdlib方法的方法类型

时间:2014-09-12 23:16:28

标签: python introspection

classify_class_attrs模块中的inspect函数可用于确定每个类的属性是什么类型的对象,包括函数是实例方法,类方法,或静态方法。这是一个例子:

from inspect import classify_class_attrs

class Example(object):
    @classmethod
    def my_class_method(cls):
        pass

    @staticmethod
    def my_static_method():
        pass

    def my_instance_method(self):
        pass

print classify_class_attrs(Example)

这将为Attribute上的每个属性输出Example个对象的列表,其中包含有关该属性的元数据。在这些情况下相关的是:

Attribute(name='my_class_method', kind='class method', defining_class=<class '__main__.Example'>, object=<classmethod object at 0x100535398>)
Attribute(name='my_instance_method', kind='method', defining_class=<class '__main__.Example'>, object=<unbound method Example.my_instance_method>)
Attribute(name='my_static_method', kind='static method', defining_class=<class '__main__.Example'>, object=<staticmethod object at 0x100535558>)

但是,Python的标准库中的许多对象似乎都不能以这种方式进行反省。我猜这与其中许多是用C实现的事实有关。例如,datetime.datetime.nowAttribute对象描述inspect.classify_class_attrs

Attribute(name='now', kind='method', defining_class=<type 'datetime.datetime'>, object=<method 'now' of 'datetime.datetime' objects>)

如果我们将此与返回的关于Example上的属性的元数据进行比较,您可能会得出datetime.datetime.now是实例方法的结论。但它实际上表现为一种类方法!

from datetime import datetime

print datetime.now()  # called from the class: 2014-09-12 16:13:33.890742
print datetime.now().now()  # called from a datetime instance: 2014-09-12 16:13:33.891161

是否有可靠的方法来确定stdlib类上的方法是静态,类还是实例方法?

1 个答案:

答案 0 :(得分:3)

我认为你可以得到你想要的很多,区分五种,而不依赖inspect未记录的任何内容:

  • Python实例方法
  • Python类方法
  • Python静态方法
  • 内置实例方法
  • 内置类方法或静态方法

但是你无法使用CPython特定的实现细节来区分最后两个。

(据我所知,只有3.x在stdlib中有任何内置的静态方法......但是当然即使在2.x中,也有人可以在扩展模块中定义一个。)


inspect中可用的详细信息,甚至它在每个Python版本中的含义都有所不同,部分原因是因为事情在2.x和3.x之间发生了变化,部分原因是因为inspect基本上是一堆随着时间的推移逐渐改善的启发式方法。

但至少对于CPython 2.6和2.7以及3.3-3.5,区分内置实例方法与其他两种类型的最简单方法是从类中查找的方法isbuiltin。对于静态方法或类方法,这将是True;对于实例方法,为False。例如:

>>> inspect.isbuiltin(str.maketrans)
True
>>> inspect.isbuiltin(datetime.datetime.now)
True
>>> inspect.isbuiltin(datetime.datetime.ctime)
False

为什么这样做?好吧,isbuiltin会:

  

如果对象是内置函数或绑定的内置方法,则返回true。

在实例上查找时,会绑定常规方法或类似classmethod的方法。但是当查看类时,常规方法是未绑定的,而类似classmethod的方法(绑定到类)。当然,当以任一方式查找时,类似staticmethod的方法最终会成为一个普通的函数。所以,它有点间接,但它总是正确的。*


类方法与静态方法怎么样?

在CPython 3.x中,内置静态和类方法描述符在查询其类时都返回完全相同的类型,并且没有任何记录的属性可用于区分它们。即使这个不是是真的,我认为参考的编写方式,保证inspect中的任何函数都无法区分它们。

如果我们转向描述符本身怎么办?是的,我们可以区分它们的方式......但我不认为它是由语言保证的:

>>> callable(str.__dict__['maketrans'])
False
>>> callable(datetime.datetime.__dict__['now'])
True

为什么这样做?好吧,静态方法只使用staticmethod描述符,就像在Python中一样(但包含内置函数而不是函数)。但是类和实例方法使用特殊的描述符类型,而不是使用classmethod包装(内置)函数和(内置)函数本身,就像Python类和实例方法那样。这些特殊描述符类型classmethod_descriptormethod_descriptor是未绑定(类和实例)方法,也是绑定它们的描述符。有这样的历史/实施原因,但我不认为语言参考中有任何要求它是真实的,甚至暗示它。

如果您愿意依赖实现工件,isinstance(m, staticmethod)似乎更简单......

所有这一切,除了CPython之外是否还有内置静态方法和classmethods的实现?如果没有,请记住,实用性胜过纯洁......


*它真正测试的是这个东西是否可以在没有额外参数的情况下调用,但这与记录的“函数或绑定方法”基本相同;无论哪种方式,这都是你想要的。