2013-10-09 10 views
7

dalla domanda Why does or rather how does object.__new__ work differently in these two casesPerché opporsi .__ lavoro new__ in modo diverso in questi tre casi

l'autore non era interessato al perché, ma piuttosto nel modo.

mi sta davvero molto a capire il motivo per cui, in particolare:

  1. perché non è object.__init__ non accetta parametri stampati invece di object.__new__ (in testclass1)

  2. perché nessun errore viene generato per testclass3? (In quanto non ha argomenti diversi da sé)

codice

>>> class testclass1(object): 
    ...  pass 
    ... 

>>> class testclass2(object): 
    ...  def __init__(self,param): 
    ...    pass 
    ... 

>>> a = object.__new__(testclass1, 56) 
    Traceback (most recent call last): 
     File "<stdin>", line 1, in <module> 
    TypeError: object.__new__() takes no parameters 

>>> b = object.__new__(testclass2, 56) 

>>> b 
    <__main__.testclass2 object at 0x276a5d0> 

>>> class testclass3(object): 
    ...  def __init__(self): 
    ...    pass 
    ... 

>>> c = object.__new__(testclass3, 56) 

>>> c 
    <__main__.testclass3 object at 0x276a790> 

>>> c1 = object.__new__(testclass3) 

>>> c1 
    <__main__.testclass3 object at 0x276a810> 
+0

Hai letto [questo] (http://hg.python.org/cpython/file/44ed0cd3dc6d/Objects/typeobject.c#l2818) un commento sui sorgenti python? Hanno deciso di * non * sollevare un errore in alcune circostanze per consentire di definire solo uno tra '__init__' o' __new__'. Altrimenti dovresti sempre ridefinirli, anche se non erano operativi. – Bakuriu

+1

Non capisco che: s ho definito solo __init__, non ci sono argomenti (oltre al self ovviamente), sto passando un argomento a __new__, perché non genera un errore? – ychaouche

risposta

14

Si utilizza una versione precedente di Python; il messaggio di errore è stato poi aggiornato:

>>> object.__new__(testclass1, 56) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: object() takes no parameters 

Python si lamenterà solo circa __init__ argomenti che non supportano se né __new____init__ sono stati sovrascritti; per esempio. quando si ereditano entrambi da object. testclass1 adatta tal caso, testclass3 non lo fa perché ha un metodo __init__.

Questo per sostenere l'attuazione tipi immutabili che non hanno un uso per __init__ (che sarebbe ereditato da object in quel caso), e tipi mutabili, dove __new__ non dovrebbe preoccuparsi quali argomenti __init__ aspetta (che di solito sarebbe più argomenti).

Vedi issue 1683368 dove Guido van Rossum, spiega le sue motivazioni per questo.

Il typeobject.c source code ha questo da dire:

Vi chiederete perché object.__new__() lamenta solo di argomenti
quando object.__init__() sia di rango, e viceversa.

Considerate i casi d'uso:

  1. Quando non viene sovrascritto, vogliamo sentire lamentele su eccesso (cioè, eventuali) argomenti, in quanto la loro presenza potrebbe indicare c'è un bug.

  2. Quando si definisce un tipo immutabile, è probabile che l'override solo __new__(), dal momento che __init__() è chiamato troppo tardi per inizializzare un oggetto immutabile . Dal momento che __new__() definisce la firma per il tipo , sarebbe un dolore di dover ignorare __init__() solo per impedire che lamentano argomenti in eccesso.

  3. Quando si definisce un tipo Mutabile, è probabile che si verifichi l'override solo __init__(). Quindi qui si applica il ragionamento inverso: non dobbiamo voler sostituire il __new__() solo per fermarlo dal lamentarsi.

  4. Quando __init__() viene sostituita, e la sottoclasse __init__() chiama object.__init__(), quest'ultimo deve lamentare eccesso argomenti; idem per __new__().

Utilizzare casi 2 e 3 rendono poco attraente per controllare incondizionatamente per argomenti in eccesso. La soluzione migliore che si rivolge a tutti i casi quattro d'uso è la seguente: __init__() lamenta argomenti in eccesso meno __new__() viene ignorata e __init__() sia di rango (IOW, se __init__() viene sostituita o __new__() sia di rango); simmetricamente, __new__() lamenta argomenti in eccesso a meno __init__() viene sostituita e __new__() sia di rango (IOW, se __new__() viene sostituita o __init__() sia di rango).

Tuttavia, per la compatibilità con le versioni precedenti, ciò causa un numero eccessivo di codice. Pertanto, in 2.6, si invierà sugli argomenti in eccesso quando vengono sovrascritti entrambi i metodi ; per tutti gli altri casi utilizzeremo le precedenti regole .

Nota che il metodo .__init__()stesso sarà ancora lamentare! Quando si crea un'istanza, vengono chiamati sia __new__ sia __init__; il tuo codice chiama solo __new__ direttamente e fa non invoca __init__! La creazione di un'istanza di testclass1 e testclass3 entrambi non riesce se si passa in argomenti:

>>> testclass1(56) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: object() takes no parameters 
>>> testclass3(56) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() takes exactly 1 argument (2 given) 

L'unica differenza è che per testclass1 sono i metodi predefiniti per object() che si lamentano invece un errore specifico per il costume __init__.

Problemi correlati