Подтвердить что ты не робот

Являются пользовательскими классами, изменяемыми

Скажем, я хочу создать класс для car, tractor и boat. Все эти классы имеют экземпляр engine, и я хочу отслеживать все движки в одном списке. Если я правильно понимаю, если объект двигателя изменен, я могу сохранить его как атрибут car, а также тот же экземпляр в списке.

Я не могу найти какую-либо достоверную информацию о том, изменяются ли пользовательские классы, и если есть выбор, когда вы их определяете, может ли кто-нибудь пролить некоторый свет?

4b9b3361

Ответ 1

Пользовательские классы считаются изменчивыми. Python не имеет (абсолютно) частных атрибутов, поэтому вы всегда можете изменить класс, достигнув внутренних элементов.

Для использования вашего класса в качестве ключа в dict или сохранения его в set вы можете определить метод .__hash__() и .__eq__(), обещая, что ваш класс неизменен. Обычно вы создаете свой API класса, чтобы не мутировать внутреннее состояние после создания в таких случаях.

Например, если ваши двигатели уникально определены своим идентификатором, вы можете использовать это как основу своего хэша:

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

Ответ 2

Все объекты (за исключением нескольких в стандартной библиотеке, некоторые, которые реализуют специальные механизмы доступа, используя такие вещи, как дескрипторы и декораторы или некоторые, реализованные на C), являются изменяемыми. Сюда входят экземпляры пользовательских классов, самих классов и даже типы объектов, которые определяют классы. Вы можете даже мутировать объект класса во время выполнения и проявлять изменения в экземплярах класса, созданного до модификации. По большому счету, вещи могут быть неизменными по соглашению на Python, если вы копаете достаточно глубоко.

Ответ 3

Я думаю, вы смешиваете изменчивость с тем, как 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 store weakref.ref вместо фактического хранения реальных ссылок на объекты. В этом случае вы должны убедиться, что ссылка все еще жива (не была собрана мусором), прежде чем вы вернете новую ссылку на объект.

Ответ 4

EDIT: это концептуально неправильно, Неизменяемый объект в python может пролить свет на вопрос почему.

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

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

выводит True.