(ここでの「変数」は「名前」を指しますが、pythonistasが使用する定義については完全にはわかりません)
オブジェクトといくつかのメソッドがあります。これらのメソッドはすべて、オブジェクトの変数を必要とし、すべて変更します。 OOPの手法を尊重し、最もPythonicで、最良の方法で、メソッドでオブジェクト変数を使用すると同時に、他のメソッドの元の値を保持するにはどうすればよいでしょうか。
メソッドが呼び出されるたびにオブジェクトをコピーする必要がありますか?元の値を保存し、メソッドがそれらを必要とするたびにそれらをリセットするためのreset()メソッドを用意する必要がありますか?それとももっと良い方法はありますか?
編集:擬似コードを求められました。直面している問題を具体的に解決するのではなく、概念を理解することに関心があるので、例を挙げてみましょう。
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
上記はPlayerオブジェクトの私のクラスです(たとえば、彼がバスケットボールのオブジェクトであると仮定します)。このオブジェクトには、プレーヤーの統計に値を割り当てる3つの異なるメソッドがあります。
つまり、チームに2つのリーグゲームと1つのカップゲームがあるとします。私はこれらの電話をかけなければならないでしょう:
p.playLeagueGame()
p.playLeagueGame()
p.playCupGame()
2回目と3回目の呼び出しが行われたときに、以前に変更されたプレーヤーの統計をリセットする必要があることは明らかです。そのために、すべての変数を0に戻すリセットメソッドを作成するか、呼び出しごとにオブジェクトをコピーすることができます。または、まったく異なることをします。
それが私の質問の出番です。最善のアプローチは何ですか、pythonそしておっと賢明ですか?
PDATE:これが非常に複雑になっているのではないかと疑っています。関数でローカル変数を使用することで、問題を簡単に解決できます。ただし、別の関数内に関数がある場合はどうなりますか?内側の関数内で外側の関数のローカルを使用できますか?
それが「Pythonic」で十分かどうかはわかりませんが、オブジェクトのreset()
メソッドを追加する__init__
メソッドの「リセット可能な」デコレータを定義できます。現在の__dict__
から元の__dict__
へ。
編集-実装例は次のとおりです。
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:継承を伴うテストを追加しました
「Pythonic」についてはよくわかりませんが、オブジェクトにreset
メソッドを作成して、リセットが必要な場合はどうすればよいでしょうか。このメソッドを__init__
の一部として呼び出して、データを複製しないようにします(つまり、常に1か所で(再)初期化します-reset
メソッド)
すべてのデフォルト値を使用してデータメンバーとしてdefault
dictを作成し、___init__
_中に__dict__.update(self.default)
を実行し、その後、すべての値をプルバックします。 。
より一般的には、___setattr__
_フックを使用して、変更されたすべての変数を追跡し、後でそのデータを使用してそれらをリセットできます。
クラスを 不変オブジェクト にする必要があるかどうかを知りたいようです。アイデアは、一度作成されると、不変オブジェクトは変更できない/変更すべきではない/変更されないということです。
Python では、int
やTuple
インスタンスなどの組み込み型は不変であり、言語によって強制されます。
_>>> 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
_
別の例として、2つの整数を追加するたびに、新しいインスタンスが作成されます。
_>>> a=5000
>>> b=7000
>>> d=a+b
>>> d
12000
>>> id(d)
42882584
>>> d=a+b
>>> id(d)
42215680
_
id()
function は、int
オブジェクト_12000
_のアドレスを返します。そして、_a+b
_を追加するたびに、新しい_12000
_オブジェクトインスタンスが作成されます。
ユーザー定義の不変クラスは、手動で適用するか、ソースコードコメントを使用した規則として単純に実行する必要があります。
_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):
"""..."""
_
いずれにせよ、操作ごとに新しいインスタンスを作成しても問題ありません。
以前の状態を復元する場合は Mementoデザインパターン を、オブジェクトを表示する場合は プロキシデザインパターン を参照してくださいpristine、作成されたばかりのように。いずれにせよ、参照されているものとその状態の間にsomethingを置く必要があります。
コードが必要な場合はコメントしてください。ただし、デザインパターン名をキーワードとして使用すると、Web上でたくさん見つかると思います。
# 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()
単純なリセットメソッド(__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
オーバーエンジニアリングするには、@resetme
デコレータを作成できます
def resetme(f):
def rf(self, *args, **kwargs):
with resetting(self):
f(self, *args, **kwargs)
return rf
したがって、明示的にwith
を使用する代わりに、デコレータを使用できます。
@resetme
def method(self):
self.foo += self.bar
print self.foo
全体的に、デザインには多少の手直しが必要なようです。これらすべてを追跡するPlayerGameStatistics
クラスについてはどうでしょうか。また、Player
またはGame
のいずれかがこれらのオブジェクトのコレクションを保持します。
また、表示するコードは良いスタートですが、Player
クラスと相互作用するコードをもっと表示できますか?単一のPlayer
オブジェクトにPlayXGame
メソッドが必要な理由を理解するのに苦労しています-単一のPlayer
が他のPlayer
sと相互作用しない場合ゲームをプレイするのですか、それとも特定のPlayer
がゲームをプレイするのはなぜですか?
私はPaoloVictorからの一番の答えが好きでした(そして試しました)。ただし、それ自体が「リセット」されることがわかりました。つまり、reset()を2回呼び出すと、例外がスローされます。
次の実装で繰り返し動作することがわかりました
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__
同様の問題があったので、Nice入力に感謝します。オブジェクトの初期状態にリセットできるようにしたいので、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()"
初期状態を更新するには、データを_tool_init_statesに書き込むreset(...)のようなメソッドを作成します。これが誰かに役立つことを願っています。メタクラスなしでこれが可能な場合は、私に知らせてください。
少なくとも別の「PlayerGameStats」クラスを含めるようにモデルを作り直す必要があるように思えます。
次のようなもの:
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)
そして、編集の質問に答えるために、はい、ネストされた関数は外部スコープに格納されている変数を見ることができます。詳細については、チュートリアルを参照してください: http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces