web-dev-qa-db-ja.com

ユーザー定義のクラスは変更可能です

cartractor、およびboatのクラスを作成するとします。これらすべてのクラスにはengineのインスタンスがあり、すべてのエンジンを1つのリストで追跡したいと思います。モーターオブジェクトが可変であるかどうかを正しく理解していれば、それをcarの属性として、また同じインスタンスをリストに格納できます。

ユーザー定義クラスが可変であるかどうかについての確かな情報を追跡することはできません。それらを定義するときに選択する選択肢がある場合、誰かが光を当てることができますか?

23
jonathan topf

ユーザークラスは変更可能と見なされます。 Pythonには(絶対に)プライベート属性がないため、内部にアクセスすることでいつでもクラスを変更できます。

クラスをdictのキーとして使用したり、setに格納したりするには、 .__hash__()メソッド と-を定義できます。 .__eq__() method 、クラスが不変であることを約束します。通常、このような場合、作成後に内部状態を変更しないようにクラスAPIを設計します。

たとえば、エンジンがIDによって一意に定義されている場合、それをハッシュの基礎として使用できます。

_class Engine(object):
    def __init__(self, id):
        self.id = id

    def __hash__(self):
        return hash(self.id)

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id
        return NotImplemented
_

これで、クラスEngineのインスタンスをセットで使用できます。

_>>> eng1 = Engine(1)
>>> eng2 = Engine(2)
>>> eng1 == eng2
False
>>> eng1 == eng1
True
>>> eng1 == Engine(1)
True
>>> engines = set([eng1, eng2])
>>> engines
set([<__main__.Engine object at 0x105ebef10>, <__main__.Engine object at 0x105ebef90>])
>>> engines.add(Engine(1))
>>> engines
set([<__main__.Engine object at 0x105ebef10>, <__main__.Engine object at 0x105ebef90>])
_

上記のサンプルでは、​​別のEngine(1)インスタンスをセットに追加しましたが、それはすでに存在していると認識され、セットは変更されませんでした。

リストに関する限り、.__eq__()の実装が重要であることに注意してください。リストは、オブジェクトが可変であるかどうかを気にしませんが、.__eq__()メソッドを配置すると、特定のエンジンがすでにリストにあるかどうかをテストできます。

_>>> Engine(1) in [eng1, eng2]
True
_
33
Martijn Pieters

すべてのオブジェクト(標準ライブラリ内のいくつかを除いて、記述子やデコレータなどを使用して特別なアクセスメカニズムを実装するもの、またはCで実装されるもの)は変更可能です。これには、ユーザー定義クラスのインスタンス、クラス自体、さらにはクラスを定義する型オブジェクトも含まれます。実行時にクラスオブジェクトを変更して、変更前に作成されたクラスのインスタンスに変更を明示することもできます。概して、物事は、十分に深く掘り下げた場合にのみ、Python.

1
Silas Ray

pythonが参照を保持する方法と可変性を混同していると思います-考慮してください:

class Foo(object):
    pass

t = (1,2,Foo())  # t is a Tuple, :. t is immutable
b = a[2]  # b is an instance of Foo
b.foo = "Hello"  # b is mutable.  (I just changed it)
print (hash(b))  # b is hashable -- although the default hash isn't very useful
d = {b : 3}      # since b is hashable, it can be used as a key in a dictionary (or set).
c = t            # even though t is immutable, we can create multiple references to it.
a = [t]          # here we add another reference to t in a list.

ここで、エンジンのリストをグローバルに取得/保存することについての質問です。これを行うには、いくつかの異なる方法があります。ここに1つあります。

class Engine(object):
     def __init__(self, make, model):
        self.make = make
        self.model = model

class EngineFactory(object):
    def __init__(self,**kwargs):
        self._engines = kwargs

    def all_engines(self):
        return self._engines.values()

    def __call__(self,make, model):
    """ Return the same object every for each make,model combination requested """
       if (make,model) in _engines:
           return self._engines[(make,model)]
       else:
           a = self._engines[(make,model)] = Engine(make,model)
           return a   

 engine_factory = EngineFactory()

 engine1 = engine_factory('cool_engine',1.0)           
 engine2 = engine_factory('cool_engine',1.0)
 engine1 is engine2 #True !!!  They're the same engine.  Changing engine1 changes engine2

上記の例は、オブジェクトへの実際の参照を実際に格納する代わりに、EngineFactory._enginesdictにweakref.refオブジェクトを格納させることで少し改善できます。その場合、オブジェクトへの新しい参照を返す前に、参照がまだ生きている(ガベージコレクションされていない)ことを確認します。

0
mgilson