L'invocazione del metodo in Python consiste di due distinti passaggi separabili. Prima viene eseguita una ricerca degli attributi, quindi viene richiamato il risultato di tale ricerca. Ciò significa che i due seguenti frammenti hanno la stessa semantica:
foo.bar()
method = foo.bar
method()
Abilità ricerca in Python è un processo piuttosto complesso. Diciamo che stiamo cercando fino attributo denominato attr sull'oggetto obj, il che significa l'espressione seguente codice Python: obj.attr
primo dizionario esempio 's obj viene cercato per 'attr', poi il il dizionario di istanza della classe obj ei dizionari delle sue classi genitore vengono ricercati nell'ordine di risoluzione dei metodi per "attr".
Normalmente se viene trovato un valore nell'istanza, viene restituito. Ma se la ricerca sulla classe produce un valore che ha entrambi i metodi __get__ e __set__ (per essere precisi, se una ricerca di dizionario sulla classe values e le classi parent hanno valori per entrambe le chiavi) allora l'attributo class è considerato come qualcosa chiamato "descrittore di dati". Ciò significa che il metodo __get__ su quel valore viene chiamato, passando l'oggetto su cui si è verificata la ricerca e il risultato di tale valore viene restituito. Se l'attributo class non viene trovato o non è un descrittore di dati, viene restituito il valore dal dizionario delle istanze.
Se non c'è alcun valore nel dizionario di istanza, viene restituito il valore dalla ricerca di classe. A meno che non si tratti di un "descrittore non dati", cioè ha il metodo __get__. Quindi viene richiamato il metodo __get__ e restituito il valore risultante.
C'è un'altra caso particolare, se il obj sembra essere una classe, (un'istanza del tipo tipo), allora il valore di istanza viene anche controllato se è un descrittore e invocato conseguenza.
Se non viene trovato alcun valore nell'istanza o nella sua gerarchia di classi e la classe obj ha un metodo __getattr__, tale metodo viene chiamato.
Quanto segue mostra l'algoritmo come codificato in Python, facendo effettivamente ciò che farebbe la funzione getattr(). (Escluse eventuali bug che sono avanza)
NotFound = object() # A singleton to signify not found values
def lookup_attribute(obj, attr):
class_attr_value = lookup_attr_on_class(obj, attr)
if is_data_descriptor(class_attr_value):
return invoke_descriptor(class_attr_value, obj, obj.__class__)
if attr in obj.__dict__:
instance_attr_value = obj.__dict__[attr]
if isinstance(obj, type) and is_descriptor(instance_attr_value):
return invoke_descriptor(instance_attr_value, None, obj)
return instance_attr_value
if class_attr_value is NotFound:
getattr_method = lookup_attr_on_class(obj, '__getattr__')
if getattr_method is NotFound:
raise AttributeError()
return getattr_method(obj, attr)
if is_descriptor(class_attr_value):
return invoke_descriptor(class_attr_value, obj, obj.__class__)
return class_attr_value
def lookup_attr_on_class(obj, attr):
for parent_class in obj.__class__.__mro__:
if attr in parent_class.__dict__:
return parent_class.__dict__[attr]
return NotFound
def is_descriptor(obj):
if lookup_attr_on_class(obj, '__get__') is NotFound:
return False
return True
def is_data_descriptor(obj):
if not is_descriptor(obj) or lookup_attr_on_class(obj, '__set__') is NotFound :
return False
return True
def invoke_descriptor(descriptor, obj, cls):
descriptormethod = lookup_attr_on_class(descriptor, '__get__')
return descriptormethod(descriptor, obj, cls)
Che cosa significa tutto questo descrittore sciocchezze devono con chiamata di metodo si chiede? Beh, il fatto è che anche le funzioni sono oggetti, e capita di implementare il protocollo descrittore. Se la ricerca dell'attributo trova un oggetto funzione sulla classe, vengono chiamati i metodi __get__ e restituisce un oggetto "metodo associato". Un metodo associato è solo un piccolo wrapper attorno all'oggetto funzione che memorizza l'oggetto su cui è stata cercata la funzione, e quando viene invocato antepone l'oggetto alla lista degli argomenti (dove in genere per le funzioni che si intendono metodi l'argomento auto è).
Ecco alcuni codice illustrativi:
class Function(object):
def __get__(self, obj, cls):
return BoundMethod(obj, cls, self.func)
# Init and call added so that it would work as a function
# decorator if you'd like to experiment with it yourself
def __init__(self, the_actual_implementation):
self.func = the_actual_implementation
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
class BoundMethod(object):
def __init__(self, obj, cls, func):
self.obj, self.cls, self.func = obj, cls, func
def __call__(self, *args, **kwargs):
if self.obj is not None:
return self.func(self.obj, *args, **kwargs)
elif isinstance(args[0], self.cls):
return self.func(*args, **kwargs)
raise TypeError("Unbound method expects an instance of %s as first arg" % self.cls)
Per ordine di risoluzione dei metodi (che nel caso di pitone significa in realtà ordine risoluzione attributo) Python utilizza l'algoritmo C3 da Dylan. È troppo complicato da spiegare qui, quindi se sei interessato vedi this article.A meno che tu non stia facendo delle gerarchie di ereditarietà davvero funky (e non dovresti), è sufficiente sapere che l'ordine di ricerca è lasciato a destra, prima la profondità e tutte le sottoclassi di una classe vengono cercate prima che la classe venga cercata.
Hai difficoltà a trovare la fonte? Hai guardato su http://svn.python.org/view/python/trunk/Python/ –
Le fonti mi sembrano un po 'più basse, in ogni caso grazie per l'interesse. –