2

次のコードがあるとします。

class A:
    var = 0
a = A()

はそれを理解してa.varおりA.var、さまざまな変数であり、なぜこのようなことが起こるのか理解していると思います。なぜ誰かがインスタンスのクラス変数を変更したいので、それはPythonのデータモデルの単なる副作用だと思いました。

しかし、今日、私はそのような使用法の奇妙な例に出くわしました:それはグーグルアプリエンジンdb.Modelリファレンスにあります。Google App Engineデータストアは、db.Modelクラスを継承し、クラス変数としてキーを導入することを前提としています。

class Story(db.Model):
    title = db.StringProperty()
    body = db.TextProperty()
    created = db.DateTimeProperty(auto_now_add=True)
s = Story(title="The Three Little Pigs")

なぜ彼らは私がそのようにすることを期待しているのか分かりませんか?コンストラクターを導入して、インスタンス変数のみを使用してみませんか?

4

6 に答える 6

3

db.Modelクラスは、従来のModelViewControllerデザインパターンの「Model」スタイルクラスです。そこにある各割り当ては、実際にはデータベースに列を設定すると同時に、プログラミングに使用できる使いやすいインターフェイスを提供します。これが理由です

title="The Three Little Pigs"

データベース内のオブジェクトと列を更新します。

このパスオフロジックを処理するコンストラクター(db.Modelには間違いありません)があり、キーワードargsリストを取得してダイジェストし、このリレーショナルモデルを作成します。

これが、変数がそのまま設定され、関係が維持される理由です。

編集:それをよりよく説明させてください。通常のクラスは、オブジェクトのブループリントを設定するだけです。インスタンス変数とクラス変数があります。db.Modelを継承しているため、これは実際には3番目のことを実行しています。データベースに列定義を設定することです。この3番目のタスクを実行するために、属性の設定や取得などの変更を舞台裏でEXTENSIVEにすることです。db.Modelから継承すると、実際にはクラスではなく、DBテンプレートになります。簡単に言えば、これはクラスの使用の非常に特殊なエッジケースです

于 2012-07-10T22:08:28.313 に答える
1

すべての変数がとして宣言されているinstance variables場合、classesusingStoryクラスはそこから何も継承superclassしません。

于 2012-07-10T22:04:40.727 に答える
1

モデルとプロパティのドキュメントから、モデルが__getattr__メソッドと__setattr__メソッドをオーバーライドしたように見えるため、実際には「Story.title=...」は実際にはインスタンス属性を設定しません。代わりに、インスタンスのプロパティとともに保存される値を設定します。

story .__ dict __ ['title']を要求した場合、それはあなたに何を与えますか?

于 2012-07-10T22:11:57.097 に答える
1

a.varとA.varが異なる変数であることを理解しています

まず最初に:今のところ、いいえ、そうではありません

Pythonでは、classブロック内で宣言するものはすべてクラスに属します。インスタンスにその名前の何かがまだない場合は、インスタンスを介してクラスの属性を検索できます。インスタンスの属性に割り当てると、以前に属性があったかどうかに関係なく、インスタンスにその属性が含まれるようになります。(__init__この点では、これは単なる別の関数です。Pythonの機構によって自動的に呼び出されますが、オブジェクトに属性を追加するだけで、クラスのすべてのインスタンスのコンテンツに何らかのテンプレートを魔法のように指定することはありません。魔法があります。__slots__そのためのクラス属性ですが、それでも期待どおりの動作はしません。)

しかし、今のところ、それ自体aはありませんので、を参照してください。また、インスタンスを介してクラス属性を変更できますが、置換ではなく変更に注意してください。もちろん、これには、属性の元の値が変更可能なものである必要があります。修飾は修飾されますが、修飾されません。.vara.varA.varliststr

ただし、GAEの例はまったく異なるものです。クラスStoryには、具体的には「プロパティ」である属性があり、それらを「割り当てる」と、さまざまな魔法を実行できます。__getattr__これは、クラス__setattr__などのメソッドを使用して割り当て構文の動作を変更することで機能します。

于 2012-07-10T22:59:04.343 に答える
1

他の答えはほとんど正しいですが、1つの重要なことを見逃しています。

このようなクラスを定義する場合:

class Foo(object):
  a = 5

およびインスタンス:

myinstance = Foo()

次にFoo.a、とmyinstance.aはまったく同じ変数です。一方を変更するともう一方も変更され、のインスタンスを複数作成するとFoo、それぞれの.aプロパティは同じ変数になります。これは、Pythonが属性アクセスを解決する方法によるものです。最初にオブジェクトのdictを調べ、そこで見つからない場合は、クラスのdictを調べます。

これは、変数の共有の性質を考えると、割り当てが期待どおりに機能しない理由を説明するのにも役立ちます。

>>> bar = Foo()
>>> baz = Foo()
>>> Foo.a = 6
>>> bar.a = 7
>>> bar.a
7
>>> baz.a
6

ここで起こったことは、に割り当てたときに、要求したときFoo.aにすべてのインスタンスがFoo通常解決する変数を変更したことですinstance.a。しかし、に割り当てたときbar.a、Pythonはそのインスタンスに、という新しい変数を作成しましたa。これにより、クラス変数がマスクされます。これ以降、その特定のインスタンスには常に独自のローカル値が表示されます。

クラスの各インスタンスに個別の変数を5に初期化させたい場合、通常の方法は次のようになります。

class Foo(object);
  def __init__(self):
    self.a = 5

つまりa、新しいインスタンスの変数を5に設定するコンストラクターを使用してクラスを定義します。

最後に、App Engineが行っているのは、記述子と呼ばれるまったく異なる種類の黒魔術です。つまり、Pythonではオブジェクトが特別なメソッド__get____set__メソッドを定義できます。これらの特別なメソッドを定義するクラスのインスタンスがクラスにアタッチされ、そのクラスのインスタンスを作成すると、属性にアクセスしようとすると、インスタンスまたはクラス変数を設定または返す代わりに、特別なメソッド__get____set__メソッドが呼び出されます。 。記述子のより包括的な紹介はここにありますが、ここに簡単なデモがあります:

class MultiplyDescriptor(object):
  def __init__(self, multiplicand, initial=0):
    self.multiplicand = multiplicand
    self.value = initial

  def __get__(self, obj, objtype):
    if obj is None:
      return self
    return self.multiplicand * self.value

  def __set__(self, obj, value):
    self.value = value

今、あなたはこのようなことをすることができます:

class Foo(object):
  a = MultiplyDescriptor(2)

bar = Foo()
bar.a = 10
print bar.a # Prints 20!

記述子は、驚くべき量のPython言語の背後にある秘密のソースです。たとえば、propertyメソッド、静的メソッド、クラスメソッド、およびその他の多くのものと同様に、記述子を使用して実装されます。

于 2012-07-27T07:48:14.640 に答える
0

これらのクラス変数は、GoogleAppEngineがモデルを生成するためのメタデータです。

参考までに、あなたの例では、a.var == A.var

>>> class A:
...     var = 0
... 
... a = A()
... A.var = 3
... a.var == A.var
1: True
于 2012-07-10T22:05:32.817 に答える