有没有一种内置的方法可以使用CPython内置函数使任意可调用的类作为未绑定的类方法?
在Python2中,可以将任意可调用项转换为类的方法。重要的是,如果可调用的是用C实现的CPython内置类,那么可以使用它来生成用户定义类的方法,这些类本身就是C层,在调用时不调用字节码 如果您依赖GIL来提供“无锁”同步,这有时会很有用;由于GIL只能在操作代码之间交换,如果代码特定部分中的所有步骤都可以推送到C,则可以使其原子化 在Python 2中,您可以执行如下操作:有没有一种内置的方法可以使用CPython内置函数使任意可调用的类作为未绑定的类方法?,python,python-3.x,methods,built-in,python-descriptors,Python,Python 3.x,Methods,Built In,Python Descriptors,在Python2中,可以将任意可调用项转换为类的方法。重要的是,如果可调用的是用C实现的CPython内置类,那么可以使用它来生成用户定义类的方法,这些类本身就是C层,在调用时不调用字节码 如果您依赖GIL来提供“无锁”同步,这有时会很有用;由于GIL只能在操作代码之间交换,如果代码特定部分中的所有步骤都可以推送到C,则可以使其原子化 在Python 2中,您可以执行如下操作: import types from operator import attrgetter class Foo(obje
import types
from operator import attrgetter
class Foo(object):
... This class maintains a member named length storing the length...
def __len__(self):
return self.length # We don't want this, because we're trying to push all work to C
# Instead, we explicitly make an unbound method that uses attrgetter to achieve
# the same result as above __len__, but without no byte code invoked to satisfy it
Foo.__len__ = types.MethodType(attrgetter('length'), None, Foo)
在Python3中,不再有未绑定的方法类型,类型。MethodType
只接受两个参数,并且只创建绑定方法(这对于Python的特殊方法,如\uLen\uuuUn
,\uUn\uHash\uUn
等,是不有用的,因为特殊方法通常直接在类型上查找,而不是在实例上)
在Py3中是否有我所缺少的实现这一点的方法
我看过的东西:
functools.partialmethod
(似乎没有C实现,因此它没有满足要求,而且在Python实现和比我需要的更为通用的功能之间,它的速度很慢,在我的测试中大约需要5个us,而在Py2中,直接Python定义或attrgetter
大约需要200-300个ns,开销大约增加了20倍)attrgetter
或类似程序遵循非数据描述符协议(AFAICT不可能,不能在\uuuuuuuu get\uuuuuuuuuu
或类似程序中使用monkey patch)attrgetter
子类化为\uuu get\uuuu
,但是当然,\uu get\uuuu
需要以某种方式委托给C层,现在我们回到了开始的地方attrgetter
用例)首先使用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
类
、Python内置函数(如hex
、len
等)或任何其他未在Python层定义的可调用对象中生成类似未绑定方法的对象)。重要的是,它需要附加到类,而不是每个实例(既可以减少每个实例的开销,又可以正确地使用dunder特殊方法,在大多数情况下都会绕过实例查找)。最近找到了一个解决方案(可能仅限于CPython)。这有点难看,是一个直接调用CPython API的ctypes
黑客,但它可以工作,并获得所需的性能:
import ctypes
from operator import attrgetter
make_instance_method = ctypes.pythonapi.PyInstanceMethod_New
make_instance_method.argtypes = (ctypes.py_object,)
make_instance_method.restype = ctypes.py_object
class Foo:
# ... This class maintains a member named length storing the length...
# Defines a __len__ method that, at the C level, fetches self.length
__len__ = make_instance_method(attrgetter('length'))
在某种程度上,这是对Python2版本的改进,因为它不需要定义类来为其生成未绑定的方法,因此可以通过简单赋值在类体中定义它(其中Python2版本必须在Foo.\uu___=types.MethodType(attrgetter('length')中显式引用Foo
两次),无,Foo)
,并且仅在定义完类Foo之后)
另一方面,它实际上并没有在CPython 3.7AFAICT上提供性能优势,至少在这里它取代def\uu len\uuuuuu(self):返回self.length
;事实上,对于通过len(实例)访问的\uu len\uuu
在Foo
的实例上,ipython
%%timeit
微基准点显示len(实例)
在通过\ulen\uUu=make\u实例方法(attrgetter('length')定义\ulen\uUu
时慢约10%
,。这可能是由于CPython没有将其移动到“FastCall”协议(3.8中称为“Vectorcall”,当时它被半公开供临时第三方使用)而导致的,attrgetter
本身的开销稍高的产物,而用户定义函数在3.7中已经从中受益,并且每次都必须动态选择是否执行点式或非点式属性查找以及单个或多个属性查找(通过选择适合于在构造时执行的GET的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
实现,Vectorcall可以避免哪个Vectorcall)增加了普通方法避免的更多开销。对于更复杂的情况(例如,如果要检索的属性是,因为attrgetter
的开销在很大程度上是固定的,而Python中的嵌套属性查找意味着更多的字节代码,但现在,它并不经常有用
如果他们开始为Vectorcall优化
操作符.attrgetter
,我会重新标记并更新这个答案。作为参考,至少涵盖了三年前的一些选项,但解决方案并没有将工作推到Python 3的C层。为什么不编写一个C扩展来实现这一点呢?您可能会找到一些方法要做到这一点,但通常情况下,在C级完成或不完成某项工作并不属于Python的公共API的一部分,因此,如果您想确保在C中完成某项工作,您需要自己在C中完成。@BrenBarn:总是可能的,但这比重新调整内置程序的用途要大得多。Python2方法的增量工作将非常理想我不可能从一个导入和一个单行程序变成一个完整的C扩展模块,必须在每个系统上构建和安装这个扩展模块才能启用这个用例