2013-11-15 152 views
3

我寫了一個mixed number fraction class延伸和拓展標準庫Fraction類的功能,以接受任何Fraction會多:Mixed('3 4/5') == == Mixed(3,4,5) == Mixed(Fraction(19,5))Fraction(19,5),等我讀了很多關於super並且我還沒有100%肯定我明白了什麼,爲什麼這條線在Fraction類源__new__定義的:別名self = super(...).__ new __(...)?爲什麼?

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

我懷疑它使每一個參考self點和創建的新實例Fraction由於類是不可變的。那是怎麼回事?爲什麼?

+0

'self'是一個**局部變量**;而這個名字只是一個慣例。你爲什麼認爲對這個實例的每一個其他引用都會指向它不應該的東西? –

+1

請記住'__new__'是實例工廠。它產生一個新的實例,但只有當你調用正確的'type .__ new__'父方法時,它纔會返回創建的實例。 –

回答

3

super(Fraction, cls).__new__將尋找通過類MRO去尋找下一個.__new__方法,從開始的Fraction位置上搜索一個步:

>>> from fractions import Fraction 
>>> Fraction.mro() 
[<class 'fractions.Fraction'>, <class 'numbers.Rational'>, <class 'numbers.Real'>, <class 'numbers.Complex'>, <class 'numbers.Number'>, <class 'object'>] 

所以它會考慮所有的其他類要查看下一個__new__的定義,代碼然後調用它。最終,那將是object.__new__,在Python的C代碼創建形成數字對象的實際C結構:

>>> for cls in Fraction.mro()[1:]: 
...  if '__new__' in cls.__dict__: 
...   print(cls) 
... 
<class 'object'> 

__new__法的規定目的是創建一個新的實例。而且由於數字確實是不可變的,所以您希望能夠自定義實例的創建方式,因爲數字一旦存在就不能再被修改。

名稱self只是一個本地名稱。它與方法使用的約定相匹配,但__new__不是一種常規綁定方法,因爲在創建新實例時沒有什麼可以綁定。您可以使用完全不同的東西(instancethis_new_object_we_just_created等)在整個函數中替換該名稱,並且代碼仍然可以工作。 self其他函數不受它的影響。

碰巧,Fraction實例可變;該類定義了_numerator_denominator插槽,在創建實例後仍然可以反彈。 Fraction.__new__()工廠方法實際上是這樣做的;它爲這些屬性分配新的值。調整這些屬性後,返回self,從而履行__new__方法的合同,返回新實例。

原則上,設置_numerator_denominator屬性也都可以在__init__方法中完成。然而,Python開發者已經決定要堅持公約恆定類型作爲類是意思被視爲一成不變的:

>>> fraction = Fraction(3, 4) 
>>> fraction.numerator 
3 
>>> fraction.numerator = 4 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: can't set attribute 
>>> fraction._numerator = 4 
>>> fraction.numerator 
4 

,但你可以看到,如果你知道可變屬性,你可以仍然從外部突變實例。

+0

啊哈。我沒有意識到,正如Martijn Pieters在上面評論的那樣,孩子中的__new__必須調用父母的正確的__new__。假設如果例如層次結構中的介入類定義了一個'__new__'方法,那麼調用仍然會找到對象.__ new__'的方法是正確的嗎?或者換句話說,所有正確書寫的'__new__'調用最終都會在層次結構中找到他們的方式來「object .__ new__」? – JB0x2D1

+0

@ JB0x2D1:'super(Fraction,cls).__ new__'會在找到'object .__ new__'之前找到* that *方法,並且它返回* that方法返回一個實例。如果它通過調用'object .__ new__'或者使用'super()'或者其他任何方法來選擇這樣做,那就取決於該方法。 –

+1

@ JB0x2D1:它可以改爲使用緩存的對象,而不是* always *調用'object .__ new__',而是返回現有的實例。 Python對於小整數來說是這樣做的,例如,一個名爲'interning'的進程可以加速常見的比較操作並減少內存佔用。 –

相關問題