2016-12-30 57 views
4

讓我們先說這個問題,說you should use __new__ instead of __init__ for subclassing immutable objects爲什麼我不能在python3中繼承元組?

有了這樣說,讓我們來看看下面的代碼:

class MyTuple(tuple): 
    def __init__(self, *args): 
     super(MyTuple, self).__init__(*args) 

mytuple = MyTuple([1,2,3]) 

這個工作在python2,但在python3我得到:

Traceback (most recent call last): 
    File "tmp.py", line 5, in <module> 
    mytuple = MyTuple([1,2,3]) 
    File "tmp.py", line 3, in __init__ 
    super(MyTuple, self).__init__(*args) 
TypeError: object.__init__() takes no parameters 

爲什麼會出現這種情況? python3中改變了什麼?

+0

我可以問你爲什麼不使用'__new__'?這似乎是要走的路。 – TigerhawkT3

+0

@ TigerhawkT3我現在:)但我仍然對此很好奇 – goncalopp

回答

4

Python 3改變了如何對object.__new__object.__init__對兩個參數進行覆蓋時對參數的反應。如果一個類覆蓋(或繼承了覆蓋的方法)object.__init__object.__new__,object.__init__object.__new__都會在收到任何多餘參數時拋出異常。在Python 2中,這會給出一個DeprecationWarning(默認禁止)。

tuple沒有自己的__init__。它繼承了object.__init__,所以你實際上將一堆參數傳遞給object.__init__object.__init__不接受。 Python 2正在給你一個(壓制)警告,而Python 3正在使它成爲一個錯誤。

的代碼有一個評論說,做了解釋object.__init__一個良好的工作和object.__new__的附加參數微妙的處理:

/* You may wonder why object.__new__() only complains about arguments 
    when object.__init__() is not overridden, and vice versa. 

    Consider the use cases: 

    1. When neither is overridden, we want to hear complaints about 
     excess (i.e., any) arguments, since their presence could 
     indicate there's a bug. 

    2. When defining an Immutable type, we are likely to override only 
     __new__(), since __init__() is called too late to initialize an 
     Immutable object. Since __new__() defines the signature for the 
     type, it would be a pain to have to override __init__() just to 
     stop it from complaining about excess arguments. 

    3. When defining a Mutable type, we are likely to override only 
     __init__(). So here the converse reasoning applies: we don't 
     want to have to override __new__() just to stop it from 
     complaining. 

    4. When __init__() is overridden, and the subclass __init__() calls 
     object.__init__(), the latter should complain about excess 
     arguments; ditto for __new__(). 

    Use cases 2 and 3 make it unattractive to unconditionally check for 
    excess arguments. The best solution that addresses all four use 
    cases is as follows: __init__() complains about excess arguments 
    unless __new__() is overridden and __init__() is not overridden 
    (IOW, if __init__() is overridden or __new__() is not overridden); 
    symmetrically, __new__() complains about excess arguments unless 
    __init__() is overridden and __new__() is not overridden 
    (IOW, if __new__() is overridden or __init__() is not overridden). 

    However, for backwards compatibility, this breaks too much code. 
    Therefore, in 2.6, we'll *warn* about excess arguments when both 
    methods are overridden; for all other cases we'll use the above 
    rules. 

*/ 
+0

我甚至看到了這個評論,但我想我沒有仔細閱讀它,因爲它也在python2.x代碼庫中。 – mgilson

1

我一直在圍繞C代碼庫進行挖掘,並且還沒有發現任何真正的線索(但是)在python3中更改了禁止此行爲的更改。我已經測試了python2.7,python3.3,python3.5和python3.6。你的代碼只有在沒有異常的情況下工作的時候就是python2.7。我也沒有在文檔中找到任何有關這種變化的參考文獻,但我確實有一些想法...

首先,讓我們同意tuple.__init__不能做任何事情,因爲tuple是不可變的。在調用__init__時,元組已經被凍結。所以,這引出我的猜測 - 因爲tuple.__init__什麼都不做,開發者認爲它容易讓人接受任何爭論。通過阻止基類接受參數,他們鼓勵人們重寫__new__(並因此鼓勵對不可變對象進行適當的繼承)。

+0

這是一個很好的猜測。有趣的是,這段代碼實際上適用於python2.7:'print mytuple'產生'(1,2,3)' – goncalopp

+0

@goncalopp - 當然。這是因爲'__new__'創建了正確的元組並將其傳遞給'__init__'(以及傳遞給'tuple'的初始參數)。 – mgilson

相關問題