Pythonの古いスタイルと新しいスタイルのクラスの違いは何ですか?


回答:


560

以下からの新スタイルと古典的なクラス

Python 2.1までは、古いスタイルのクラスがユーザーが使用できる唯一のフレーバーでした。

(古いスタイルの)クラスの概念は、タイプの概念とは無関係です。もしx古いスタイルのクラスのインスタンスである場合x.__class__ 、のクラスを指定しますがxtype(x)常に<type 'instance'>です。

これは、クラスに関係なく、すべての古いスタイルのインスタンスが、インスタンスと呼ばれる単一の組み込み型で実装されているという事実を反映しています。

Python 2.2では、新しいスタイルのクラスが導入され、classとtypeの概念が統一されました。新しいスタイルのクラスは、単にユーザー定義型であり、それ以上でもそれ以下でもありません。

xが新しいスタイルのクラスのインスタンスである場合、type(x)通常はと同じですx.__class__(ただし、これは保証されません。新しいスタイルのクラスインスタンスは、に返される値をオーバーライドできますx.__class__)。

新しいスタイルのクラスを導入する主な動機は、完全なメタモデルを備えた統合オブジェクトモデルを提供することです。

また、ほとんどの組み込み型をサブクラス化する機能や、計算されたプロパティを有効にする「記述子」の導入など、多くの直接的な利点もあります。

互換性の理由から、クラスはデフォルトでまだ古いスタイルです

新しいスタイルのクラスは、別の新しいスタイルのクラス(タイプ)を親クラスとして指定するか、他の親が不要な場合は「トップレベルタイプ」オブジェクトを指定することによって作成されます。

新しいスタイルのクラスの動作は、タイプが返すものに加えて、多くの重要な詳細において古いスタイルのクラスの動作とは異なります。

これらの変更の一部は、特別なメソッドが呼び出される方法など、新しいオブジェクトモデルの基本です。その他は、多重継承の場合のメソッド解決順序のように、互換性を考慮してこれまで実装できなかった「修正」です。

Python 3には新しいスタイルのクラスしかありません

からサブクラス化するobjectかどうかに関係なく、クラスはPython 3の新しいスタイルです。


41
これらの違いはどれも、新しいスタイルのクラスを使用する説得力のある理由のように聞こえませんが、誰もが常に新しいスタイルを使用する必要があると言います。必要に応じてダックタイピングを使用している場合は、を使用する必要はありませんtype(x)。組み込み型をサブクラス化していない場合、新しいスタイルのクラスで見ることができる利点はないようです。には余分な型指定があるという欠点があります(object)
再帰的

78
などの特定の機能super()は、古いスタイルのクラスでは機能しません。言うまでもなく、その記事が言うように、MROのような根本的な修正と特別なメソッドがあります。これは、それを使用する正当な理由以上のものです。
John Doe、

21
@ユーザー:古いスタイルのクラスは、2.7でも2.1と同じように動作します。また、奇妙なことを覚えている人はほとんどいないため、ドキュメントではそれらのほとんどについて説明しなくなったため、さらに悪い結果になっています。上記のドキュメントの引用はこれを直接述べています:古いスタイルのクラスに実装できなかった「修正」があります。Python 2.1以降、他の誰も対処していない奇妙なことに遭遇したくない場合、およびドキュメントが説明さえしなくなった場合を除き、古いスタイルのクラスは使用しないでください。
abarnert 2013年

10
2.7で古いスタイルのクラスを使用する場合に遭遇する可能性のある奇妙な例を以下に示します。bugs.python.org
KT。

5
不思議に思う人にとって、Python 3でオブジェクトから明示的に継承する理由は、Pythonの複数のバージョンのサポートが容易になることです。
jpmc26 2014

308

宣言的に:

新しいスタイルのクラスは、objectまたは別の新しいスタイルのクラスから継承します

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

古いスタイルのクラスにはありません。

class OldStyleClass():
    pass

Python 3注:

Python 3は古いスタイルのクラスをサポートしていないため、上記のいずれの形式でも新しいスタイルのクラスになります。


24
新しいスタイルのクラスが別の新しいスタイルのクラスから継承する場合、拡張により、それはから継承しobjectます。
aaronasterling 2010

2
これは古いスタイルのpythonクラスの間違った例ですか? class AnotherOldStyleClass: pass
Ankur Agarwal

11
@abc私はそれを信じてclass A: passおりclass A(): pass、完全に同等です。1つ目は「Aは親クラスを継承しない」ことを意味し、2つ目は「Aは親クラスを継承しない」ことを意味します。これはnot isandによく似ていますis not
eyquem 2013

5
補足として、3.Xの場合、「オブジェクト」の継承は自動的に想定されます(つまり、3.Xで「オブジェクト」を継承しない方法はありません)。下位互換性の理由から、 "(object)"をそこに保持することは悪くありません。
Yo Hsiao

1
継承されたクラスについて技術を習得する場合、この回答では、古いスタイルのクラスから継承して別の古いスタイルのクラスを作成することに注意してください。(書かれているように、この答えは、古いスタイルのクラスから継承できるかどうかをユーザーに問いかけます。可能です。)
jpmc26

224

古いスタイルクラスと新しいスタイルクラスの間の重要な動作の変更

  • 追加
  • 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

8
すてきな明確な要約、ありがとう。「英語で説明するのは難しい」というのは、プレオーダーの深さ優先検索を使用する古いスタイルのクラスとは対照的に、ポストオーダーの深さ優先検索を説明していると思います。(事前注文とは最初の子の前に自分自身を検索することを意味し、事後注文とは最後の子の後に自分自身を検索することを意味します)。
スティーブカーター、

40

古いスタイルのクラスでも、属性の検索はわずかに高速です。これは通常重要ではありませんが、パフォーマンスに敏感な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

5
あなたが実際に気づいたのは興味深いですが、これは、新しいスタイルのクラスがインスタンスdictで属性を見つけたら、それが説明であるかどうか、つまり、返される値を取得するために呼び出す必要があるgetメソッド。古いスタイルのクラスは、追加の計算なしで見つかったオブジェクトを単純に返します(ただし、記述子をサポートしません)。この素晴らしい投稿の詳細は、Guidopython-history.blogspot.co.uk/2010/06/…、特にスロット
xuloChavez

1
CPython 2.7.2では正しくないようです:%timeit aobj.a 10000000 loops, best of 3: 66.1 ns per loop %timeit bobj.a 10000000 loops, best of 3: 53.9 ns per loop
Benedikt Waldvogel

1
私にとっては、x86-64 Linux上のCPython 2.7.2のaobjの方がさらに高速です。
xioxox

41
パフォーマンスに敏感なアプリケーションを純粋なPythonコードに依存することはおそらく悪い考えです。「高速なコードが必要なので、古いスタイルのPythonクラスを使用します。」Numpyは純粋なPythonとしてはカウントされません。
Phillip Cloud

IPython 2.7.6でも、これは正しくありません。'' ''ループあたり456ナノ秒対477のNS '' ''
kmonsoor

37

Guidoは、The New Story on New-Style Classesを書いています。これは、Pythonの新しいスタイルのクラスと古いスタイルのクラスに関する非常に優れた記事です。

Python 3には新しいスタイルのクラスしかありません。「古いスタイルのクラス」を作成した場合でも、それは暗黙的にから派生しobjectます。

新しいスタイルのクラスには、古いスタイルのクラスには欠けている高度な機能がいくつかあります。たとえばsuper、新しいC3 mro、いくつかの魔法のメソッドなどです。


24

ここに、非常に実用的な、真/偽の違いがあります。次のコードの2つのバージョンの唯一の違いは、2番目のバージョンではPersonobjectから継承することです。それ以外は、2つのバージョンは同じですが、結果は異なります。

  1. 昔ながらのクラス

    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>
    >>>
    
  2. 新しいスタイルのクラス

    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>
    >>>

2
「_names_cache」は何をしますか?参照を共有してもらえますか?
Muatik 2014年

4
_names_cacheは、渡したすべての名前をキャッシュ(将来の取得のために保存)する辞書ですPerson.__new__。setdefaultメソッド(任意のディクショナリで定義)は、キーと値の2つの引数を取ります。キーが辞書にある場合、その値を返します。辞書にない場合は、最初に2番目の引数として渡された値に設定し、それを返します。
ychaouche 2014年

4
使い方が間違っています。新しいオブジェクトが既に存在する場合__new__()は、それを作成しないという考え方ですが、この場合は常に呼び出され、常に新しいオブジェクトを作成してからスローします。この場合、aよりもifが推奨され.setdefault()ます。
Amit Upadhyay、2015

しかし、なぜ出力に違いがあるのか​​わかりませんでした。つまり、古いスタイルのクラスでは2つのインスタンスが異なり、したがってFalseが返されましたが、新しいスタイルのクラスでは、両方のインスタンスが同じです。どうやって ?新しいスタイルクラスの変更点は何ですか。2つのインスタンスが同じになりましたが、古いスタイルクラスにはありませんでした。
Pabitra Pati

1
@PabitraPati:ここはちょっと安いデモです。__new__実際には古いスタイルのクラスのものではありません。インスタンスの構築では使用されません(これはdefineのように特別に見えるランダムな名前です__spam__)。したがって、古いスタイルのクラスを構築すると、が呼び出されるだけで__init__、新しいスタイルの構築は__new__、名前を付けてシングルトンインスタンスに結合して構築し、__init__初期化します。
ShadowRanger 2017年

10

新しいスタイルのクラスobjectは、Python 2.2以降から継承され、そのように記述する必要があります(つまりのclass Classname(object):代わりにclass Classname:)。中心的な変更は型とクラスを統合することであり、これの素晴らしい副作用は、組み込み型から継承できることです。

読むdescrintro詳細については。


8

新しいスタイルのクラスsuper(Foo, self)Fooは、クラスとselfインスタンスがどこであるかを使用できます。

super(type[, object-or-type])

タイプの親クラスまたは兄弟クラスにメソッド呼び出しを委任するプロキシオブジェクトを返します。これは、クラスでオーバーライドされた継承されたメソッドにアクセスするのに役立ちます。検索順序は、型自体がスキップされることを除いて、getattr()で使用される順序と同じです。

また、Python 3.xではsuper()、パラメータなしでクラス内で簡単に使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.