1. ホーム
  2. python

[解決済み] objectクラスのインスタンスに属性を設定できない

2023-02-26 19:41:41

質問

というわけで、Pythonで遊んでいるときに この質問 で、これが有効でないことを発見しました。

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

により AttributeError: 'object' object has no attribute 'attr' . しかし、objectから継承されたクラスであれば、有効である。

class Sub(object):
    pass

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

印刷 s.attr は期待通りに'hello'を表示します。なぜこのようなことが起こるのでしょうか?Pythonの言語仕様の中で、vanillaオブジェクトに属性を割り当ててはいけないと定めているものは何ですか?

どのように解決するのですか?

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

のインスタンスは object ではない を持ち歩く __dict__ -- もしそうなら、恐ろしい循環依存性の問題が起こる前に(というのは dict は他のほとんどのものと同じように object ;-) を継承する場合、これは を鞍替えすることになります。 のオーバーヘッドを意味します。 多くの バイトのオーバーヘッドを意味します(基本的に、任意に割り当て可能な属性を持たないすべてのオブジェクトは、dict を持たないし、必要としません)。

たとえば、優れた pympler プロジェクト (svn経由で次の場所から入手できます) を使ってみましょう。 ここで から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 インスタンスは任意の属性を持つことができますが、その柔軟性のためにかなりのスペースコストを支払うことになります。

では、もしあなたが int 一つ という属性があります。 foobar ...? これは稀なニーズですが、Pythonはこの目的のために特別なメカニズムを提供しています...

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

...ではなく かなり のような小さな int と同じくらい小さいです。(あるいは、2つの int があり、ひとつは self と、ひとつは self.foobar -- は再割り当て可能です) が、確かに dint .

クラスが __slots__ という特別な属性(文字列の列)がある場合、そのクラスは class ステートメント(より正確にはデフォルトのメタクラス。 type ) は ではなく は、そのクラスのすべてのインスタンスに __dict__ (したがって、任意の属性を持つ能力) を持たず、与えられた名前を持つ "slots" (基本的に、それぞれがあるオブジェクトへの1つの参照を保持できる場所) の有限で硬いセットだけです。

失われた柔軟性と引き換えに、インスタンスごとに多くのバイトを得ることができます (おそらく、何十億ものインスタンスが飛び回っている場合のみ意味がありますが、ここでは の使用例があります)。