Как скопировать класс конечного автомата в python?

#python #copy #state-machine

#python #Копировать #конечный автомат

Вопрос:

Приведенный ниже фрагмент автономного кода определяет простой конечный автомат и использует модуль копирования для создания его копий.

 import copy
class StateMachine:
    def __init__(self):
        self.doit = self._doit_A
    def _doit_A(self):
        print('state A')
        self.doit = self._doit_B
    def _doit_B(self):
        print('state B')
        self.doit = self._doit_A

sm = StateMachine()
sm_copy_before_changes = copy.copy(sm)

print('--- Original StateMachine ---')
for _ in range(3): sm.doit()

print('--- pre copy ---')
for _ in range(3): sm_copy_before_changes.doit()

sm_copy_after_changes = copy.copy(sm)
print('--- post copy ---')
for _ in range(3): sm_copy_after_changes.doit()
 

Результат

 --- Original StateMachine ---
state A
state B
state A
--- pre copy ---
state A
state A
state A
--- post copy ---
state B
state B
state B
 

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

Копия, созданная до doit() того, как были сделаны какие-либо вызовы для оригинала, застряла в состоянии A

Копия, созданная после doit() выполнения вызовов в оригинале, застряла в состоянии B

Почему это не работает и каков наилучший способ заставить копии работать должным образом?

deepcopy() заставляет его работать. Что такое copy() игнорирование, которое deepcopy() не игнорируется в этом случае?

В реальной ситуации передо мной я не хочу deepcopy() использовать конечный автомат. предположительно, я могу переопределить __copy__() ? Как бы я это сделал в этом случае? или я должен переопределить __deepcopy__() ?

Комментарии:

1. я не уверен, правильно ли я понимаю, пожалуйста, посмотрите мой ответ и скажите мне, хотите ли вы этого.

Ответ №1:

Ну, вам просто нужно проверить объекты:

 assert sm.doit == sm_copy_before_changes.doit
sm_copy_before_changes, sm, sm_copy_before_changes.doit
 

дает:

 <__main__.StateMachine at 0x7f0b649d6cc0>,
<__main__.StateMachine at 0x7f0b649d6d30>,
<bound method StateMachine._doit_A of <__main__.StateMachine object at 0x7f0b649d6d30>>
 

Итак, когда вы copy создаете новый объект — но он doit по-прежнему ссылается на doit объект sm — так что, очевидно, он застрял в одном состоянии.

Теперь давайте попробуем deepcopy!

 sm_before_2 = copy.deepcopy(sm)

assert sm_before_2.doit != sm.doit
sm_before_2, sm_before_2.doit
 

дает:

 <__main__.StateMachine at 0x7f0b64a66a90>,
<bound method StateMachine._doit_A of <__main__.StateMachine object at 0x7f0b64a66a90>>
 

Ну, это, очевидно, сработало бы сейчас!

И это несколько упоминается в документах

Мелкая копия создает новый составной объект, а затем (насколько это возможно) вставляет в него ссылки на объекты, найденные в оригинале.

Глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.



взлом __copy__ :

одним из очень быстрых способов взлома __copy__ , чтобы включить желаемое поведение, было бы:

     def __copy__(self):
      new = self.__class__()
      for i in dir(self):
        if i != '__weakref__':
          setattr(new, i, getattr(self, i))
      return new
 

очевидно, будьте осторожны, чтобы протестировать это, чтобы не было никаких непредвиденных последствий в вашем приложении!

Комментарии:

1. Спасибо, Партха. Итак, кажется, что методы python связаны с экземплярами объектов, с которыми они работают, и вызывающий объект может быть или не быть одним и тем же, в отличие от модели C , где методы связаны с классом, а вызывающий экземпляр определяет объект, с которым они работают. Достаточно справедливо. Итак, как бы мне переопределить __copy__() в этом экземпляре, чтобы получить поведение в стиле C ? Я не хочу полагаться на deepcopy() то, что это не подходит для реального варианта использования.

2. @OldSchool Мне было бы любопытно узнать, почему вы немного опасаетесь deepcopy — я имею в виду очевидное; y, как вы сказали, у вас проблема реального мира, — но что именно (если об этом можно рассказать)?

3. @OldSchool кроме того, добавлен быстрый взлом для перегрузки __copy__

4. Конечные автоматы реального мира содержат ссылки на большие структуры, которые не нужно копировать. Вот почему я не хочу делать глубокую копию

5. Когда вы говорите «взломать»: что в вашей реализации касается вас как взлома? Каков наилучший канонический способ переопределить __copy__() для этого класса, чтобы получить поведение, которое я описал?