问题标签 [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.
python-2.7 - 当我(错误?)使用超级时,我的方法不会被调用
输出中没有“UCD.edit”,这意味着没有调用 UpperCaseDocument.edit。我错过了什么?
谢谢你的帮助。
(如果您认为 super 只会调用一个基类方法,请使用以下代码进行测试:
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 创建一个对象
c++ - 如何整理/修复 PyCXX 创建的新型 Python 扩展类?
我几乎完成了对 C++ Python 包装器 (PyCXX) 的重写。
原始允许新旧样式扩展类,但也允许从新样式类派生:
代码很复杂,并且似乎包含错误(为什么 PyCXX 以它的方式处理新型类?)
我的问题是:PyCXX 复杂机制的基本原理/理由是什么?有没有更清洁的替代品?
我将尝试在下面详细说明我在此查询中所处的位置。首先,我将尝试描述 PyCXX 目前正在做什么,然后我将描述我认为可能需要改进的地方。
当 Python 运行时遇到 时d = Derived()
,它会执行PyObject_Call( ob ) where ob is the
PyTypeObject for
NewStyleClass . I will write
ob as
NewStyleClass_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_new
和tp_init
槽中:NewStyleClass_PyTypeObject
请注意,我们需要将 Python Derived 实例绑定在一起,它是对应的 C++ 类实例。(为什么?解释如下,见“X”)。为此,我们使用:
现在这座桥提出了一个问题。我非常怀疑这种设计。
请注意如何为这个新的 PyObject 分配内存:
然后我们将 this 指针类型转换为,并使用紧随其后Bridge
的 4 或 8 ( ) 个字节指向相应的 C++ 类实例(如上所示,这被连接起来了)。sizeof(void*)
PyObject
extension_object_init
现在要让它工作,我们需要:
a)subtype->tp_alloc(subtype,0)
必须分配一个额外的sizeof(void*)
字节 b)PyObject
不需要任何超出 的内存sizeof(PyObject_HEAD)
,因为如果它这样做了,那么这将与上述指针冲突
在这一点上我的一个主要问题是:我们能否保证PyObject
Python 运行时为我们创建的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_PyTypeObject
从NewStyleClassCXXClass
. <-- 'X'(上面引用过)
所以真的我的问题是:我们为什么不这样做呢?从强制为 PyObject 进行额外分配的派生有什么特别之处NewStyleClass
吗?
我意识到我不理解派生类的创建顺序。伊莱的帖子没有涵盖这一点。
我怀疑这可能与以下事实有关
^ 这个变量名是'subtype' 我不明白这个,我想知道这是否可能是关键。
编辑:我想到了为什么 PyCXX 使用 sizeof(FinalClass) 进行初始化的一种可能解释。它可能是一个经过尝试和放弃的想法的遗物。即,如果 Python 的 tp_new 调用为 FinalClass(它以 PyObject 作为基础)分配了足够的空间,也许可以使用“placement new”或一些巧妙的 reinterpret_cast 业务在该确切位置生成一个新的 FinalClass。我的猜测是这可能已经尝试过了,发现会造成一些问题,解决了问题,然后遗物就被抛在了后面。
python - 描述符在设置为实例时不像描述符——为什么不呢?
我有一个枚举描述符,我希望能够动态地将其添加到类中。这里是
为了尝试理解这种行为,我编写了一个单元测试来检查它
这一切正常吗?为什么它的工作方式不同,无论它是成员__dict__
还是class.__dict__
???
请告诉我有更好的方法来做到这一点!
python - 如何验证类是否实现了丰富的比较方法?
使用旧样式类只需使用hasattr
作品:
使用新样式类,每个类都具有以下属性__eq__
:
这hasattr(object, '__eq__')
也是返回的预期行为True
。对于每种丰富的比较方法都是如此。
如果我不能使用,如何验证类是否实现了丰富的比较方法hasattr
?想到的一件事是调用该方法并查看它是否引发NotImplemented
异常。但是调用这些方法可能会产生意想不到的损害。
python - 对python属性获取过程感到困惑:无限递归?
假设我们要获取一个对象的属性:smth = object.attr
。还假设我们已经知道我们的属性位于哪个对象中,假设它是名为 A 的类。
据我所知,属性获取过程如下所示:
- 在 A 类的属性字典中找到描述符。
- 如果存在,则返回属性的 getter 的输出。否则,返回属性本身。
我在这里看到一个问题。假设我们的属性是一个带有 getter 的描述符。在这种情况下,要解析object.descriptor
,我们必须解析descriptor.__get__
哪个是非数据描述符(函数),也需要解析。这个递归的终点在哪里?
python - 如何使只读属性可变?
我有两个类,一个具有“就地运算符”覆盖(例如+=
),另一个通过@property
. (注意:这从我的实际代码大大简化到重现问题的最低限度。)
现在,当我尝试在暴露的属性上使用该运算符时:
从我发现这是可以预料的,因为它试图将属性设置为owner
. 是否有某种方法可以防止将属性设置为新对象,同时仍允许我(就地)修改其背后的对象,或者这只是语言的一个怪癖?
(另请参阅this question,但我正尝试另辟蹊径,最好不要恢复到旧式类,因为最终我希望它可以与 Python 3 一起使用。)
与此同时,我用一种做同样事情的方法解决了这个问题。
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__
所有基类的方法都被调用,但是经典类也可以从基类继承。那么为什么只推荐用于新风格课程呢?
或者,另一方面,为什么不推荐经典课程这样做?
python - 对象的子类化与将类型定义为元类相同吗?
这是一个老式的类:
这是一个新型类:
这也是一个新式类:
NewStyle
和之间有什么区别NewStyle2
吗?
我的印象是继承自的唯一效果object
实际上是定义type
元类,但我找不到任何确认,除了我没有看到任何区别。
python-2.7 - 为什么 isinstance(nonnewstyle, object) 在 Python 2.X 中返回 true?
我正在阅读一本关于 Python 的教科书,它涵盖了新型类差异。在使用经典类的解释器中观察 Python 2.X 中的以下代码:
(要使用新式类,必须明确派生自 2.X 中的对象类)
那么一个不从对象类派生的对象(如经典类中的情况)如何成为对象的实例呢?在 3.X 和 2.X 的情况下,幕后发生了什么?
至于这是否是一个重复的问题:我已经知道一切在技术上都是一个对象,我想知道在 python 本身的设计中如何明确处理差异,而不是认为 isinstance 的结果是理所当然的。