是用户定义的类mutab

2024-10-16 20:51:19 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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


Tags: 对象实例用户引擎信息列表属性定义
3条回答

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

要将类用作dict中的键或将它们存储在set中,可以定义^{} method^{} 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

现在可以在集合中使用类引擎的实例:

>>> 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._enginesdict storeweakref.ref对象而不是实际存储对对象的实际引用,可以稍微改进上面的示例。在这种情况下,在返回对该对象的新引用之前,您需要检查以确保引用仍然有效(没有被垃圾回收)。

相关问题 更多 >