Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python PyInstance_NewRaw()具有新旧样式的类_Python_Python 2.7_Python Extensions - Fatal编程技术网

Python PyInstance_NewRaw()具有新旧样式的类

Python PyInstance_NewRaw()具有新旧样式的类,python,python-2.7,python-extensions,Python,Python 2.7,Python Extensions,最近,我在尝试实例化对象而不调用其构造函数时遇到了一个基于C的python扩展中的问题——这是扩展的一个要求 用于创建实例的类是动态获取的:在某个时刻,我有一个实例x,我希望使用它的类来创建其他实例,因此我存储x.\uuuu class\uuuu,以备以后使用--让这个值为klass 稍后,我调用PyInstance_NewRaw(klass,PyDict_New()),然后问题就出现了。如果klass是一个旧式类,那么调用的结果就是所需的新实例。但是,如果它是一个新样式的类,则结果为NULL,

最近,我在尝试实例化对象而不调用其构造函数时遇到了一个基于C的python扩展中的问题——这是扩展的一个要求

用于创建实例的类是动态获取的:在某个时刻,我有一个实例
x
,我希望使用它的类来创建其他实例,因此我存储
x.\uuuu class\uuuu
,以备以后使用--让这个值为
klass

稍后,我调用
PyInstance_NewRaw(klass,PyDict_New())
,然后问题就出现了。如果
klass
是一个旧式类,那么调用的结果就是所需的新实例。但是,如果它是一个新样式的类,则结果为NULL,引发的异常为:

SystemError:../Objects/classobject.c:521:内部函数的参数错误

作为记录,我使用的是Python版本
2.7.5
。在谷歌上搜索,我发现只有一个人在寻找解决方案(在我看来,他在做一个变通方案,但没有详细说明)

对于记录#2:扩展正在创建的实例是这些相同的
x
实例的代理--
x.\uuuuu-class\uuuuuuuu
x.\uuu-dict\uuuuuuu
是已知的,因此扩展正在基于
\uuu-class\uuuuuu
生成新实例(使用前面提到的C函数)以及将相应的
\uuuuu dict\uuuu
设置为新实例(那些
\uuuuu dict\uuuu
具有进程间共享内存数据)。第二次调用实例的
\uuuu init\uuuu
不仅在概念上有问题(第一:它的状态已经知道,第二:ctor的预期行为是每个实例只调用一次),而且也不切实际,因为扩展无法计算参数及其顺序,无法为系统中的每个实例调用
\uuu init\uu()
。此外,更改系统中每个类(其实例可能是代理)的
\uuuuuu init\uuuuu
,并让它们意识到它们将受到代理机制的约束,这在概念上是有问题的(它们不应该知道),而且是不切实际的


因此,我的问题是:无论实例的类样式如何,如何执行相同的
PyInstance\u NewRaw
行为?

新样式类的类型不是
instance
,而是类本身。因此,
PyInstance.*
方法对于新样式的类甚至没有意义

事实上,他明确地解释了这一点:

请注意,这里描述的类对象表示旧式的类,在Python3中会消失。为扩展模块创建新类型时,您需要使用类型对象(第节)

因此,您必须编写代码来检查
klass
是旧式类还是新式类,并针对每种情况执行适当的操作。旧样式类的类型是
PyClass\u类型
,而新样式类的类型是
PyType\u类型
,或者是自定义元类

同时,对于新样式的类,没有直接等价于
PyInstance\u NewRaw
。或者,更确切地说,直接调用其
tp_alloc
插槽,然后添加一个dict将为您提供一个非函数类。您可以尝试复制所有其他适当的工作,但这将是棘手的。或者,您可以使用
tp\u new
,但如果类(或其任何基)中有自定义的
\uuu new\uuu
函数,则这将是错误的。有关一些想法,请参阅中被拒绝的修补程序

但实际上,你想做的一开始可能不是个好主意。也许如果你解释了为什么这是一个要求,以及你想做什么,会有更好的方法去做


如果目标是通过创建新的未初始化的类实例来构建对象,然后从初始化的原型复制其
\u dict\uuu
,那么我认为有一个更简单的解决方案适合您:

\uuuu class\uuuu
是一个可写属性。因此(在Python中显示它;C API基本上是相同的,只是要详细得多,我可能会在某个地方搞砸引用计数):

新对象将是
cls
的一个实例,特别是,它将具有相同的类字典,包括MRO、元类等

如果
cls
有一个构造它所需的元类,或者有一个自定义的
\uuuuuuuu新的\uuuuuuu方法,或者
\uuuuu插槽\uuuuuuuu
,那么这将不起作用。但是,在这些情况下,您在
\uuuuuuu dict\uuuuuuu
上进行复制的设计无论如何都没有任何意义。我相信在任何可能奏效的情况下,这个简单的解决方案都会奏效


一开始打电话似乎是个好办法,但实际上不是。让我解释一下背景

执行此操作时:

foo = Foo(1, 2)
(其中,
Foo
是一个新样式的类),它被转换为类似以下伪代码的内容:

foo = Foo.__new__(1, 2)
if isinstance(foo, Foo):
    foo.__init__(1, 2)
问题是,如果
Foo
或它的一个基定义了
\uuuuuuuuuuuuuuuu新方法,它将期望从构造函数调用中获取参数,就像
\uuuuuuuuuu init\uuuuuuuuuuuuuuuuuu
方法一样

正如您在问题中所解释的,实际上您不知道构造函数调用参数,这是您无法首先调用普通
\uuuuu init\uuuu
方法的主要原因。因此,您也不能调用
\uuuuu new\uuuu

\uuuuu new\uuuu
的基本实现接受并忽略给定的任何参数。因此,如果没有一个类具有
\uuuuu new\uuuuuu
重写或
\uuuuuu元类\uuuuuu
,那么由于
对象中的一个怪癖,您将碰巧侥幸逃脱。但这些都是前一个解决方案可以处理的完全相同的情况,而该解决方案之所以有效,原因更为明显

换言之,就是
foo = Foo.__new__(1, 2)
if isinstance(foo, Foo):
    foo.__init__(1, 2)