2017-05-03 10 views
2

私は独学のプログラマーです。私は最近、Pythonを学んでいます。私は奇妙な問題に遭遇しましたが、それは私がPythonの構文やプログラムの流れを知らない結果であると想像しています。Pythonクラスのインスタンス変数の分離

私はファイルTestClass.pyにあるTestという1つのクラスを持っています。 `

class Test: 

    __tags = {} 
    __fields = {} 

    def __init__(self, tags: dict={}, fields: dict={}): 
     self.__tags = tags 
     self.__fields = fields 

    def setTag(self, key, value): 
     self.__tags[key] = value 

    def getTag(self, key): 
     return self.__tags[key] 

    def setField(self, key, value): 
     self.__fields[key] = value 

    def getField(self, key): 
     return self.__fields[key] 


    def getAll(self): 
     return [ 
      { 
       'tags': self.__tags, 
       'fields': self.__fields 
      } 
     ] 

私は物事が奇妙な取得する場所print文があるtest.py

import TestClass 

t1 = TestClass.Test() 
t1.setTag('test1', 'value1') 
t1.setField('testfield', 'fieldvalue') 

t2 = TestClass.Test() 
t2.setTag('test2', 'value2') 

print(t1.getAll()) 
print(t2.getAll()) 

、手続き型のコードを含むファイルで、このクラスの機能をテストしています。

[{'tags': {'test1': 'value1'}, 'fields': {'testfield': 'fieldvalue'}}] 
[{'tags': {'test2': 'value2'}, 'fields': {}}] 

しかし、実際の出力は...なぜしかし

[{'tags': {'test2': 'value2', 'test1': 'value1'}, 'fields': {'testfield': 'fieldvalue'}}] 
[{'tags': {'test2': 'value2', 'test1': 'value1'}, 'fields': {'testfield': 'fieldvalue'}}] 

です:出力はすべきですか?

編集:のPython 3.5

答えて

3

あなただけの1ではありません落ちたが、二つのPythonでも新規参入者のための "トラップ" を知られています。

この動作が予想され、それを修正するために、あなたがあなたのクラス宣言の始まりを変更する必要があります。今、「なぜ?」を理解

from typing import Optional 


class Test: 
    def __init__(self, tags: Optional(dict)=None, fields: Optional(dict)=None): 
     self.__tags = tags or {} 
     self.__fields = fields or {} 
     ... 
    ... 


ザ・Pythonコード - を含む式を、モジュールレベルまたはクラス本体の中に存在するか、または関数またはメソッドの宣言は、そのモジュールが最初にロードされたときに一度だけ処理されます。

これは、クラス本体で作成した空の辞書と、この時点で辞書として作成された__init__のデフォルトパラメータを意味し、クラスがインスタンス化されるたびに再利用されます。

最初の部分は、Pythonでクラス本体に直接宣言された属性がクラスの属性であることです。つまり、そのクラスのすべてのインスタンスで共有されます。メソッド内にself.attribute = XXXの属性を割り当てた場合は、インスタンス属性を作成します。

第2の問題は、関数/メソッドパラメータの既定値が関数コードとともに保存されるためです。空に宣言した辞書は、各メソッド呼び出し後に同じであり、クラスのすべてのインスタンスで共有されます。

これを避けるための通常のパターンは、Noneまたは選択した他のセンチネル値と、テストする関数本体内にデフォルトのパラメータを設定することです:これらのパラメータに値が送られなかった場合は、変更可能なオブジェクト)インスタンス。これは、関数が実際に実行され、その実行に対して一意のときに作成されます。私は私の答えself.__tags = tags or {}で提案されているorキーワードとして

(インスタンスに割り当てる場合は、当然のことながら、そのインスタンスに、self.attr = {}でユニークな属性) - それが私たちの前に(古いPythonで一般的なパターンから頼みますしかし、 "or"演算子ショートカット、のような表現で のような便利なショートカットは、 "真の"値に評価された場合は最初のオペランドを返し、2番目の属性を返します(真でない場合)。 、重要ではありません。第2パラメータの真理値は、とにかく重要です)。インライン「if」式を使用した同じ表現は、self.__tags = tags if tags else {}です。

また、古いチュートリアルで「個人的」属性として言及されているものを持つために属性名に2つ前に付けるパターンはありますが、それは良いプログラミングパターンではなく、避けるべきです。 Pythonは実際に私的または保護された属性へのアクセスを実装していません - 特定の属性、メソッド、または関数名が_(単一の下線)で始まる場合、それを誰でもコード化した者を私的に使用するためのものです。これらの属性を制御するコードの将来のバージョンでは、それらを変更したり呼び出したりすることで、未承認の動作が発生する可能性があります。二重のアンダースコアの接頭辞については

、しかし、actuall副作用がある:時間をコンパイル、__で始まるクラス属性が変更され、そして__xxx_<classname>__xxxに改名された - で名前が変更されたクラス本体内のすべてのocurrencesをクラスファクトリの外側にある同じファッションとコードは、完全にマングルされた名前を書くだけで、通常の方法でアクセスできます。この機能は、基本クラスが誤ってまたは属性名の使い易さによってサブクラスで上書きされない属性とメソッドを保持できるようにするためのものです(ただし、 "セキュリティ"の目的ではありません)。

古い言語のチュートリアルやテキストでは、通常、この機能がPythonの「プライベート属性」を実行する方法として説明されています。実際には正しくありません。

+0

このように出力がどのように変化するかを尋ねてもらえますか? –

+0

これは私にとって実際にはいくつかのことをクリアします。まず、 "'NoneType'オブジェクトが項目割り当てをサポートしていないという問題が発生しました(' tags:dict = None'を設定しています)。第二に、私は私の質問に記載された問題に遭遇していた。これにより、両方がクリアされ、 'または'は多くの意味があります!ありがとうございました!約8分でこれを答えとして受け入れます。 – nwilging

+0

3つは、「物事はプライベートでなければならないので、私はどこにでも二重のアンダースコアを置くでしょう! – jonrsharpe

関連する問題