#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__() для этого класса, чтобы получить поведение, которое я описал?