【问题标题】:Python object historyPython 对象历史
【发布时间】:2013-10-07 22:09:42
【问题描述】:

我正在尝试想出一种方法来跟踪 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'

【问题讨论】:

  • 欢迎来到 StackOverflow!现在,您的问题似乎是“有更好的方法吗?”当然,什么是做任何事情的“更好”方式是非常主观的(一种方式可能更容易编码,另一种可能运行得更快,另一种占用更少的内存,另一种非常适合持久化到数据库)。因此,这个问题不可能有一个单一的答案,因此 StackOverflow 并不是获得此类反馈的最佳场所。如果您正在代码中寻找通用 cmets,请查看 codereview.stackexchange.com。
  • 旁白:def __init__(self,current="Any", history = []): 表示在初始化时没有为history 传递值的每个room 将共享相同 历史记录。 IOW,在执行 def 时构造一个 history 列表,而不是每次创建对象时。阅读this question 了解可变默认参数的详细信息。
  • 如果历史保存在一个列表中,你真的需要单独跟踪 self.current 吗?它应该始终是附加的最后一项。
  • 感谢您的欢迎,马克。所以最好问:我怎么……? DSM,你是对的,最好说:class room(): def __init__(self,current="Any")。无需传递默认历史记录,也无需将历史记录传递给 __ini__() 。 --- 汤姆,我不确定你在说什么。为了让我将具有名称的对象存储在列表中以供将来参考,我需要为其分配一个名称,这是传递给 changeroom() 方法的参数。除非你说我不应该在儿童房中使用“名字”,而应该使用“当前”。

标签: python object history


【解决方案1】:

这听起来像是外观模式的一个案例 - 有一个内部房间对象来捕获当前状态,并在外观上使用 __setattr__ 来捕获分配,存档先前的状态(内部对象),并创建一个新的更新的属性值。您需要覆盖 __getattr__ 以委托给内部对象,该对象也将具有其他任何适当的方法。

【讨论】:

  • Marcin,这听起来更像是我最初打算做的事情,然后无法开始工作。我会再考虑一下。
【解决方案2】:

我个人会这样实现:

#! /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"

【讨论】:

  • Hyperboreus,感谢您的输出 - 哇 - 需要一些时间来消化。里面有很多我不明白的东西。我想存储整个对象的一个​​原因,我应该提到的,是最终会有太多的属性随着每个状态的变化而改变,我认为存储整个对象的快照是最简单的。需要调查“深拷贝”!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
相关资源
最近更新 更多