2015-07-25 15 views
9

Ho visto questo molto spesso:Perché le persone utilizzano il parametro proprietario predefinito su Nessuno in __get__?

def __get__(self, instance, owner=None): 

Perché alcune persone usano il valore di default per la None il parametro owner?

Questo è anche fatto in Python docs:

descr.__get__(self, obj, type=None) --> value 
+0

domanda un po 'correlato: http://stackoverflow.com/q/8719585 –

+0

@BasSwinckels effetti, ma purtroppo che non fornisce informazioni sulla mia domanda. –

+0

Non correlato, ma il terzo parametro non è il "proprietario". Ciò implica che il proprietario è l'oggetto su cui si trova il descrittore. Piuttosto è il tipo con cui è stato invocato il descrittore. Cioè, se si accede al descrittore tramite una sottoclasse, il terzo parametro sarà la sottoclasse e non la classe genitore (che "possiede" il descrittore). – Dunes

risposta

6

Poiché il proprietario può essere facilmente derivato dall'istanza, il secondo argomento è facoltativo. Solo quando non c'è un'istanza da cui derivare un proprietario, è necessario l'argomento proprietario.

Questo è descritto nella proposta che introduce descrittori, PEP 252 - Making Types Look More Like Classes:

__get__: una funzione richiamabile con uno o due argomenti che recupera il valore di attributo da un oggetto. Questo è anche definito come operazione "vincolante", poiché può restituire un oggetto "metodo associato" nel caso dei descrittori di metodo. Il primo argomento , X, è l'oggetto da cui deve essere recuperato l'attributo oa cui deve essere associato.Quando X è None, il opzionale secondo argomento, T, dovrebbe essere metaoggetto e operazione di legatura può restituire un metodo non legato limitato a istanze di T.

(grassetto in grassetto).

Il binding, dal primo giorno, era pensato per essere applicabile all'istanza da solo, con il tipo facoltativo. Metodi non ne hanno bisogno, per esempio, in quanto possono essere legati al solo esempio:

>>> class Foo: pass 
... 
>>> def bar(self): return self 
... 
>>> foo = Foo() 
>>> foo.bar = bar.__get__(foo) # look ma! no class! 
>>> foo.bar 
<bound method Foo.bar of <__main__.Foo object at 0x10a0c2710>> 
>>> foo.bar() 
<__main__.Foo object at 0x10a0c2710> 

Inoltre, il secondo argomento può facilmente essere derivato dal primo argomento; assistere a un classmethod ancora vincolante per la classe, anche se non abbiamo passato uno in:

>>> classmethod(bar).__get__(foo) 
<bound method type.bar of <class '__main__.Foo'>> 
>>> classmethod(bar).__get__(foo)() 
<class '__main__.Foo'> 

L'unica ragione per cui l'argomento è lì, in primo luogo è quello di sostenere vincolante in classe, per esempio quando non ci sono istanze da associare a. Il metodo di classe di nuovo; legame None come istanza non funziona, funziona solo se abbiamo effettivamente passa nella classe:

>>> classmethod(bar).__get__(None) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __get__(None, None) is invalid 
>>> classmethod(bar).__get__(None, Foo) 
<bound method type.bar of <class '__main__.Foo'>> 
+0

Avevo l'impressione che l'argomento 'owner' sarebbe sempre la classe di antenato in cui dict l'oggetto descrittore è effettivamente trovato, ma dopo ulteriori sperimentazioni e uno sguardo alla sorgente, sembra essere semplicemente' type (X) ' . A differenza di quanto pensavo, possiamo sempre dedurre il proprietario dall'istanza. – user2357112

+1

@ user2357112: questo piuttosto vanificherebbe lo scopo di '@ classmethod', che promette di associare il metodo alla classe corrente quando è sottoclasse, non la classe su cui è stata definita. –

2

Perché è così che viene specificato il protocollo descrittore:

descr.__get__(self, obj, type=None) --> value

cf https://docs.python.org/2/howto/descriptor.html#descriptor-protocol

Th L'argomento e type consente l'accesso alla classe su cui viene esaminato il descrittore quando viene cercata su una classe anziché su un'istanza. Poiché è possibile ottenere la classe dall'istanza, è in qualche modo ridondante quando il descrittore viene esaminato su un'istanza, quindi è stato reso facoltativo per consentire la chiamata meno dettagliata desc.__get__(obj) (anziché desc.__get__(obj, type(obj))).

+2

Non penso che questo spieghi ancora il ragionamento, perché qualcuno dovrebbe fare 'desc .__ get __ (obj)'? http://pastebin.com/YwNWXR7c sembra che il proprietario sia passato in ogni caso, anche quando si usa un'istanza. –

3

Questo è il modo standard per farlo; tutti i descrittori built-in Python che ho visto farlo, incluse funzioni, proprietà, metodi statici, ecc. Non conosco alcun caso nel protocollo descrittore in cui __get__ verrà chiamato senza l'argomento proprietario, ma se si desidera chiamare manualmente __get__, può essere utile non dover passare un proprietario. L'argomento del proprietario di solito non fa molto.

Ad esempio, you might want un modo più pulito per dare a singoli oggetti nuovi metodi. Il seguente decoratore pulisce la sintassi e lascia i metodi hanno accesso a self:

def method_of(instance): 
    def method_adder(function): 
     setattr(instance, function.__name__, function.__get__(instance)) 
     return function 
    return method_adder 

@method_of(a) 
def foo(self, arg1, arg2): 
    stuff() 

Ora a ha un metodo foo. Abbiamo utilizzato manualmente il metodo __get__ della funzione foo per creare un oggetto metodo associato come qualsiasi altro, tranne che poiché questo metodo non è associato a una classe, non abbiamo passato una classe a __get__. L'unica differenza è che quando si stampa l'oggetto metodo, viene visualizzato ?.foo anziché SomeClassName.foo.

+0

Se questo è l'unico motivo dietro il valore predefinito di 'owner = None', penso che sia abbastanza inutile. Questi sembrano essere scenari estremamente rari, e potrei semplicemente scrivere 'function .__ get __ (instance, None)'. Ho accettato la tua risposta, dopo essere giunto alla conclusione che non esiste ** veramente ** una buona ragione per usare "owner = None" invece di "owner". –

Problemi correlati