【问题标题】:Class-level classmethod() can only be called on a method_descriptor or instance method类级别的 classmethod() 只能在 method_descriptor 或实例方法上调用
【发布时间】:2019-07-25 00:00:52
【问题描述】:

util.py:

import inspect

class Singleton(type):
    _instances=[]
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)


class MetaResult(Singleton):
    def __getattribute__(cls, name):
        return super().__getattribute__(name)

class Result(metaclass=MetaResult):
    @staticmethod
    def res_decorator(func):
        def funcwrap(*args, **kwargs):
            sig = inspect.signature(func)
            bound_sig = sig.bind(*args, **kwargs)
            bound_sig.apply_defaults()
            #additional code to extract function arguments 
        return funcwrap        

check_params.py

from util import Result as _Result
from abc import ABCMeta as _ABCMeta

class paramparse(metaclass=_ABCMeta)

    @classmethod
    @_Result.res_decorator
    def parse_flash_params(cls, flash_config_path):
        #some code

现在,我使用以下设置对文件 check_params.py 进行 cythonize:

cythonize.py

import os as _os
from pathlib import Path as _Path
from distutils.core import setup as _setup
from Cython.Distutils import build_ext as _build_ext

files_to_compile = []

def cython_build(source_path):
    for dirpath, _, fnames in os.walk(source_path):
        for fname in [x for x in fnames if f.endswith('.py'):
            fname = _Path(fname)
            files_to_compile.append(fname)

    for e in files_to_compile:
        e.cython_directives = {'binding':True, 'language_level':3}

    _setup(name="Proj1",cmdclass={'build_ext':_build_ext}, ext_modules=files_to_compile)


cythonized 为: python cythonize.py --path C:\directory_where_check_params_exist

生成一个 pyd 文件,尝试在该文件上运行以下单元测试:

现在,在单元测试中使用:

unit_test_check_params.py

from check_params import *    #getting error here , details outside the code

# unit tests written here

check_params.pyx:112:在初始化 check_params ??? 电子
TypeError: Class-level classmethod() 只能在 method_descriptor 或实例方法上调用。

因此,当我调试它时,错误显示为由于 check_params.py 中装饰器 (def parse_flash_params) 上的类方法描述符引起的

如果您需要更多信息,请告诉我。

【问题讨论】:

  • 什么是Singletonclass1.decorator1?请发布我们可以实际运行的内容,在运行时实际重现问题。
  • 它是Singletonclass1中定义的一个装饰器,它是一个单例类。我认为我们可以先从尝试一个简单的装饰器开始..
  • @classmethod 在 Cython 中运行良好。一个简单的装饰器在 Cython 中运行良好。一个简单的装饰器与 classmethod 相结合可以按任何顺序正常工作。这个问题是无法回答的。阅读minimal reproducible example 并进行编辑。
  • 嗨@DavidW,请检查更新的问题。我尽量做到最小化,同时仍然试图有意义。
  • 您的 cythonize.py 有一些语法错误,因此无法正常工作。和 check_params 一样

标签: python cython


【解决方案1】:

这仍然不是一个非常有用的示例,因为您提供的代码仍然实际上不起作用。然而:

案例

@classmethod
@_Result.res_decorator

绝对是 Cython 错误。在the function __Pyx_Method_ClassMethod Cython 中有很多类型检查以确保类型是方法(定义在类中的函数),而实际上它只需要可调用,并且只应该在调用时进行检查。作为一种快速解决方法,您可以编辑相关的内部 Cython 文件 (CythonFunction.c) 以替换行

PyErr_SetString(PyExc_TypeError,
               "Class-level classmethod() can only be called on "
               "a method_descriptor or instance method.");
return NULL;

    return PyClassMethod_New(method);

在我看来,这更接近 Python 所做的事情,它接受任何对象并且仅在实际调用函数时检查可调用性。

从长远来看,您应该 report it as a bug in Cython 提供一个实际可以证明问题的示例。这样它实际上可以修复。我认为您不需要 Result 和 staticclass 位 - res_decorator 作为独立函数应该可以证明问题。


第二种可能的顺序

@_Result.res_decorator
@classmethod

在非 Cythonized Python 中也不起作用,因为 classmethod 装饰器的直接结果是不可调用的。它只有在成为绑定方法时才变得可调用,这在稍后发生。因此,这不是 Cython 中的错误。


最终附录:

一个更简洁的解决方法是强制 Cython 使用内置的类方法,而不是它自己的导致错误的版本

try:
    myclassmethod = __builtins__.classmethod
except AttributeError:
    myclassmethod = __builtins__['classmethod']

class paramparse(metaclass=_ABCMeta):

    @myclassmethod
    @_Result.res_decorator
    def parse_flash_params(cls, flash_config_path):
        pass

try ... except 块是因为 __builtins__ 在 Cython 和 Python 模块中的行为略有不同,这很好,因为它是一个实现细节。

【讨论】:

  • 我实际上注意到you do appear to have reported the bug already 如此公平......
  • 是的@DavidW 我已经编辑了问题以删除语法错误。你对一切都是正确的。我试图更改代码,它工作。您对类方法重新定义的解决方法也同样出色。
  • 该解决方法将导致 cdef 类中大多数类方法的性能下降,因为它会导致 Cython 使用普通的 Python 类方法类型,而不是为 C 类方法设计的类型。
  • @user2357112 公平点。更好的解决方法可能是仅将设置异常的代码替换为return PyClassMethod_New(method);
  • 实际上,而不是性能回归,我认为当 classmethod 委托给期望实例而不是类的 PyMethodDescrObject 时,它可能只会导致 TypeError。 (仅替换异常设置代码应该可以避免问题,无论问题是性能回归还是错误。)
猜你喜欢
  • 2015-12-18
  • 2012-04-30
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
  • 2010-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多