29

假设我想为 和 创建car一个tractorboat。所有这些类都有一个实例,engine我想在一个列表中跟踪所有引擎。如果我理解正确,如果电机对象是可变的,我可以将其存储为car列表中的属性和相同的实例。

我无法找到任何关于用户定义的类是否可变的可靠信息,如果在定义它们时可以选择,任何人都可以解释一下吗?

4

4 回答 4

37

用户类被认为是可变的。Python 没有(绝对)私有属性,因此您始终可以通过进入内部来更改类。

为了将您的类用作 a 中的键 dict或将它们存储在 a 中set,您可以定义一个.__hash__()方法和一个.__eq__()方法,从而保证您的类是不可变的。在这种情况下,您通常会将您的类 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
于 2012-08-22T15:29:52.897 回答
1

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

于 2012-08-22T15:31:14.020 回答
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._enginesdict 存储weakref.ref对象而不是实际存储对对象的真实引用,可以对上面的示例进行一些改进。在这种情况下,您将在返回对该对象的新引用之前检查以确保该引用仍然存在(尚未被垃圾回收)。

于 2012-08-22T16:04:36.680 回答
-3

编辑:这在概念上是错误的,python 中的不可变对象可以解释为什么。

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

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

打印True

于 2012-08-22T15:32:42.333 回答