“重置”对象变量的“Pythonic”方式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4866587/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:53:52  来源:igfitidea点击:

"Pythonic" way to "reset" an object's variables?

variablespythongloballocal

提问by

("variables" here refers to "names", I think, not completely sure about the definition pythonistas use)

(这里的“变量”指的是“名称”,我认为,不完全确定pythonistas使用的定义)

I have an object and some methods. These methods all need and all change the object's variables. How can I, in the most pythonic and in the best, respecting the techniques of OOP, way achieve to have the object variables used by the methods but also keep their original values for the other methods?

我有一个对象和一些方法。这些方法都需要并且都改变了对象的变量。我怎样才能以最 Pythonic 和最好的方式尊重 OOP 的技术,实现方法使用的对象变量,同时保留其他方法的原始值?

Should I copy the object everytime a method is called? Should I save the original values and have a reset() method to reset them everytime a method needs them? Or is there an even better way?

每次调用方法时都应该复制对象吗?我应该保存原始值并在每次方法需要它们时使用 reset() 方法来重置它们吗?或者有更好的方法吗?

EDIT:I was asked for pseudocode. Since I am more interested in understanding the concept rather than just specifically solving the problem I am encountering I am going to try give an example:

编辑:我被要求提供伪代码。由于我对理解概念更感兴趣,而不仅仅是专门解决我遇到的问题,我将尝试举一个例子:

class Player():
    games = 0
    points = 0
    fouls = 0
    rebounds = 0
    assists = 0
    turnovers = 0
    steals = 0

    def playCupGame(self):
        # simulates a game and then assigns values to the variables, accordingly
        self.points = K #just an example

    def playLeagueGame(self):
        # simulates a game and then assigns values to the variables, accordingly
        self.points = Z #just an example
        self.rebounds = W #example again

    def playTrainingGame(self):
        # simulates a game and then assigns values to the variables, accordingly
        self.points = X #just an example
        self.rebounds = Y #example again

The above is my class for a Player object (for the example assume he is a basketball one). This object has three different methods that all assign values to the players' statistics.

以上是我的 Player 对象类(例如假设他是一个篮球对象)。该对象具有三种不同的方法,它们都为玩家的统计数据赋值。

So, let's say the team has two league games and then a cup game. I'd have to make these calls:

所以,假设球队有两场联赛,然后是一场杯赛。我必须打这些电话:

p.playLeagueGame()
p.playLeagueGame()
p.playCupGame()

It's obvious that when the second and the third calls are made, the previously changed statistics of the player need to be reset. For that, I can either write a reset method that sets all the variables back to 0, or copy the object for every call I make. Or do something completely different.

很明显,当第二次和第三次调用时,之前改变的玩家统计数据需要重置。为此,我可以编写一个重置方法,将所有变量设置回 0,或者为每次调用复制对象。或者做一些完全不同的事情。

That's where my question lays, what's the best approach, python and oop wise?

这就是我的问题所在,python 和 oop 明智的最佳方法是什么?

UPDATE:I am suspicious that I have superovercomplicated this and I can easily solve my problem by using local variables in the functions. However, what happens if I have a function inside another function, can I use locals of the outer one inside the inner one?

更新:我怀疑我已经把它复杂化了,我可以通过在函数中使用局部变量来轻松解决我的问题。但是,如果我在另一个函数中有一个函数会发生什么,我可以在内部函数中使用外部函数的局部变量吗?

回答by PaoloVictor

Not sure if it's "Pythonic" enough, but you can define a "resettable" decorator for the __init__method that creates a copy the object's __dict__and adds a reset()method that switches the current __dict__to the original one.

不确定它是否足够“Pythonic”,但是您可以为__init__创建对象副本的方法定义一个“可重置”装饰器,__dict__并添加一个reset()将当前对象切换__dict__到原始对象的方法。

Edit - Here's an example implementation:

编辑 - 这是一个示例实现:

def resettable(f):
    import copy

    def __init_and_copy__(self, *args, **kwargs):
        f(self, *args)
        self.__original_dict__ = copy.deepcopy(self.__dict__)

        def reset(o = self):
            o.__dict__ = o.__original_dict__

        self.reset = reset

    return __init_and_copy__

class Point(object):
    @resettable
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "%d %d" % (self.x, self.y)

class LabeledPoint(Point):
    @resettable
    def __init__(self, x, y, label):
        self.x = x
        self.y = y
        self.label = label

    def __str__(self):
        return "%d %d (%s)" % (self.x, self.y, self.label)

p = Point(1, 2)

print p # 1 2

p.x = 15
p.y = 25

print p # 15 25

p.reset()

print p # 1 2

p2 = LabeledPoint(1, 2, "Test")

print p2 # 1 2 (Test)

p2.x = 3
p2.label = "Test2"

print p2 # 3 2 (Test2)

p2.reset()

print p2 # 1 2 (Test)

Edit2: Added a test with inheritance

Edit2:添加了一个带有继承的测试

回答by Bryan Oakley

I'm not sure about "pythonic", but why not just create a resetmethod in your object that does whatever resetting is required? Call this method as part of your __init__so you're not duplicating the data (ie: always (re)initialize it in one place -- the resetmethod)

我不确定“pythonic”,但为什么不在reset您的对象中创建一个方法来执行任何需要重置的操作?将此方法作为您的一部分调用,__init__这样您就不会复制数据(即:始终(重新)在一个地方初始化它——reset方法)

回答by vz0

Sounds like you want to know if your class should be an immutable object. The idea is that, once created, an immutable object can't/should't/would't be changed.

听起来您想知道您的类是否应该是不可变对象。这个想法是,一旦创建,一个不可变的对象就不能/不应该/不会改变。

On Python, built-in types like intor tupleinstances are immutable, enforced by the language:

Python 上,像inttuple实例这样的内置类型是不可变的,由语言强制执行:

>>> a=(1, 2, 3, 1, 2, 3)
>>> a[0] = 9
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

As another example, every time you add two integers a new instance is created:

再举一个例子,每次添加两个整数时都会创建一个新实例:

>>> a=5000
>>> b=7000
>>> d=a+b
>>> d
12000
>>> id(d)
42882584
>>> d=a+b
>>> id(d)
42215680

The id()functionreturns the address of the intobject 12000. And every time we add a+ba new 12000object instance is created.

id()函数返回int对象的地址12000。并且每次我们添加a+b一个新的12000对象实例都会被创建。

User defined immutable classes must be enforced manually, or simply done as a convention with a source code comment:

用户定义的不可变类必须手动强制执行,或者简单地作为带有源代码注释的约定完成:

class X(object):
    """Immutable class. Don't change instance variables values!"""
    def __init__(self, *args):
        self._some_internal_value = ...

    def some_operation(self, arg0):
        new_instance = X(arg0 + ...)
        new_instance._some_internal_operation(self._some_internal_value, 42)
        return new_instance

    def _some_internal_operation(self, a, b):
        """..."""

Either way, it's OK to create a new instance for every operation.

无论哪种方式,都可以为每个操作创建一个新实例。

回答by Apalala

See the Memento Design Patternif you want to restore previous state, or the Proxy Design Patternif you want the object to seem pristine, as if just created. In any case, you need to put somethingbetween what's referenced, and it's state.

如果您想恢复以前的状态,请参阅Memento 设计模式,如果您希望对象看起来像刚创建时一样原始,请参阅代理设计模式。在任何情况下,您都需要在引用的内容和状态之间添加一些内容。

Please comment if you need some code, though I'm sure you'll find plenty on the web if you use the design pattern names as keywords.

如果您需要一些代码,请发表评论,但我相信如果您使用设计模式名称作为关键字,您会在网上找到很多。

# The Memento design pattern
class Scores(object):
    ...

class Player(object):
    def __init__(self,...):
        ...
        self.scores = None
        self.history = []
        self.reset()

    def reset(self):
        if (self.scores):
            self.history.append(self.scores)
        self.scores = Scores()

回答by richo

I would create a defaultdict as a data member with all of the default values, then do __dict__.update(self.default)during __init__and then again at some later point to pull all the values back.

我会创建一个defaultdict 作为具有所有默认值的数据成员,然后在__dict__.update(self.default)期间执行__init__,然后在稍后的某个时间点再次将所有值拉回来。

More generally, you can use a __setattr__hook to keep track of every variable that has been changed and later use that data to reset them.

更一般地,您可以使用__setattr__钩子来跟踪已更改的每个变量,然后使用该数据重置它们。

回答by user470379

It sounds like overall your design needs some reworking. What about a PlayerGameStatisticsclass that would keep track of all that, and either a Playeror a Gamewould hold a collection of these objects?

听起来总体上您的设计需要一些返工。如果一个PlayerGameStatistics类可以跟踪所有这些,并且 aPlayer或 aGame将保存这些对象的集合呢?

Also the code you show is a good start, but could you show more code that interacts with the Playerclass? I'm just having a hard time seeing why a single Playerobject should have PlayXGamemethods -- does a single Playernot interact with other Players when playing a game, or why does a specific Playerplay the game?

您展示的代码也是一个好的开始,但是您能否展示更多与Player类交互的代码?我只是很难理解为什么单个Player对象应该有PlayXGame方法——在玩游戏时,单个对象是否Player不与其他Players交互,或者为什么特定对象会Player玩游戏?

回答by senderle

A simple reset method (called in __init__and re-called when necessary) makes a lot of sense. But here's a solution that I think is interesting, if a bit over-engineered: create a context manager. I'm curious what people think about this...

一个简单的重置方法(调用__init__并在必要时重新调用)很有意义。但是这里有一个我认为很有趣的解决方案,如果有点过度设计:创建一个上下文管理器。我很好奇人们对此有何看法...

from contextlib import contextmanager

@contextmanager
def resetting(resettable):
    try:
        resettable.setdef()
        yield resettable
    finally:
        resettable.reset()

class Resetter(object):
    def __init__(self, foo=5, bar=6):
        self.foo = foo
        self.bar = bar
    def setdef(self):
        self._foo = self.foo
        self._bar = self.bar
    def reset(self):
        self.foo = self._foo
        self.bar = self._bar
    def method(self):
        with resetting(self):
            self.foo += self.bar
            print self.foo

r = Resetter()
r.method()    # prints 11
r.method()    # still prints 11

To over-over-engineer, you could then create a @resetmedecorator

为了过度设计,你可以创建一个@resetme装饰器

def resetme(f):
    def rf(self, *args, **kwargs):
        with resetting(self):
            f(self, *args, **kwargs)
    return rf

So that instead of having to explicitly use withyou could just use the decorator:

这样with您就可以使用装饰器,而不必显式使用:

@resetme
def method(self):
    self.foo += self.bar
    print self.foo

回答by ncoghlan

It sounds to me like you need to rework your model to at least include a separate "PlayerGameStats" class.

在我看来,您需要重新设计模型以至少包含一个单独的“PlayerGameStats”类。

Something along the lines of:

类似的东西:

PlayerGameStats = collections.namedtuple("points fouls rebounds assists turnovers steals")

class Player():
    def __init__(self):
        self.cup_games = []
        self.league_games = []
        self.training_games = []

def playCupGame(self):
    # simulates a game and then assigns values to the variables, accordingly
    stats = PlayerGameStats(points, fouls, rebounds, assists, turnovers, steals)
    self.cup_games.append(stats)

def playLeagueGame(self):
    # simulates a game and then assigns values to the variables, accordingly
    stats = PlayerGameStats(points, fouls, rebounds, assists, turnovers, steals)
    self.league_games.append(stats)

def playTrainingGame(self):
    # simulates a game and then assigns values to the variables, accordingly
    stats = PlayerGameStats(points, fouls, rebounds, assists, turnovers, steals)
    self.training_games.append(stats)

And to answer the question in your edit, yes nested functions can see variables stored in outer scopes. You can read more about that in the tutorial: http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces

为了回答您编辑中的问题,是的嵌套函数可以查看存储在外部作用域中的变量。您可以在教程中阅读更多相关信息:http: //docs.python.org/tutorial/classes.html#python-scopes-and-namespaces

回答by Heiner

thanks for the nice input, as I had kind of a similar problem. I'm solving it with a hook on the init method, since I'd like to be able to reset to whatever initial state an object had. Here's my code:

感谢您的好意见,因为我遇到了类似的问题。我正在使用 init 方法上的钩子来解决它,因为我希望能够将对象重置为任何初始状态。这是我的代码:

import copy
_tool_init_states = {}

def wrap_init(init_func):
    def init_hook(inst, *args, **kws):
        if inst not in _tool_init_states:
            # if there is a class hierarchy, only the outer scope does work
            _tool_init_states[inst] = None
            res = init_func(inst, *args, **kws)
            _tool_init_states[inst] = copy.deepcopy(inst.__dict__)
            return res
        else:
            return init_func(inst, *args, **kws)
    return init_hook

def reset(inst):
    inst.__dict__.clear()
    inst.__dict__.update(
        copy.deepcopy(_tool_init_states[inst])
    )

class _Resettable(type):
    """Wraps __init__ to store object _after_ init."""
    def __new__(mcs, *more):
        mcs = super(_Resetable, mcs).__new__(mcs, *more)
        mcs.__init__ = wrap_init(mcs.__init__)
        mcs.reset = reset
        return mcs

class MyResettableClass(object):
    __metaclass__ = Resettable
    def __init__(self):
        self.do_whatever = "you want,"
        self.it_will_be = "resetted by calling reset()"

To update the initial state, you could build some method like reset(...) that writes data into _tool_init_states. I hope this helps somebody. If this is possible without a metaclass, please let me know.

要更新初始状态,您可以构建一些方法,例如将数据写入 _tool_init_states 的 reset(...)。我希望这对某人有帮助。如果这在没有元类的情况下是可能的,请告诉我。

回答by Brett Stottlemyer

I liked (and tried) the top answer from PaoloVictor. However, I found that it "reset" itself, i.e., if you called reset() a 2nd time it would throw an exception.

我喜欢(并尝试过)来自 PaoloVictor 的最佳答案。但是,我发现它“重置”了自己,即,如果您第二次调用 reset() 它将引发异常。

I found that it worked repeatably with the following implementation

我发现它与以下实现重复工作

def resettable(f):
    import copy

    def __init_and_copy__(self, *args, **kwargs):
        f(self, *args, **kwargs)
        def reset(o = self):
            o.__dict__ = o.__original_dict__
            o.__original_dict__ = copy.deepcopy(self.__dict__)
        self.reset = reset
        self.__original_dict__ = copy.deepcopy(self.__dict__)
    return __init_and_copy__