当前位置: 首页 > 知识库问答 >
问题:

有没有一种内置的方法可以使用CPython内置来使任意可调用的行为作为未绑定的类方法?

齐胜涝
2023-03-14

在Python 2中,可以将任意可调用函数转换为类的方法。重要的是,如果可调用函数是用C实现的CPython内置函数,您可以使用它来创建用户定义的类的方法,这些类本身就是C层,在调用时不调用字节代码。

如果您依赖GIL来提供“无锁”同步,这有时会很有用;由于GIL只能在操作代码之间交换,如果您代码特定部分中的所有步骤都可以推送到C,您可以使其以原子方式运行。

在Python 2中,你可以做这样的事情:

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)

在Python 3中,不再有未绑定的方法类型和<code>类型。MethodType只接受两个参数,并仅创建绑定方法(这对于Python特殊方法(如<code>__len_、<code>_hash__</code>)不有用,因为特殊方法通常直接在类型上查找,而不是在实例上查找)。

有没有什么方法可以在Py3中实现这一点,我错过了?

我看过的事情:

  1. functools.partialmethod(似乎没有C实现,所以它不符合要求,在Python实现和比我需要的更通用的用途之间,它很慢,在我的测试中大约需要5个我们,而直接Python定义需要大约200-300个ns,或者在Py2中增加了大约200-300个ns,开销增加了大约20倍)
  2. 试图使attrger或类似的东西遵循非数据描述符协议(不可能是AFAICT,不能在__get__之类的猴子补丁)
  3. 试图找到一种方法来子类attrger,给它一个__get__,但是当然,__get__需要以某种方式委托给C层,现在我们又回到了我们开始的地方
  4. (特定于attrger用例)使用__slots__首先使成员成为描述符,然后尝试以某种方式从数据的结果描述符转换为跳过绑定和获取实际值的最后一步的东西,使其可调用,以便延迟实际值检索

不过,我不能发誓我没有错过这些选项中的任何一个。有人有任何解决方案吗?完全黑客攻击是允许的;我认识到我在这里做病态的事情。理想情况下,它会很灵活(让您可以从class、Python内置函数(如hexlen等)或任何其他未在Python层定义的可调用对象中创建类似于未绑定方法的东西)。重要的是,它需要附加到类,而不是每个实例(既要减少每个实例的开销,又要正确地适用于dunder特殊方法,这些方法在大多数情况下会绕过实例查找)。

共有1个答案

傅兴平
2023-03-14

最近找到了一个(可能只有CPython)解决方案。作为一个直接调用CPython APIs的< code>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'))

在某种程度上,这是对Python 2版本的改进,因为它不需要定义类来为其创建未绑定的方法,所以可以通过简单的赋值在类体中定义它(其中Python 2版本必须在<code>Foo中显式引用<code>Foo</code>两次。__len__=types.MethodType(attgetter('length'),None,Foo)</code>,并且仅在<code>类Foo</code>已完成定义之后)。

另一方面,它实际上并没有在CPython 3.7 AFAICT上提供性能优势,至少在这里它取代<code>def__len_(self):return self的简单情况下没有。长度;事实上,对于通过len(实例)访问的___len_,在Foo的实例上,ipython%%timeitmicrobenchmarks显示len(实例)在通过_ulenn_方法(attgetter('length')定义时约慢10%,这很可能是<code>attgetter</code>本身的产物,由于CPython没有将其移动到“FastCall”协议(在3.8中称为“Vectorcall”,当其半公开供临时第三方使用时),而用户定义函数在3.7中已经从中受益,此外,每次都必须动态选择是否执行点式或非点式属性查找以及单个或多个属性查找(Vectorcall可以通过选择适合于在构建时执行的GET的<code>__call_

如果他们有时间优化<code>运算符。对于Vectorcall,我将重新标记并更新此答案。

 类似资料:
  • 问题内容: 嗨,我想使用WMI类来查找应用程序和产品信息。但是问题是我想使用Java或任何脚本语言(如python,javascript或perl)。我听说过JWMI,这可能是一个选择。有人可以帮我吗??? 问题答案: JavaScript和Java不是一回事。 JavaScript Windows脚本宿主(WSH)下提供了JavaScript。有了它,访问WMI相当容易: jWMI(Java)

  • 问题内容: 如果通过创建缓冲区,则该内存将位于Java堆之外。有没有一种方法可以以跨平台的方式测量应用程序中此类内存的使用情况,类似于我可以使用and 来测量Java堆使用情况的方法? 问题答案: 您可以使用反射来获取Java 7的OpenJDK / HotSpot。没有独立于平台的方式,它仅通过ByteBuffer.allocateDirect()向您显示用法,而没有其他分配本地内存的方式。 另

  • 问题内容: 有没有办法用Laravel的ELOQUENT ORM来“限制”结果? 和雄辩? 问题答案: 创建一个扩展口才的游戏模型,并使用此模型: 这里将获得30条记录,这里将抵消30条记录。 在最新的Laravel版本中,您还可以使用:

  • 问题内容: 我一直在寻找使用CSS编写的类似Google Chrome的标签,但是找不到。 我正在尝试复制外观,以便在Web应用程序或网站中使用它。 问题答案: 是的,用css3 艾夫(Ive)发表了一篇关于如何对其进行深入研究的博客,可悲的是,除非您使用图像,否则诺努斯将无法继续工作 编辑: 删除了对redeyeoperations的旧引用,现在导致其链接服务器场。这是一个较轻的版本,它在第三方

  • 问题内容: 我知道,如果已经有该键的记录,您可以用来更新某个值, 我可以做这个: 但是,如何做到这一点而不必两次写出列和值呢? 问题答案: 不幸的是没有。 您可以不必重复该值而达到一半: 但是您仍然必须列出这些列。

  • 问题内容: 我正在建立一个带有flask的网站,其中用户具有帐户并能够登录。我正在使用flask-principal作为登录部分和角色管理。有没有办法让用户的会话在5分钟或10分钟后过期?我在flask文档或flask-principal文档中找不到该文件。 我想到了一种手动方法,在登录时在服务器端设置一个带有时间标签的变量,并在用户执行下一个操作时,服务器会验证该时间戳记上的时间增量并删除会话。