假设我想为 和 创建car
一个tractor
类boat
。所有这些类都有一个实例,engine
我想在一个列表中跟踪所有引擎。如果我理解正确,如果电机对象是可变的,我可以将其存储为car
列表中的属性和相同的实例。
我无法找到任何关于用户定义的类是否可变的可靠信息,如果在定义它们时可以选择,任何人都可以解释一下吗?
假设我想为 和 创建car
一个tractor
类boat
。所有这些类都有一个实例,engine
我想在一个列表中跟踪所有引擎。如果我理解正确,如果电机对象是可变的,我可以将其存储为car
列表中的属性和相同的实例。
我无法找到任何关于用户定义的类是否可变的可靠信息,如果在定义它们时可以选择,任何人都可以解释一下吗?
用户类被认为是可变的。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
所有对象(除了标准库中的少数对象,一些使用描述符和装饰器等实现特殊访问机制的对象,或者一些在 C 中实现的对象)都是可变的。这包括用户定义类的实例、类本身,甚至是定义类的类型对象。您甚至可以在运行时改变一个类对象,并在修改之前创建的类的实例中显示修改。总的来说,如果你挖掘得足够深,事情在 Python 中只有按照惯例是不可变的。
我认为您将可变性与 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
对象而不是实际存储对对象的真实引用,可以对上面的示例进行一些改进。在这种情况下,您将在返回对该对象的新引用之前检查以确保该引用仍然存在(尚未被垃圾回收)。
编辑:这在概念上是错误的,python 中的不可变对象可以解释为什么。
class Engine():
def __init__(self, sn):
self.sn = sn
a = Engine(42)
b = a
print (a is b)
打印True
。