问题标签 [new-style-class]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
0 回答
34 浏览

python-2.7 - 当我(错误?)使用超级时,我的方法不会被调用

输出中没有“UCD.edit”,这意味着没有调用 UpperCaseDocument.edit。我错过了什么?

谢谢你的帮助。

(如果您认为 super 只会调用一个基类方法,请使用以下代码进行测试:

0 投票
1 回答
306 浏览

c++ - 为什么 PyCXX 会以它的方式处理新式类?

我正在挑选一些 C++ Python 包装器代码,这些代码允许消费者从 C++ 构建自定义的旧样式和新样式 Python 类。

原始代码来自PyCXX ,这里这里都有新旧样式类。然而,我已经大量重写了代码,在这个问题中,我将参考我自己的代码,因为它使我能够以最清晰的方式呈现情况。我认为如果没有几天的审查,很少有人能够理解原始代码......对我来说,这已经花费了数周时间,但我仍然不清楚。

旧样式只是派生自 PyObject,

当 one_time_setup() 被调用时,它强制(通过访问基类typeobject())为这个新类型创建关联PyTypeObject

稍后在构造实例时,它使用PyObject_Init

到目前为止,一切都很好。

但是新样式类使用更复杂的机器。我怀疑这与新样式类允许派生的事实有关。

这是我的问题,为什么新样式类处理是以它的方式实现的?为什么必须创建这个额外的 PythonClassInstance 结构?为什么它不能像旧式的类处理那样做事情呢?即只需从 PyObject 基本类型输入转换?并且看到它没有这样做,这是否意味着它没有使用它的 PyObject 基本类型?

这是一个很大的问题,我将继续修改帖子,直到我满意它很好地代表了这个问题。它不适合 SO 的格式,对此我很抱歉。然而,一些世界级的工程师经常光顾这个网站(例如,我之前的一个问题是由 GCC 的首席开发人员回答的),我很重视利用他们的专业知识的机会。所以请不要太仓促投票关闭。

新样式类的一次性设置如下所示:

所以新样式使用了一个自定义的 PythonClassInstance 结构:

PyObject_HEAD,如果我深入研究 Python 的 object.h,它只是一个宏PyObject ob_base;——没有其他复杂性,例如 #if #else。所以我不明白为什么它不能简单地是:

甚至:

无论如何,它的目的似乎是将指针标记到 PyObject 的末尾。这是因为 Python 运行时经常会触发我们放在其函数表中的函数,而第一个参数将是负责调用的 PyObject。因此,这允许我们检索关联的 C++ 对象。

但是我们也需要对旧式类这样做。

这是负责执行此操作的函数:

我的评论表达了我的困惑。

在这里,为了完整起见,我们将 lambda-trampoline 插入到 PyTypeObject 的函数指针表中,以便 Python 运行时可以触发它:

(在这个演示中,我使用了 tp_setattro,请注意还有大约 30 个其他插槽,如果您查看 PyTypeObject 的文档,您可以看到)

(事实上​​,以这种方式工作的主要原因是我们可以在每个蹦床上尝试{}catch{}。这使消费者不必编写重复的错误捕获代码。)

因此,我们提取“关联 C++ 对象的基本类型”并调用它的虚拟 setattro(此处仅以 setattro 为例)。派生类将覆盖 setattro,并且将调用此覆盖。

旧式类提供了这样的覆盖,我将其标记为 MARKER1——它位于该问题的顶部列表中。

我唯一能想到的是,也许不同的维护者使用了不同的技术。但是,新旧风格的类需要不同的架构,还有什么更令人信服的理由吗?


PS作为参考,我应该包括来自新样式类的以下方法:

^ 对我来说,这看起来完全错误。它似乎正在分配内存,重新转换到可能超过分配数量的某些结构,然后在此结束时清空。我很惊讶它没有导致任何崩溃。我在源代码中的任何地方都看不到这 4 个字节是拥有的。

^ 请注意,除了默认值之外,没有 reinit 的实现


编辑:我会冒险猜测,这要归功于 IRC 频道上 Yhg1s 的洞察力。

也许是因为当你创建一个新的老式类时,它保证它会完美地重叠一个 PyObject 结构。

因此,从 PyObject 派生并将指向底层 PyObject 的指针传递给 Python 是安全的,这就是旧式类所做的 (MARKER2)

另一方面,新样式类创建了一个 {PyObject + 也许是别的} 对象。ie 做同样的把戏是不安全的,因为 Python 运行时最终会写到基类分配的末尾(它只是一个 PyObject)。

因此,我们需要让 Python 为类分配,并返回一个我们存储的指针。

因为我们现在不再使用 PyObject 基类进行存储,所以我们不能使用类型转换的便捷技巧来检索关联的 C++ 对象。这意味着我们需要在实际分配的 PyObject 末尾标记一个额外的 sizeof(void*) 字节,并使用它来指向我们关联的 C++ 对象实例。

然而,这里有些矛盾。

^ 如果这确实是完成上述内容的结构,那么它就是说新样式类实例确实完全适合 PyObject,即它没有重叠到 m_pycxx_object 中。

如果是这样的话,那么整个过程肯定是不必要的。

编辑:这里有一些链接可以帮助我学习必要的基础工作:

http://eli.thegreenplace.net/2012/04/16/python-object-creation-sequence
http://realmike.org/blog/2010/07/18/introduction-to-new-style-classes-in -python
使用 Python 的 C API 创建一个对象

0 投票
2 回答
403 浏览

c++ - 如何整理/修复 PyCXX 创建的新型 Python 扩展类?

我几乎完成了对 C++ Python 包装器 (PyCXX) 的重写。

原始允许新旧样式扩展类,但也允许从新样式类派生:

代码很复杂,并且似乎包含错误(为什么 PyCXX 以它的方式处理新型类?

我的问题是:PyCXX 复杂机制的基本原理/理由是什么?有没有更清洁的替代品?

我将尝试在下面详细说明我在此查询中所处的位置。首先,我将尝试描述 PyCXX 目前正在做什么,然后我将描述我认为可能需要改进的地方。


当 Python 运行时遇到 时d = Derived(),它会执行PyObject_Call( ob ) where ob is thePyTypeObject forNewStyleClass . I will writeob asNewStyleClass_PyTypeObject`。

PyTypeObject 已经用 C++ 构建并使用PyType_Ready

PyObject_Call将调用type_call(PyTypeObject *type, PyObject *args, PyObject *kwds),返回一个初始化的派生实例,即

像这样的东西。

(所有这些都来自(顺便说一下, http ://eli.thegreenplace.net/2012/04/16/python-object-creation-sequence,谢谢 Eli!)

type_call 本质上是:

我们的 C++ 包装器已将函数插入到类似这样的东西的tp_newtp_init槽中:NewStyleClass_PyTypeObject

请注意,我们需要将 Python Derived 实例绑定在一起,它是对应的 C++ 类实例。(为什么?解释如下,见“X”)。为此,我们使用:

现在这座桥提出了一个问题。我非常怀疑这种设计。

请注意如何为这个新的 PyObject 分配内存:

然后我们将 this 指针类型转换为,并使用紧随其后Bridge的 4 或 8 ( ) 个字节指向相应的 C++ 类实例(如上所示,这被连接起来了)。sizeof(void*)PyObjectextension_object_init

现在要让它工作,我们需要:

a)subtype->tp_alloc(subtype,0)必须分配一个额外的sizeof(void*)字节 b)PyObject不需要任何超出 的内存sizeof(PyObject_HEAD),因为如果它这样做了,那么这将与上述指针冲突

在这一点上我的一个主要问题是:我们能否保证PyObjectPython 运行时为我们创建的derived_instance不会与 Bridge 的ExtObjBase* m_pycxx_object字段重叠?

我将尝试回答:由美国决定分配多少内存。当我们创建时,我们输入我们希望为这种类型的新实例分配NewStyleClass_PyTypeObject多少内存:PyTypeObject

你可以看到它进入table->tp_basicsize

但现在我似乎很清楚,从中生成的 PyObject-sNewStyleClass_PyTypeObject永远不需要额外分配的内存。

这意味着整个Bridge机制是不必要的。

PyCXX 的原始技术使用 PyObject 作为 的基类NewStyleClassCXXClass,并初始化这个基类,以便 Python 运行时的 PyObjectd = Derived()实际上是这个基类,这种技术看起来不错。因为它允许无缝类型转换。

每当 Python 运行时NewStyleClass_PyTypeObjectNewStyleClassCXXClass. <-- 'X'(上面引用过)

所以真的我的问题是:我们为什么不这样做呢?从强制为 PyObject 进行额外分配的派生有什么特别之处NewStyleClass吗?

我意识到我不理解派生类的创建顺序。伊莱的帖子没有涵盖这一点。

我怀疑这可能与以下事实有关

^ 这个变量名是'subtype' 我不明白这个,我想知道这是否可能是关键。

编辑:我想到了为什么 PyCXX 使用 sizeof(FinalClass) 进行初始化的一种可能解释。它可能是一个经过尝试和放弃的想法的遗物。即,如果 Python 的 tp_new 调用为 FinalClass(它以 PyObject 作为基础)分配了足够的空间,也许可以使用“placement new”或一些巧妙的 reinterpret_cast 业务在该确切位置生成一个新的 FinalClass。我的猜测是这可能已经尝试过了,发现会造成一些问题,解决了问题,然后遗物就被抛在了后面。

0 投票
0 回答
40 浏览

python - 描述符在设置为实例时不像描述符——为什么不呢?

我有一个枚举描述符,我希望能够动态地将其添加到类中。这里是

为了尝试理解这种行为,我编写了一个单元测试来检查它

这一切正常吗?为什么它的工作方式不同,无论它是成员__dict__还是class.__dict__???

请告诉我有更好的方法来做到这一点!

0 投票
1 回答
84 浏览

python - 如何验证类是否实现了丰富的比较方法?

使用旧样式类只需使用hasattr作品:

使用新样式类,每个类都具有以下属性__eq__

hasattr(object, '__eq__')也是返回的预期行为True。对于每种丰富的比较方法都是如此。

如果我不能使用,如何验证类是否实现了丰富的比较方法hasattr?想到的一件事是调用该方法并查看它是否引发NotImplemented异常。但是调用这些方法可能会产生意想不到的损害。

0 投票
0 回答
115 浏览

python - 对python属性获取过程感到困惑:无限递归?

假设我们要获取一个对象的属性:smth = object.attr。还假设我们已经知道我们的属性位于哪个对象中,假设它是名为 A 的类。

据我所知,属性获取过程如下所示:

  1. 在 A 类的属性字典中找到描述符。
  2. 如果存在,则返回属性的 getter 的输出。否则,返回属性本身。

我在这里看到一个问题。假设我们的属性是一个带有 getter 的描述符。在这种情况下,要解析object.descriptor,我们必须解析descriptor.__get__哪个是非数据描述符(函数),也需要解析。这个递归的终点在哪里?

0 投票
1 回答
353 浏览

python - 如何使只读属性可变?

我有两个类,一个具有“就地运算符”覆盖(例如+=),另一个通过@property. (注意:这从我的实际代码大大简化到重现问题的最低限度。)

现在,当我尝试在暴露的属性上使用该运算符时:

从我发现这是可以预料的,因为它试图将属性设置为owner. 是否有某种方法可以防止属性设置为新对象,同时仍允许我(就地)修改其背后的对象,或者这只是语言的一个怪癖?

(另请参阅this question,但我正尝试另辟蹊径,最好不要恢复到旧式类,因为最终我希望它可以与 Python 3 一起使用。)


与此同时,我用一种做同样事情的方法解决了这个问题。

0 投票
1 回答
2161 浏览

python - 为什么只在新样式类中偏爱 object.__setattr__(self, name, value) ?

根据 Python 2.7.12 文档:

如果__setattr__()要分配给实例属性,它不应该简单地执行self.name = value——这将导致对自身的递归调用。相反,它应该在实例属性字典中插入值,例如self.__dict__[name] = value. 对于新式类,它应该调用具有相同名称的基类方法,而不是访问实例字典,例如, object.__setattr__(self, name, value) .

但是,以下代码可以按预期工作:

我知道super(Class, obj).__setattr__(name, value)可以确保__setattr__所有基类的方法都被调用,但是经典类也可以从基类继承。那么为什么只推荐用于新风格课程呢?

或者,另一方面,为什么不推荐经典课程这样做?

0 投票
1 回答
69 浏览

python - 对象的子类化与将类型定义为元类相同吗?

这是一个老式的类:

这是一个新型类:

这也是一个新式类:

NewStyle和之间有什么区别NewStyle2吗?

我的印象是继承自的唯一效果object实际上是定义type元类,但我找不到任何确认,除了我没有看到任何区别。

0 投票
3 回答
522 浏览

python-2.7 - 为什么 isinstance(nonnewstyle, object) 在 Python 2.X 中返回 true?

我正在阅读一本关于 Python 的教科书,它涵盖了新型类差异。在使用经典类的解释器中观察 Python 2.X 中的以下代码:

(要使用新式类,必须明确派生自 2.X 中的对象类)

那么一个不从对象类派生的对象(如经典类中的情况)如何成为对象的实例呢?在 3.X 和 2.X 的情况下,幕后发生了什么?

至于这是否是一个重复的问题:我已经知道一切在技术上都是一个对象,我想知道在 python 本身的设计中如何明确处理差异,而不是认为 isinstance 的结果是理所当然的。