問題タブ [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.
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 にメモリがどのように割り当てられたかに注意してください。
次に、このポインターを に型キャストし、の直後に続くBridge
4 または 8 ( ) バイトを使用して、対応する C++ クラス インスタンスを指します (これは、上記のように接続されます)。sizeof(void*)
PyObject
extension_object_init
これが機能するためには、次のものが必要です。
a)subtype->tp_alloc(subtype,0)
余分なsizeof(void*)
バイトを割り当てる必要があります b)PyObject
は を超えるメモリを必要としませんsizeof(PyObject_HEAD)
。
この時点での主な質問の 1 つはPyObject
、Python ランタイムが作成したが Bridge のフィールドとderived_instance
重複しないことを保証できるかということです。ExtObjBase* m_pycxx_object
私はそれに答えようとします。割り当てられるメモリの量を決定するのは米国です。作成するときに、このタイプの新しいインスタンスに割り当てるNewStyleClass_PyTypeObject
メモリ量を入力します。PyTypeObject
あなたはそれが次のように入っているのを見ることができますtable->tp_basicsize
しかし今では、生成された PyObject-sNewStyleClass_PyTypeObject
が追加の割り当てられたメモリを必要としないことは明らかです。
これは、このBridge
メカニズム全体が不要であることを意味します。
そして、PyObject を の基底クラスとして使用し、NewStyleClassCXXClass
この基底を初期化して、Python ランタイムの PyObjectd = Derived()
が実際にこの基底になるようにする PyCXX の独自の手法は、見栄えがします。シームレスな型キャストが可能になるためです。
Python ランタイムが からスロットを呼び出すときは常にNewStyleClass_PyTypeObject
、最初のパラメーターとして d の PyObject へのポインターが渡され、型キャストして に戻すことができますNewStyleClassCXXClass
。<-- 'X' (上記参照)
だから本当に私の質問は、なぜ私たちはこれをやらないのですか? PyObject の余分な割り当てを強制することから派生することについて何か特別なことはありNewStyleClass
ますか?
派生クラスの場合の作成手順がわからないことに気づきました。エリの投稿はそれをカバーしていませんでした。
という事実が関係していると思われます
^ この変数名は 'subtype' です。これがわかりません。これが鍵を握っているのではないでしょうか。
編集: PyCXX が初期化に sizeof(FinalClass) を使用している理由について、1 つの考えられる説明を考えました。それは試行錯誤したアイデアの遺物かもしれません。つまり、Python の tp_new 呼び出しが FinalClass (ベースとして PyObject を持つ) に十分なスペースを割り当てる場合、'placement new' を使用するか、狡猾な reinterpret_cast ビジネスを使用して、その正確な場所に新しい FinalClass を生成できる可能性があります。私の推測では、これが試行され、何らかの問題を引き起こすことが判明し、回避され、遺物が置き去りにされた可能性があります。
python - クラスが豊富な比較メソッドを実装しているかどうかを確認する方法は?
古いスタイルのクラスを使用するだけで、次のようにhasattr
機能します。
新しいスタイル クラスを使用すると、すべてのクラスに次の属性があります__eq__
。
これは期待される動作であり、hasattr(object, '__eq__')
も返しますTrue
。これは、すべてのリッチ比較メソッドに当てはまります。
使用できない場合、クラスがリッチ比較メソッドを実装しているかどうかを確認するにはどうすればよいhasattr
ですか? 頭に浮かぶことの 1 つは、メソッドを呼び出して、NotImplemented
例外が発生するかどうかを確認することです。しかし、これらのメソッドを呼び出すと、予期しない損害が発生する可能性があります。
python - Python 属性取得プロセスと混同: 無限再帰?
オブジェクトの属性を取得したいとします: smth = object.attr
. また、属性がどのオブジェクトにあるかが既にわかっているとします。それを A というクラスとします。
私の知る限り、属性取得手順は次のようになります。
- クラス A の属性ディクショナリで記述子を見つけます。
- 属性のゲッターが存在する場合は、その出力を返します。それ以外の場合は、属性自体を返します。
ここに問題があります。属性がゲッターを持つ記述子であるとします。その場合、 を解決するobject.descriptor
にdescriptor.__get__
は、非データ記述子 (関数) を解決する必要があり、これも解決する必要があります。この再帰の終わりはどこですか?
python - 読み取り専用プロパティを変更可能にするにはどうすればよいですか?
私は 2 つのクラスを持っています。1 つは「インプレース演算子」オーバーライド (たとえば+=
) を持ち、もう 1 つは を介して最初のインスタンスを公開します@property
。(注:これは、実際のコードから問題を再現する最小限のものまで大幅に簡略化されています。)
ここで、公開されたプロパティでその演算子を使用しようとすると:
プロパティを on に設定しようとしているので、これは予想されることowner
です。 プロパティを新しいオブジェクトに設定するのを防ぎながら、その背後にあるオブジェクトを (その場で)変更できるようにする方法はありますか、それとも言語の癖ですか?
(この質問も参照してください。ただし、最終的には 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__
いますが、クラシック クラスは基本クラスから継承することもできます。では、なぜ新しいスタイルのクラスにのみ推奨されるのでしょうか?
または、一方で、古典的なクラスでそうすることが推奨されないのはなぜですか?