「オブジェクト」クラスのインスタンスに属性を設定できません


87

それで、私はこの質問に答えている間Pythonで遊んでいました、そして私はこれが有効でないことを発見しました:

o = object()
o.attr = 'hello'

によるAttributeError: 'object' object has no attribute 'attr'。ただし、オブジェクトから継承されたクラスでは、次のように有効です。

class Sub(object):
    pass

s = Sub()
s.attr = 'hello'

印刷s.attrすると、期待どおりに「hello」が表示されます。なぜそうなのですか?Python言語仕様で、バニラオブジェクトに属性を割り当てることができないと指定しているものは何ですか?


純粋な当て推量:objectタイプは不変であり、新しい属性を追加することはできませんか?これが最も理にかなっているようです。
クリス・ルッツ

2
@ S.Lott:この質問の最初の行を参照してください。純粋に好奇心。
Smashery 2009年

3
タイトルは誤解を招く可能性があります。クラスobjectではなく、objectクラスインスタンスに属性を設定しようとしています。
dhill 2015

回答:


129

任意の属性の割り当てをサポート__dict__するには、オブジェクトにオブジェクトに関連付けられた:dictが必要です。ここに、任意の属性を格納できます。そうでなければ、新しい属性を置く場所がありません。

のインスタンスは-を持ち運びobjectませ__dict__-もしそうなら、恐ろしい循環依存問題の前に(dict他のほとんどすべてのように、;-から継承するので)object、これはPythonのすべてのオブジェクトをdictでサドルします、これはオーバーヘッドを意味します現在dictを持っていない、または必要としないオブジェクトごとの多くのバイト数(基本的に、任意に割り当て可能な属性を持たないすべてのオブジェクトはdictを持っていないか、必要としません)。

たとえば、優れたpymplerプロジェクト(ここからsvnを介して取得できます)を使用して、いくつかの測定を行うことができます...:

>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16

すべてintが16バイトではなく144バイトを占めることを望まないでしょう?-)

さて、(何からでも継承して)クラスを作成すると、状況が変わります...:

>>> class dint(int): pass
... 
>>> asizeof.asizeof(dint(23))
184

...これ__dict__ 追加されました(さらに、もう少しオーバーヘッドがあります)-dintインスタンスは任意の属性を持つことができますが、その柔軟性にはかなりのスペースコストがかかります。

では、追加の属性が1つintだけのsが必要な場合はどうでしょうか...?まれなニーズですが、Pythonはその目的のために特別なメカニズムを提供します...foobar

>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
... 
>>> asizeof.asizeof(fint(23))
80

...ではない、かなりのように小さなとしてint、あなたを気に!(または、2つint、1つと1つ、selfもう1つはself.foobar再割り当てできます)が、確かにdint。よりもはるかに優れています。

クラスに__slots__特別な属性(文字列のシーケンス)がある場合、classステートメント(より正確にはデフォルトのメタクラスtype)は、そのクラスのすべてのインスタンスに(したがって任意の属性を持つ機能)を備えているわけではなく__dict__、有限です。 、指定された名前の「スロット」(基本的にはそれぞれがオブジェクトへの1つの参照を保持できる場所)の厳密なセット。

柔軟性が失われる代わりに、インスタンスごとに多くのバイトが得られます(おそらく、無数のインスタンスが活発に機能している場合にのみ意味がありますが、そのユースケースがあります)。


5
これは、メカニズムがどのように実装されているかを説明していますが、なぜこのように実装されているのかについては説明していません。オーバーヘッドのマイナス面はありませんが、いくつかの単純さを追加する、その場でdictを追加することを実装するための少なくとも2つまたは3つの方法を考えることができます。
ГеоргиКременлиев

空でないことを注意__slots__などの可変長型で動作しない strtupleともPythonの3中をint
arekolek 2016


それは偉大な説明だが、それでも、なぜ(またはどのように)答えていないSubしている__dict__属性とオブジェクトがない、ということSubから継承objectその属性(など他の人がどのようにされ、__module__)継承に追加しますか?これは新しい質問かもしれません
Rodrigo E. Principe

2
オブジェクト__dict__は最初に必要になったときにのみ作成されるため、メモリコストの状況は、asizeof出力で見た目ほど単純ではありません。(マテリアライズasizeofを回避__dict__する方法がわかりません。)この例では、必要になるまでdictがマテリアライズされないことを確認できます。また、__dict__マテリアライズを担当するコードパスの1つをここで確認できます
user2357112は、

17

他の回答者が言ったように、objectはありません__dict__。またはを含むすべてのタイプのobject基本クラスです。したがって、によって提供されるものはすべて、彼らにとっても負担になります。オプションのような単純なものでも、値ごとに追加のポインターが必要になります。これは、非常に限られたユーティリティのために、システム内の各オブジェクトに追加の4〜8バイトのメモリを浪費します。intstrobject __dict__


Python 3.3以降では、ダミークラスのインスタンスを実行する代わりにtypes.SimpleNamespace、これを使用できます(使用する必要があります)。


4

それは単に最適化によるものです。

ディクトは比較的大きいです。

>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140

Cで定義されているほとんどの(おそらくすべての)クラスには、最適化のためのdictがありません。

あなたが見れば、ソースコードは、オブジェクトの辞書を持っているかいないかどうかを確認するために多くのチェックがあることがわかります。


1

それで、私自身の質問を調べて、Python言語についてこれを発見しました:intのようなものから継承することができ、同じ振る舞いを見ることができます:

>>> class MyInt(int):
       pass

>>> x = MyInt()
>>> print x
0
>>> x.hello = 4
>>> print x.hello
4
>>> x = x + 1
>>> print x
1
>>> print x.hello
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: 'int' object has no attribute 'hello'

最後のエラーは、add関数がintを返すためだと思い__add__ます。そのため、カスタム属性を保持するには、などの関数をオーバーライドする必要があります。しかし、「オブジェクト」を「int」のように考えると、これはすべて私には理にかなっています(私は思います)。


0

これは、オブジェクトがクラスではなく「タイプ」であるためです。一般に、C拡張機能で定義されているすべてのクラス(すべての組み込みデータ型やnumpy配列など)では、任意の属性を追加できません。


ただし、Sub()がオブジェクトであるのと同様に、object()はオブジェクトです。私の理解では、sとoはどちらもオブジェクトです。では、sとoの根本的な違いは何ですか?1つはインスタンス化されたタイプで、もう1つはインスタンス化されたクラスですか?
Smashery 2009年

ビンゴ。それがまさに問題です。
ライアン

1
Python 3では、型とクラスの違いは実際には存在しません。したがって、「タイプ」と「クラス」はかなり同義語になりました。ただし__dict__、Alex Martelliの理由により、がないクラスに属性を追加することはできません。
PM2Ring19年


-2

これは(IMO)Pythonの基本的な制限の1つです。クラスを再度開くことはできません。ただし、実際の問題は、Cで実装されたクラスを実行時に変更できないという事実が原因であると思います...サブクラスは変更できますが、基本クラスは変更できません。

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