2016-05-23 74 views
4

考慮下面的代碼示例與其他基類蟒蛇abstractmethod打破了抽象功能

import abc 
class ABCtest(abc.ABC): 
    @abc.abstractmethod 
    def foo(self): 
     raise RuntimeError("Abstract method was called, this should be impossible") 

class ABCtest_B(ABCtest): 
    pass 

test = ABCtest_B() 

這正確地引發錯誤:

Traceback (most recent call last): 
    File "/.../test.py", line 10, in <module> 
    test = ABCtest_B() 
TypeError: Can't instantiate abstract class ABCtest_B with abstract methods foo 

然而當ABCtest子類也從一個內建類型一樣繼承strlist沒有錯誤,並且test.foo()調用抽象方法:

class ABCtest_C(ABCtest, str): 
    pass 

>>> test = ABCtest_C() 
>>> test.foo() 
Traceback (most recent call last): 
    File "<pyshell#0>", line 1, in <module> 
    test.foo() 
    File "/.../test.py", line 5, in foo 
    raise RuntimeError("Abstract method was called, this should be impossible") 
RuntimeError: Abstract method was called, this should be impossible 

這似乎發生在從C中定義的任何類繼承,包括itertools.chainnumpy.ndarray,但仍然正確地引發了在python中定義的類的錯誤。爲什麼要實現內置類型之一會破壞抽象類的功能?

+1

@DonkeyKong(或任何其他人沒有得到它)​​方法'foo'應該被強制覆蓋在一個子類中,通常(並且沒有從'str'繼承)實例化它會產生一個錯誤,但是當也從'str'繼承,沒有發生錯誤,抽象方法'test.foo'是一個有效的可調用方法。 –

+0

@ TadhgMcDonald-Jensen剛剛抓住了,謝謝:) – miradulo

+0

@Torxed'str'不是一個變量名。 – miradulo

回答

3

出人意料的是,測試類是否是抽象發生在object.__new__,而不是由abc模塊本身定義的東西:

static PyObject * 
object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 
{ 
    ... 
    if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) { 
     ... 
     PyErr_Format(PyExc_TypeError, 
        "Can't instantiate abstract class %s " 
        "with abstract methods %U", 
        type->tp_name, 
        joined); 

(幾乎?)所有內置類型不屬於object供應一個不同的__new__,它會覆蓋object.__new__而不會呼叫object.__new__。當您從非object內置類型多重繼承時,您將繼承其__new__方法,繞過抽象方法檢查。

我在abc文檔中看不到__new__或內置類型的多重繼承。文檔可以在這裏使用增強。

看起來有點奇怪,他們會爲ABC實現使用元類,使得使用其他元類與抽象類混亂,然後將核心語言代碼中的關鍵檢查與abc無關併爲抽象類和非抽象類運行。