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,)
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
Penso che tu intenda "se non a: a = self.z' – BBischof
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