2013-03-03 2 views
19

Vorrei creare un metodo all'interno di una classe con argomenti di valori predefiniti presi da questa classe. In generale faccio filtraggio su alcuni dati. All'interno della mia classe ho un metodo in cui normalmente passo al vettore di dati. A volte non ho il vettore e prendo dati simulati. Ogni volta che non passo un particolare vettore mi piacerebbe prendere i dati simulati di default. Ho pensato che dovrebbe essere una costruzione facile in cui all'interno della mia definizione del metodo dico a=self.vector. Ma per qualche ragione ho un errore NameError: name 'self' is not defined. La costruzione semplificata è:Assegnazione della variabile di classe come valore predefinito all'argomento del metodo di classe

class baseClass(object): # This class takes an initial data or simulation 
    def __init__(self): 
     self.x = 1 
     self.y = 2 

class extendedClass(baseClass): # This class does some filtering 
    def __init__(self): 
     baseClass.__init__(self) 
     self.z = 5 
    def doSomething(self, a=self.z): 
     self.z = 3 
     self.b = a 

if __name__ == '__main__': 
    a = extendedClass() 
    print a.__dict__ 
    a.doSomething() 
    print a.__dict__ 

Un'uscita I expected dovrebbe essere:

{'y': 2, 'x': 1, 'z': 5} 
{'y': 2, 'x': 1, 'z': 3, 'b': 5} 

Ho cercato di assegnazione predefinito come def doSomething(self, a=z): ovviamente non funziona mai. Per quanto ne so, self.z è visibile in questo ambito e non dovrebbe essere un problema averlo come valore predefinito. Non so perché ho questo errore e come farlo. Questa è probabilmente una domanda facile, ma cerco di capirlo o di trovare la soluzione senza problemi già da qualche tempo. Ho trovato le domande similar solo per altre lingue.

risposta

25

La tua comprensione è sbagliata. self è di per sé un parametro per quella definizione di funzione, quindi non c'è modo che possa essere in ambito in quel punto. È solo nell'ambito della funzione stessa.

La risposta è semplicemente di default l'argomento None, e quindi controllare per questo all'interno del metodo:

def doSomething(self, a=None): 
    if a is None: 
     a = self.z 
    self.z = 3 
    self.b = a 
+0

Nel caso in cui l'argomento non può assumere valori falsy, è sufficiente scrivere 'if a: a = self.z' o anche' a = a o self.z'. – danijar

+0

Penso che tu intenda "se non a: a = self.z' – BBischof

+0

Si tratta di un modello pythonic standard, ovvero con una proprietà di classe con un valore predefinito che viene utilizzato come fallback da una proprietà di metodo opzionale? O è meglio cambiare il costruttore __init__ 'extendedClass' per includere una proprietà opzionale' z = None', e se 'None' lo imposta su un valore predefinito all'interno del metodo, e non lo ha come parametro del metodo? Posso solo pensare che avere una proprietà di classe hard-coded sia utile se è davvero una costante e richiesta da più di un metodo, altrimenti perché non limitarsi semplicemente a "z" all'ambito del metodo, magari senza classe o oggetto proprietà a tutti? – Davos

6

Ecco lo smontaggio del codice per un modulo di esempio semplice. Un oggetto codice è un contenitore di sola lettura per bytecode, le costanti e i nomi che utilizza e metadati relativi al numero di variabili locali, dimensioni dello stack richieste, ecc. Si noti che tutti gli oggetti codice sono compilati come costanti. Questi sono creati al momento della compilazione. Ma gli oggetti class A e function test vengono istanziati al momento dell'esecuzione (ad es. Quando il modulo viene importato).

Per rendere la classe, BUILD_CLASS prende il nome 'A', le basi tuple(object,), e un dict che contiene gli attributi dello spazio dei nomi di classe. Questo è come istanziare manualmente un tipo chiamando lo type(name, bases, dict). Per creare dict, viene creata una funzione dall'oggetto codice A e chiamata. Infine, l'oggetto classe viene memorizzato nello spazio dei nomi del modulo tramite STORE_NAME.

Nel codice oggetto A, self.z viene caricato in pila come argomento su MAKE_FUNCTION. Il bytecode op LOAD_NAME cercherà self nei locals correnti (vale a dire lo spazio dei nomi di classe in fase di definizione), i globali dei moduli e i builtin. Ciò ovviamente fallirà se self non è definito nell'ambito globale o incorporato; chiaramente non è definito nell'ambito locale.

In caso affermativo, tuttavia, la funzione verrà creata con (self.z,) come attributo __defaults__ e quindi archiviata nel nome locale test.

>>> code = compile(''' 
... class A(object): 
... def test(self, a=self.z): pass 
... ''', '<input>', 'exec') 

>>> dis.dis(code) 
    2   0 LOAD_CONST    0 ('A') 
       3 LOAD_NAME    0 (object) 
       6 BUILD_TUPLE    1 
       9 LOAD_CONST    1 (<code object A ...>) 
      12 MAKE_FUNCTION   0 
      15 CALL_FUNCTION   0 
      18 BUILD_CLASS   
      19 STORE_NAME    1 (A) 
      22 LOAD_CONST    2 (None) 
      25 RETURN_VALUE 

>>> dis.dis(code.co_consts[1]) # code object A 
    2   0 LOAD_NAME    0 (__name__) 
       3 STORE_NAME    1 (__module__) 

    3   6 LOAD_NAME    2 (self) 
       9 LOAD_ATTR    3 (z) 
      12 LOAD_CONST    0 (<code object test ...>) 
      15 MAKE_FUNCTION   1 
      18 STORE_NAME    4 (test) 
      21 LOAD_LOCALS   
      22 RETURN_VALUE  

@uselpa: Il vostro esempio pastebin (riscritto per 2.x):

>>> code = compile(''' 
... default = 1 
... class Cl(object): 
...  def __init__(self, a=default): 
...   print a 
... Cl() 
... default = 2 
... Cl() 
... ''', '<input>', 'exec') 
>>> dis.dis(code) 
    2   0 LOAD_CONST    0 (1) 
       3 STORE_NAME    0 (default) 

    3   6 LOAD_CONST    1 ('Cl') 
       9 LOAD_NAME    1 (object) 
      12 BUILD_TUPLE    1 
      15 LOAD_CONST    2 (<code object Cl ...>) 
      18 MAKE_FUNCTION   0 
      21 CALL_FUNCTION   0 
      24 BUILD_CLASS   
      25 STORE_NAME    2 (Cl) 

    6   28 LOAD_NAME    2 (Cl) 
      31 CALL_FUNCTION   0 
      34 POP_TOP    

    7   35 LOAD_CONST    3 (2) 
      38 STORE_NAME    0 (default) 

    8   41 LOAD_NAME    2 (Cl) 
      44 CALL_FUNCTION   0 
      47 POP_TOP    
      48 LOAD_CONST    4 (None) 
      51 RETURN_VALUE   

Come si può vedere, classe di oggetti Cl (e la funzione oggetto __init__) viene istanziato solo e memorizzato al nome locale 'Cl' una volta. Il modulo viene eseguito in modo sequenziale in fase di runtime, quindi in seguito il nuovo nome default non avrà alcun effetto sul valore predefinito in __init__.

Si potrebbe istanziare dinamicamente una nuova funzione che utilizza il codice precedentemente compilato e un nuovo valore di default:

>>> default = 1 
>>> class Cl(object): 
...  def __init__(self, a=default): 
...   print a 
... 

>>> from types import FunctionType 
>>> default = 2 
>>> Cl.__init__ = FunctionType(
... Cl.__init__.__code__, globals(), '__init__', (default,), None) 
>>> c = Cl() 
2 

Questo riutilizza il codice oggetto già compilato da __init__.__code__ per creare una funzione con un nuovo __defaults__ tuple:

>>> Cl.__init__.__defaults__ 
(2,) 
+1

Sono bloccato. Se la classe e il metodo vengono creati al momento dell'istanziazione, allora perché ovviamente non sono nel mio esempio su http://pastebin.com/UJfMpnqW? – uselpa

+0

@uselpa: ho aggiunto il tuo esempio per spero di chiarire la differenza tra "tempo di compilazione" e "tempo di esecuzione". Un modulo può compilare correttamente senza errori di sintassi, ma ha ancora errori di runtime quando viene importato (eseguito). – eryksun

+0

Penso di aver capito. Grazie mille per la tua pazienza! – uselpa