web-dev-qa-db-ja.com

すでにインスタンス化されたオブジェクトに属性を追加できるのはなぜですか?

私はPythonを勉強していますが、Pythonの概念と概念をすべて理解していると思いますが、今日私は完全に理解していなかったコードを見つけました:

サークルを定義するはずのクラスがありますが、ボディがありません:

class Circle():
    pass

私は属性を定義していないので、どうすればこれを行うことができます:

my_circle = Circle()
my_circle.radius = 12

奇妙な部分は、Pythonは上記のステートメントを受け入れます。なぜPythonがundefined name error。私は、動的型付けを介して、必要なときにいつでも変数をオブジェクトにバインドするだけで、属性radiusが存在しないことを理解していますCircleクラスでこれを行うことができますか?

[〜#〜] edit [〜#〜]:答えにたくさんの素晴らしい情報があります! これらすべての素晴らしい回答をありがとう!答えとしてマークするだけで残念です。

53
NlightNFotis

主要な原則は、宣言のようなものはないということです。つまり、「このクラスにはメソッドfooがある」または「このクラスのインスタンスには属性バーがある」と宣言することはなく、そこに格納するオブジェクトのタイプに関するステートメントを作成することはできません。メソッド、属性、クラスなどを定義するだけで、追加されます。 JBernardoが指摘しているように、どの__init__メソッドもまったく同じことを行います。新しい属性の作成を__init__という名前のメソッドにarbitrarily意的に制限することはあまり意味がありません。そして、実際にはその名前を持たない__init__として関数を保存すると便利な場合があります(例:デコレータ)。そのような制限はそれを壊します。

今、これは普遍的に真実ではありません。組み込み型では、最適化としてこの機能が省略されています。 __slots__ を介して、ユーザー定義クラスでこれを防ぐこともできます。しかし、これは単なるスペースの最適化であり(すべてのオブジェクトに辞書は必要ありません)、正確性の問題ではありません。

セーフティネットが必要な場合は、あまりにも悪いです。 Pythonは提供しません。また、合理的に追加することはできません。最も重要なことは、言語を採用するPythonプログラマーによって回避されることです(読む:ほぼすべての人がと連携)。テストと規律は、依然として正しいことを確認するのに大いに役立ちます。 __init__以外の属性を作成するために自由を使用しないでください(= /// =)、回避できる場合、自動テストを実行します。このようなトリックが原因でAttributeErrorまたは論理エラーが発生することは非常にまれであり、発生するもののほとんどはテストでキャッチされます。

47
user395760

ここでの議論の誤解を明確にするために。このコード:

class Foo(object):
    def __init__(self, bar):
        self.bar = bar

foo = Foo(5)

そしてこのコード:

class Foo(object):
    pass

foo = Foo()
foo.bar = 5

完全に同等です。本当に違いはありません。まったく同じことを行います。この違いは、最初のケースではカプセル化されており、bar属性がFoo型オブジェクトの通常の部分であることは明らかです。 2番目のケースでは、これがそうかどうかは明らかではありません。

最初のケースでは、bar属性を持たないFooオブジェクトを作成できません(おそらく可能ですが、簡単ではありません)、2番目のケースでは、Fooオブジェクトは設定しない限りbar属性を持ちません。

したがって、コードはプログラム的に同等ですが、さまざまなケースで使用されます。

35
Lennart Regebro

Pythonでは、事実上すべてのインスタンス(またはクラス)に任意の名前の属性を保存できます。組み込み型のようにCでクラスを記述するか、特定の名前のみを許可する__slots__を使用して、これをブロックすることができます。

これが機能する理由は、ほとんどのインスタンスが属性を辞書に保存するためです。はい、通常のPython {}で定義するような辞書。辞書は__dict__というインスタンス属性に保存されます。実際、 "クラスつまり、辞書を使用してクラスでできることはすべて実行できます。クラスを使用すると簡単になります。

コンパイル時にすべての属性を定義する必要がある静的言語に慣れています。 Pythonでは、クラス定義は実行済みであり、コンパイルされていません。クラスは他のクラスと同じオブジェクトです。属性を追加するのは、辞書にアイテムを追加するのと同じくらい簡単です。 Pythonは動的言語と見なされる理由です。

19
kindall

いいえ、pythonはそのように柔軟性があり、ユーザー定義のクラスに保存できる属性を強制しません。

ただし、トリックがあります。クラス定義で __slots__属性 を使用すると、__slots__シーケンスで定義されていない追加の属性を作成できなくなります。

>>> class Foo(object):
...     __slots__ = ()
... 
>>> f = Foo()
>>> f.bar = 'spam'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'
>>> class Foo(object):
...     __slots__ = ('bar',)
... 
>>> f = Foo()
>>> f.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: bar
>>> f.bar = 'spam'
17
Martijn Pieters

my_circleradiusデータメンバーを作成します。

my_circle.radiusを要求した場合、例外がスローされます。

>>> print my_circle.radius # AttributeError

興味深いことに、これはクラスを変更しません。その1つのインスタンスだけです。そう:

>>> my_circle = Circle()
>>> my_circle.radius = 5
>>> my_other_circle = Circle()
>>> print my_other_circle.radius # AttributeError
6
inspectorG4dget

Python-Class Data AttributesおよびInstance Data Attributes]には2種類の属性があります。

Pythonは、Data Attributesをその場で作成する柔軟性を提供します。

インスタンスデータ属性はインスタンスに関連しているため、__init__メソッドで行うことも、インスタンスの作成後に行うこともできます。

class Demo(object):
    classAttr = 30
    def __init__(self):
         self.inInit = 10

demo = Demo()
demo.outInit = 20
Demo.new_class_attr = 45; # You can also create class attribute here.

print demo.classAttr  # Can access it 

del demo.classAttr         # Cannot do this.. Should delete only through class

demo.classAttr = 67  # creates an instance attribute for this instance.
del demo.classAttr   # Now OK.
print Demo.classAttr  

インスタンスが作成された後、__init__の内部と外部の2つのインスタンス属性を作成したことがわかります。

ただし、違いは、__init__の内部で作成されたインスタンス属性がすべてのインスタンスに設定されるのに対して、外部で作成された場合は、インスタンスごとに異なるインスタンス属性を使用できることです。

これは、クラスの各インスタンスが同じインスタンス変数のセットを持つJavaとは異なります。

  • 注:-インスタンスを介してクラス属性にアクセスできますが、削除することはできません。また、インスタンスを介してクラス属性を変更しようとすると、実際にはクラス属性をシャドウするインスタンス属性を作成します。
4
Rohit Jain

デルナンが言ったように、__slots__属性。しかし、メモリスペースとアクセスタイプを節約する方法であるという事実は、動的属性を無効にすることも(また)意味するという事実を破棄しません。

スペルミスによる微妙なバグを防ぐためだけに、動的属性を無効にすることは合理的なことです。 「テストと規律」は問題ありませんが、自動化された検証に依存することは間違いなく間違いではありません。

また、2016年にattrsライブラリがバージョン16に達したため(明らかに元の質問と回答の後)、スロットを使用して閉じたクラスを作成することはかつてないほど容易になりました。

>>> import attr
...
... @attr.s(slots=True)
... class Circle:
...   radius = attr.ib()
...
... f = Circle(radius=2)
... f.color = 'red'
AttributeError: 'Circle' object has no attribute 'color'
1
P-Gn