2011-02-01 9 views
12

("variabili" qui si riferisce a "nomi", penso, non del tutto sicuro circa i Pythonisti definizione utilizzano)Modo "Pythonic" per "resettare" le variabili di un oggetto?

ho un oggetto e alcuni metodi. Questi metodi hanno tutti bisogno e cambiano tutte le variabili dell'oggetto. Come posso, nel modo più poderoso e nel migliore dei casi, rispettare le tecniche di OOP, come ottenere le variabili oggetto utilizzate dai metodi ma anche mantenere i loro valori originali per gli altri metodi?

Devo copiare l'oggetto ogni volta che viene chiamato un metodo? Devo salvare i valori originali e avere un metodo reset() per resettarli ogni volta che un metodo li richiede? O c'è un modo ancora migliore?

MODIFICA: Mi è stato richiesto lo pseudocodice. Dato che io sono più interessato a comprendere il concetto e non solo specificamente risolvere il problema che sto incontrando ho intenzione di provare a fare un esempio:

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 

Quanto sopra è la mia classe per un oggetto Player (per l'esempio assumere lui è un pallacanestro). Questo oggetto ha tre diversi metodi che assegnano tutti i valori alle statistiche dei giocatori.

Quindi, diciamo che la squadra ha due partite di campionato e poi una partita di coppa. Avrei dovuto effettuare queste chiamate:

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

E 'ovvio che quando la seconda e la terza chiamate sono fatti, le statistiche precedentemente modificate del giocatore devono essere reimpostati. Per questo, posso scrivere un metodo di reset che riporta tutte le variabili a 0, o copiare l'oggetto per ogni chiamata che faccio. O fai qualcosa di completamente diverso.

Ecco dove risiede la mia domanda, qual è l'approccio migliore, python e oop wise?

UPDATE: Sono sospettoso di averlo complicato in maniera eccessiva e posso facilmente risolvere il problema utilizzando le variabili locali nelle funzioni. Tuttavia, cosa succede se ho una funzione all'interno di un'altra funzione, posso usare i locali di quella esterna all'interno di quella interna?

+4

Potrebbe spiegare il vostro intento, il comportamento desiderato? Il metodo è concatenato, come in jQuery? –

+1

Forse qualche pseudocodice potrebbe aiutare a spiegare perché è necessario modificare le variabili oggetto ma cambiarle alla fine della funzione, invece di usare solo variabili locali. – tkerwin

+0

Ho letto alcune volte e non sono ancora sicuro di come dire quando un metodo vorrà vedere i valori dei membri aggiornati e quando non lo farà. Sembra che abbia bisogno di un pattern Agent, Facade o MVC. –

risposta

7

Non so se si tratta di "Pythonic" sufficiente, ma è possibile definire un decoratore "ripristinabile" per il metodo __init__ che crea una copia del __dict__ oggetto e aggiunge un metodo reset() che commuta la corrente __dict__ a quello originale.

Edit - Ecco un esempio di implementazione:

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: Aggiunto un test con l'ereditarietà

+2

In Python, si chiamano decoratori, non annotazioni –

+0

+1 Sì! Li mescolo sempre - La cosa divertente è che mentre implementavo continuavo a pensare ai "decoratori", ma scrivevo comunque "annotazioni". Yikes. – PaoloVictor

+0

-1, L'uso di '__dict__' come origine di tutti gli attributi di un oggetto non è affidabile. Non tiene conto dell'ereditarietà o metaclassi. – Apalala

7

io non sono sicuro di "divinatorio", ma perché non solo creare un metodo reset nell'oggetto che fa tutto ciò che è necessario ripristinare? Chiama questo metodo come parte del tuo __init__ in modo da non duplicare i dati (es .: sempre (re) inizializzalo in un unico posto - il metodo reset)

+0

+1, anche se lo scambio di "__dict__" è intelligente e tutto questo è il metodo più esplicito (e quindi il più IMO). Un oggetto immutabile è anche una buona opzione, ma solo se i tuoi metodi non hanno bisogno di cambiare i valori durante il calcolo. – senderle

+0

+1 Dimentica il _pythonic_. Il significato di 'reset()' deve essere definito dalla classe che ne ha bisogno. – Apalala

2

Suoni come se volessi sapere se la tua classe dovrebbe essere un immutable object. L'idea è che, una volta creato, un oggetto immutabile non può/non dovrebbe/non dovrebbe essere cambiato.

Su Python, tipi built-in come int o tuple casi sono immutabili, imposto dal linguaggio:

>>> 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 

Come altro esempio, ogni volta che si aggiungono due interi viene creata una nuova istanza:

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

Il id() function restituisce l'indirizzo dell'oggetto int12000. E ogni volta che aggiungiamo a+b viene creata una nuova istanza dell'oggetto 12000. classi immutabili definite

utente devono essere applicate manualmente, o semplicemente fatto come una convenzione con un commento il codice sorgente:

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): 
     """...""" 

In entrambi i casi, è OK per creare una nuova istanza per ogni operazione.

+1

Grazie per questa risposta, molto utile! –

1

Vedi l'Memento Design Pattern se si desidera ripristinare lo stato precedente, o il Proxy Design Pattern se si desidera che l'oggetto da sembrare incontaminata, come se appena creato. In ogni caso, è necessario inserire qualcosa tra tra ciò che viene fatto riferimento e il suo stato.

Si prega di commentare se avete bisogno di qualche codice, anche se sono sicuro che troverete molti sul web se si utilizzano i nomi dei modelli di progettazione come parole chiave.

# 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() 
2

vorrei creare un default dict come un membro di dati con tutti i valori di default, quindi fare __dict__.update(self.default) durante __init__ e poi di nuovo ad un certo punto in seguito a tirare tutti i valori di nuovo.

Più in generale, è possibile utilizzare un gancio __setattr__ per tenere traccia di ogni variabile che è stata modificata e utilizzare successivamente tali dati per reimpostarli.

1

Sembra che nel complesso il tuo progetto abbia bisogno di qualche rielaborazione. Che dire di una classe PlayerGameStatistics che tenga traccia di tutto ciò e che uno Player o uno Game possa contenere una raccolta di questi oggetti?

Anche il codice che mostri è un buon inizio, ma potresti mostrare più codice che interagisce con la classe Player? Sto solo avendo difficoltà a capire perché un singolo oggetto Player dovrebbe avere i metodi PlayXGame - un singolo Player non interagisce con altri Player s durante la riproduzione di un gioco, o perché uno specifico Player gioca?

0

Un metodo di ripristino semplice (chiamato in __init__ e richiamato quando necessario) ha molto senso. Ma ecco una soluzione che ritengo sia interessante, anche se un po 'eccessivamente ingegnerizzata: creare un gestore di contesto. Sono curioso di cosa pensa la gente di questo ...

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 

Per over-over-ingegnere, si potrebbe quindi creare una @resetme decoratore

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

modo che invece di dover utilizzare in modo esplicito with si potrebbe utilizzare il decoratore:

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

Penso che sia troppo complesso. 'def reset (self): ...' è completamente ovvio senza richiedere un mucchio di codice extra. Qualsiasi altra cosa aggiunge semplicemente complessità senza guadagno IMO. –

+0

Beh, penso che ci sia un certo guadagno, in quanto anziché tornare a uno stato fisso che non può essere modificato facilmente, i valori di foo e bar ritornano al loro stato appena prima che il metodo venisse chiamato. Se vuoi questa funzionalità in una grande classe con molti metodi, questa tecnica ti evita di dover copiare lo stato tutto il tempo. Tuttavia, per lo più sono d'accordo con te :). Ho pensato che fosse un esercizio divertente. – senderle

0

Mi sembra che sia necessario rielaborare il modello per includere almeno una classe "PlayerGameStats" separata.

Qualcosa sulla falsariga di:

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) 

E per rispondere alla domanda nella tua modifica, le funzioni si possono vedere nidificate variabili memorizzate in ambiti esterni. Puoi leggere ulteriori informazioni al riguardo nel tutorial: http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces

0

grazie per il bel input, in quanto avevo una specie di problema simile. Lo risolvo con un hook sul metodo init, dal momento che mi piacerebbe essere in grado di resettare qualsiasi stato iniziale avesse un oggetto. Ecco il mio codice:

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()" 

Per aggiornare lo stato iniziale, si potrebbe costruire un po 'di metodo come reset (...), che scrive i dati in _tool_init_states. Spero che questo aiuti qualcuno. Se questo è possibile senza metaclass, per favore fatemelo sapere.

0

Mi è piaciuta (e ho provato) la risposta migliore di PaoloVictor. Tuttavia, ho scoperto che si "resettava" da solo, cioè, se si chiamava reset() una seconda volta, si generava un'eccezione.

ho trovato che ha funzionato ripetibile con la seguente implementazione

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__