Pythonの古いスタイルと新しいスタイルのクラスの違いは何ですか?いつどちらを使うべきですか?
8 に答える
Python 2.1までは、古いスタイルのクラスがユーザーが利用できる唯一のフレーバーでした。
(古いスタイルの)クラスの概念は、型の概念とは無関係です。
x
が古いスタイルのクラスのインスタンスである場合、はのクラスをx.__class__
指定しますx
が、type(x)
常に<type 'instance'>
です。これは、クラスに関係なく、すべての古いスタイルのインスタンスが、インスタンスと呼ばれる単一の組み込み型で実装されているという事実を反映しています。
クラスとタイプの概念を統一するために、Python2.2で新しいスタイルのクラスが導入されました。新しいスタイルのクラスは、単にユーザー定義の型であり、それ以上でもそれ以下でもありません。
xが新しいスタイルのクラスのインスタンスである場合、
type(x)
通常はと同じx.__class__
です(ただし、これは保証されていません。新しいスタイルのクラスインスタンスは、に対して返される値をオーバーライドできますx.__class__
)。新しいスタイルのクラスを導入する主な動機は、完全なメタモデルを備えた統合オブジェクトモデルを提供することです。
また、ほとんどの組み込み型をサブクラス化する機能や、計算されたプロパティを有効にする「記述子」の導入など、多くの直接的な利点もあります。
互換性の理由から、クラスはデフォルトでまだ古いスタイルです。
新しいスタイルのクラスは、別の新しいスタイルのクラス(つまり、タイプ)を親クラスとして指定するか、他の親が必要ない場合は「最上位のタイプ」オブジェクトを指定することによって作成されます。
新しいスタイルのクラスの動作は、どのタイプが返されるかに加えて、いくつかの重要な詳細が古いスタイルのクラスの動作とは異なります。
これらの変更のいくつかは、特別なメソッドが呼び出される方法のように、新しいオブジェクトモデルの基本です。その他は、多重継承の場合のメソッド解決順序など、互換性の問題のために以前は実装できなかった「修正」です。
Python3には新しいスタイルのクラスしかありません。
サブクラスを作成するかどうかに関係
object
なく、Python3ではクラスは新しいスタイルです。
宣言について:
新しいスタイルのクラスは、 objectまたは別の新しいスタイルのクラスから継承します。
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
古いスタイルのクラスはそうではありません。
class OldStyleClass():
pass
Python 3 注:
Python 3 は古いスタイルのクラスをサポートしていないため、上記のどちらの形式でも新しいスタイルのクラスになります。
古いスタイル クラスと新しいスタイル クラスの間の重要な動作の変更
- 超追加
- MRO の変更 (以下で説明)
- 記述子が追加されました
Exception
(以下の例)から派生しない限り、新しいスタイル クラス オブジェクトを発生させることはできません。__slots__
追加した
MRO (メソッド解決順序) が変更されました
他の回答で言及されていましたが、クラシック MRO と C3 MRO (新しいスタイル クラスで使用される) の違いの具体例を次に示します。
問題は、多重継承で属性 (メソッドとメンバー変数を含む) が検索される順序です。
クラシック クラスは、左から右に深さ優先検索を行います。最初の試合でストップ。__mro__
属性がありません。
class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 0
assert C21().i == 2
try:
C12.__mro__
except AttributeError:
pass
else:
assert False
新しいスタイルのクラスMRO は、単一の英文に統合するのがより複雑です。ここで 詳しく 説明 し てい る. そのプロパティの 1 つは、基本クラスは、そのすべての派生クラスが検索された後にのみ検索されるということです。__mro__
検索順序を示す属性を持っています。
class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 2
assert C21().i == 2
assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)
から派生しない限り、新しいスタイル クラス オブジェクトを発生させることはできません。Exception
Python 2.5 周辺では多くのクラスを生成できましたが、Python 2.6 周辺ではこれが削除されました。Python 2.7.3 の場合:
# OK, old:
class Old: pass
try:
raise Old()
except Old:
pass
else:
assert False
# TypeError, new not derived from `Exception`.
class New(object): pass
try:
raise New()
except TypeError:
pass
else:
assert False
# OK, derived from `Exception`.
class New(Exception): pass
try:
raise New()
except New:
pass
else:
assert False
# `'str'` is a new style object, so you can't raise it:
try:
raise 'str'
except TypeError:
pass
else:
assert False
古いスタイルのクラスは、属性ルックアップに関してはまだわずかに高速です。これは通常は重要ではありませんが、パフォーマンスが重要な Python 2.x コードでは役立つ場合があります。
[3]: クラス A: ...: def __init__(self): ...: self.a = 'こんにちは' ...: [4]: クラス B(オブジェクト): ...: def __init__(self): ...: self.a = 'こんにちは' ...: [6]: aobj = A() [7]: bobj = B() [8]: %timeit aobj.a 10000000 ループ、ベストオブ 3: ループあたり 78.7 ns [10]: %timeit bobj.a 10000000 ループ、ベストオブ 3: ループあたり 86.9 ns
Guido はThe Inside Story on New-Style Classesを書きました。これは、Python の新しいスタイルと古いスタイルのクラスに関する非常に優れた記事です。
Python 3 には new-style クラスしかありません。「古いスタイルのクラス」を書いても、それは暗黙のうちに から派生していobject
ます。
super
新しいスタイルのクラスには、新しいC3 mro、いくつかの魔法のメソッドなど、古いスタイルのクラスに欠けているいくつかの高度な機能があります。
これは非常に実用的な真/偽の違いです。次のコードの 2 つのバージョンの唯一の違いは、2 番目のバージョンではPersonがobjectから継承されていることです。それ以外は、2 つのバージョンは同じですが、結果が異なります。
古いスタイルのクラス
class Person(): _names_cache = {} def __init__(self,name): self.name = name def __new__(cls,name): return cls._names_cache.setdefault(name,object.__new__(cls,name)) ahmed1 = Person("Ahmed") ahmed2 = Person("Ahmed") print ahmed1 is ahmed2 print ahmed1 print ahmed2 >>> False <__main__.Person instance at 0xb74acf8c> <__main__.Person instance at 0xb74ac6cc> >>>
新しいスタイルのクラス
class Person(object): _names_cache = {} def __init__(self,name): self.name = name def __new__(cls,name): return cls._names_cache.setdefault(name,object.__new__(cls,name)) ahmed1 = Person("Ahmed") ahmed2 = Person("Ahmed") print ahmed2 is ahmed1 print ahmed1 print ahmed2 >>> True <__main__.Person object at 0xb74ac66c> <__main__.Person object at 0xb74ac66c> >>>
新しいスタイルのクラスは から継承されており、Python 2.2 以降では (つまりの代わりに) そのobject
ように記述する必要があります。コアとなる変更は、型とクラスを統合することです。これの良い副作用は、組み込み型から継承できるようになることです。class Classname(object):
class Classname:
詳細については、説明をお読みください。
新しいスタイルのクラスでは、クラスはsuper(Foo, self)
どこで、インスタンスはどこであるかを使用できます。Foo
self
super(type[, object-or-type])
タイプの親クラスまたは兄弟クラスにメソッド呼び出しを委譲するプロキシ オブジェクトを返します。これは、クラスでオーバーライドされた継承メソッドにアクセスする場合に便利です。検索順序は、タイプ自体がスキップされることを除いて、getattr() で使用される順序と同じです。
また、Python 3.x ではsuper()
、パラメーターなしでクラス内で単純に使用できます。