2013-10-01 2 views
1

Я пытаюсь найти способ отслеживать различные повторные воплощения объекта в Python. Например, я хотел бы иметь класс, например, в комнате. Эта комната может начать свое существование как «кухня», а затем, в любой момент времени, стать «исследованием». Я хочу иметь возможность отслеживать (или регистрировать) все эти различные фазы в жизни моей комнаты. Итак, я придумал это, и мне интересно, есть ли лучший способ сделать это. Или, может быть, я что-то упустил. Из того, что я могу сказать, он, похоже, работает для моих целей.История объекта Python

class room(): 
    def __init__(self,current="Any", history = []): 
     self.current = childroom(current) 
     self.history = history 
     self.history.append(self.current) 

    def changeroom(self,current = "New"): 
     self.current = childroom(current) 
     self.history.append(self.current) 


class childroom(room): 
    def __init__(self,name="Any"): 
     self.name = name  

Когда я использую этот код ...

>>> myroom = room("kitchen") 
>>> myroom.changeroom("study") 
>>> myroom 
<__main__.room instance at 0x0000000003139208> 
>>> myroom.history 
[<__main__.childroom instance at 0x0000000003139288>, <__main__.childroom instance at 0x00000000023F4AC8>] 
>>> myroom.history[0].name 
'kitchen' 
+1

Добро пожаловать в StackOverflow! Прямо сейчас, похоже, ваш вопрос: «Есть ли лучший способ сделать это?» Конечно, что такое «лучший» способ сделать что-то довольно субъективно (один способ может быть проще скомпилировать, другой может работать быстрее, другой - меньше памяти, другой - для сохранения базы данных). Таким образом, было бы невозможно, чтобы на этот вопрос был один ответ, поэтому StackOverflow - не лучшее место для получения такой обратной связи. Если вы ищете общие комментарии к своему коду, посмотрите на codereview.stackexchange.com. –

+3

Кроме того: 'def __init __ (self, current =" Any ", history = []):' означает, что каждая 'room', которая не передала значение для истории при инициализации, будет делиться * той же * историей. IOW, один 'history' список создается, когда' def' выполняется, а не каждый раз, когда объект создан. Прочитайте [этот вопрос] (http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) об изменчивых аргументах по умолчанию для деталей. – DSM

+0

Если история сохранена в списке, вам действительно нужно отслеживать self.current по отдельности? Это всегда должен быть последний добавленный элемент. – tom

ответ

1

лично я бы реализовать это следующим образом:

#! /usr/bin/python3 
import copy 

class Room: 
    def __init__ (self, state = 'Initial'): 
     self.state = state 
     self.history = [] 

    def morph (self, state): 
     clone = copy.deepcopy (self) 
     self.state = state 
     self.history.append (clone) 

Имейте в виду, что мне делать, если ваш реальный не знаю У установки есть некоторые функции, которые ограничивают глубокое копирование.

Это дает:

>>> r = Room ('Kitchen') 
>>> r.morph ('Loo') 
>>> r.morph ('Spaceship') 
>>> r.state 
'Spaceship' 
>>> [a.state for a in r.history] 
['Kitchen', 'Loo'] 
>>> [type (a) for a in r.history] 
[<class 'test.Room'>, <class 'test.Room'>] 

Я думаю, как правило, вам не нужно, чтобы сохранить все состояние объекта, но только атрибуты, которые стоит отслеживать. Вы можете упаковать это поведение в декоратор вдоль этих линий:

#! /usr/bin/python3 

import datetime 
import copy 

class Track: 
    def __init__ (self, *args, saveState = False): 
     self.attrs = args 
     self.saveState = saveState 

    def __call__ (self, cls): 
     cls._track = [] 
     this = self 

     oGetter = cls.__getattribute__ 
     def getter (self, attr): 
      if attr == 'track': return self._track 
      if attr == 'trackTrace': return '\n'.join ('{}: "{}" has changed to "{}"'.format (*t) for t in self._track) 
      return oGetter (self, attr) 
     cls.__getattribute__ = getter 

     oSetter = cls.__setattr__ 
     def setter (self, attr, value): 
      if attr in this.attrs: 
       self._track.append ((datetime.datetime.now(), attr, copy.deepcopy (value) if this.saveState else value)) 
      return oSetter (self, attr, value) 
     cls.__setattr__ = setter 

     return cls 

Теперь мы можем использовать этот декоратор так:

@Track ('holder') 
class Book: 
    def __init__ (self, title): 
     self.title = title 
     self.holder = None 
     self.price = 8 

class Person: 
    def __init__ (self, firstName, lastName): 
     self.firstName = firstName 
     self.lastName = lastName 

    def __str__ (self): 
     return '{} {}'.format (self.firstName, self.lastName) 

r = Book ('The Hitchhiker\'s Guide to the Galaxy') 
p = Person ('Pedro', 'Párramo') 
q = Person ('María', 'Del Carmen') 
r.holder = p 
r.price = 12 
r.holder = q 
q.lastName = 'Del Carmen Orozco' 
print (r.trackTrace) 

При вызове с @Track ('holder'), он дает:

2013-10-01 14:02:43.748855: "holder" has changed to "None" 
2013-10-01 14:02:43.748930: "holder" has changed to "Pedro Párramo" 
2013-10-01 14:02:43.748938: "holder" has changed to "María Del Carmen Orozco" 

Если он вызван с @Track ('holder', 'price'), он дает:

2013-10-01 14:05:59.433086: "holder" has changed to "None" 
2013-10-01 14:05:59.433133: "price" has changed to "8" 
2013-10-01 14:05:59.433142: "holder" has changed to "Pedro Párramo" 
2013-10-01 14:05:59.433147: "price" has changed to "12" 
2013-10-01 14:05:59.433151: "holder" has changed to "María Del Carmen Orozco" 

При вызове с @Track ('holder', saveState = True), он дает:

2013-10-01 14:06:36.815674: "holder" has changed to "None" 
2013-10-01 14:06:36.815710: "holder" has changed to "Pedro Párramo" 
2013-10-01 14:06:36.815779: "holder" has changed to "María Del Carmen" 
+0

Hyperboreus, спасибо за ваш результат - вау - потребуется некоторое время для переваривания. Там много чего, я не понимаю. Одна из причин, по которой я хотел сохранить весь объект, о котором я должен был упомянуть, заключается в том, что в конечном итоге будет слишком много атрибутов, которые изменяются с каждым изменением состояния, которое, как я думал, будет хранить моментальный снимок всего объекта. Необходимо будет исследовать «глубокую кишку»! – Arne

0

Это звучит как случай для рисунка фасада - есть внутренний объект номера, который фиксирует текущее состояние и использовать __setattr__ на фасаде для захвата заданий, в архивировании предыдущее состояние (внутренний объект) и создать новый с обновленным значением свойства. Вы захотите переопределить __getattr__ для делегирования на внутренний объект, который также будет иметь соответствующие методы для чего-либо еще.

+0

Marcin, это больше похоже на то, что я намеревался сделать вначале, а затем не мог работать. Я еще немного подумаю. – Arne

+0

@Arne Рассмотрите возможность использования или копирования: https://pypi.python.org/pypi/ProxyTypes – Marcin

Смежные вопросы