用户定义的类是可变的

时间:2012-08-22 15:24:58

标签: python class immutability mutable

我想要为cartractorboat创建一个课程。所有这些类都有engine的实例,我想跟踪单个列表中的所有引擎。如果我正确理解电机对象是否可变,我可以将其存储为car的属性,也可以将其存储在列表中。

我无法追踪关于用户定义的类是否可变的任何可靠信息,以及在定义它们时是否可以选择,是否有人可以解释一下?

4 个答案:

答案 0 :(得分:31)

用户类被认为是可变的。 Python没有(绝对)私有属性,所以你总是可以通过进入内部来改变一个类。

要将您的课程用作dict中的关键字或将其存储在set中,您可以定义.__hash__() method.__eq__() method,并承诺class是不可变的。您通常将类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

答案 1 :(得分:1)

所有对象(标准库中的少数对象,一些使用描述符和装饰器等实现特殊访问机制的对象,或者在C中实现的一些对象)都是可变的。这包括用户定义的类,类本身的实例,甚至是定义类的类型对象。您甚至可以在运行时改变类对象,并在修改之前创建的类的实例中显示修改。总的来说,如果你深入挖掘,事物只能通过Python中的常规变化。

答案 2 :(得分:0)

我认为你对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.

现在关于获取/存储全球引擎列表的问题 - 有几种不同的方法可以做到这一点,这里有一个:

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._engines dict存储weakref.ref对象而不是实际存储对象的实际引用,可以稍微改进上面的示例。在这种情况下,在返回对象的新引用之前,请检查以确保引用仍处于活动状态(尚未进行垃圾回收)。

答案 3 :(得分:-2)

编辑:这在概念上是错误的,The immutable object in python可以解释为什么。

class Engine():
    def __init__(self, sn):
        self.sn = sn

a = Engine(42)
b = a
print (a is b)

打印True