2014-04-28 16 views
5

Ho cercato di capire __new__ e metaprogrammazione. Quindi ho dato un'occhiata al codice sorgente Python ufficiale.__new__ in fractions module

http://hg.python.org/cpython/file/2.7/Lib/fractions.py

La loro funzione __new__ per frazioni assomiglia:

class Fraction(Rational): 

    def __new__(cls, numerator=0, denominator=None): 
     """Constructs a Fraction. ...  """ 

     self = super(Fraction, cls).__new__(cls) 
     ... 

     if isinstance(numerator, float): 
       # Exact conversion from float 
       value = Fraction.from_float(numerator) 
       self._numerator = value._numerator 
       self._denominator = value._denominator 
       return self 

    @classmethod 
    def from_float(cls, f): 
     """Converts a finite float to a rational number, exactly. ...""" 
     # ... 
     return cls(*f.as_integer_ratio()) 

Perché si return self, piuttosto che

return Fraction.from_float(numerator) 

ho pensato ho capito, ma ora sono confuso .

(Edit)

Per sostenere la creazione di sottoclassi, farebbe alcuna differenza per passare alla

return cls.from_float(numerator) 
+0

Per la cronaca, il codice sembra funzionare lo stesso con le modifiche suggerite. Non ho ancora provato alcun benchmark, ma non penso che sarebbe più lento restituire direttamente il valore costruito ... – Fraxtil

risposta

5

Il codice in frazioni era un posto complicato per iniziare. È meglio costruire una comprensione passo dopo passo.

1) oggetto .__ new__ è lo strumento principale per la creazione di istanze di classi di nuovo stile:

o = object() 

2) Una classe che definisce __new__ tipicamente richiede oggetto .__ new__ per svolgere il proprio lavoro:

class A(object): 
    def __new__(cls): 
     return object.__new__(cls) 

a = A() 

3) Un classmethod si basa in genere su __new__ della classe per fare il suo lavoro:

class A(object): 

    def __new__(cls): 
     return object.__new__(cls) 

    @classmethod 
    def from_something(cls): 
     return cls.__new__() 

W esimo che la comprensione in atto, è possibile vedere ciò che il modulo frazioni sta facendo:

1) from_float() chiama cls(numerator, denominator) delegare la creazione dell'istanza alla frazione .__ nuovo metodo __().

2) Il Frazione .__ nuova __() metodo utilizza super() per chiamareoggetto .__ nuova __() per fare la creazione istanza effettiva.

Addenda

@Blckknght vuole sapere perché value non viene restituito direttamente (dopo la sua creazione, il suo numeratore e demoninator sono copie in self prima della value si getta. La risposta è che Fraction.from_float . crea un'istanza Frazione anziché cls In altre parole, il codice fornisce supporto per subclassing:

>>> from fractions import Fraction 
>>> class MyFraction(Fraction): 
     pass 

>>> f = MyFraction(2, 3) 
>>> f 
Fraction(2, 3) 
>>> f.__class__    # this MUST be MyFraction, not a Fraction 
<class '__main__.MyFraction'> 

Modifica

@pluto vuole sapere "perché non usare: cls.from_float ritorno (numeratore)"?

Si chiama Open-Closed Principle. Un subclasser dovrebbe essere in grado di eseguire l'override from_float() senza involontariamente rompere __init __():

>>> class MyFraction(Fraction): 
     def from_float(cls, *args): 
     raise NotImplemented 


>>> f = MyFraction(5.75) 
>>> f 
Fraction(23, 4) 
+0

Penso che ti manchi il punto. 'Fraction (1.0)' finisce per creare due separate istanze 'Fraction',' self' e 'value'. 'value' viene scartato immediatamente dopo che i valori di' _numerator' e '_denominator' vengono copiati su' self'. Non c'è alcun ovvio motivo per cui il blocco '# Exact conversion' funziona in questo modo (che posso vedere). Invece, potrebbe essere 'return Fraction.from_float (numeratore)', che scarta 'self' e restituisce il valore' 'non più chiamato ''. Idealmente, suppongo, dovresti rimandare la creazione di 'self', o usare semplicemente' numerator.as_integer_ratio' direttamente, ma sospetto che la logica si complichi. – Blckknght

+0

Hmm, apprezzo il tuo aggiornamento. Sospetto che tu possa ancora risolvere il problema della sottoclasse chiamando 'cls.from_float' piuttosto che' Fraction.from_float'. Suppongo che rischierebbe la possibilità che una sottoclasse sovrascriva 'from_float' in qualche modo incompatibile, ma se lo fanno probabilmente avranno anche' __new__' sovrascritto, o molte altre cose saranno probabilmente rotte. – Blckknght

+0

Per la sottoclasse, perché non usare: return cls.from_float (numeratore) – Richard