最近我試過玩過多個類繼承(在Python 3中),這是我以前從未做過的(從來沒有真正用過它)。python:用於多重繼承的更好的基礎'對象'
我很驚訝它有多「壞」。它不像我期待的那樣行事。我瞭解MRO和訂單解決方案,但除非我錯過了一些東西,否則設計不允許在鏈條末尾提供額外的參數。
讓我舉一個簡單的例子:
class BaseA(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.pop('is_cool', False):
self.a = True
else:
self.a = False
class FixedBaseA(object):
def __init__(self, *args, **kwargs):
if kwargs.pop('is_cool', False):
self.a = True
else:
self.a = False
# if you move super to the top (see BaseA), MyThing instances will raise an error
# Note: instead of having super down here, one could have explicitly extracted
# the keyword 'is_cool' in the definition of __init__ method
# --> __init__(self, *args, is_cool=False, **kwargs)
super().__init__(*args, **kwargs)
class BaseB(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.pop('is_cool', False):
self.b = True
else:
self.b = False
class MyThing(BaseA, BaseB):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class FixedMyThing(FixedBaseA, BaseB):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class FixedMyThing2(BaseB, FixedBaseA):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class MyThing2(BaseB, BaseA):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
正如你可能猜到了,我希望的行爲是:
- 不管我給出BaseA和BaseB以什麼順序繼承的類,我會最終與屬性
a
和b
正確設置,並沒有錯誤 - 給予額外的參數不會引起錯誤
當然情況並非如此。下面是一些測試:
def test(cls, *args, **kwargs):
try:
x = cls(*args, **kwargs)
print('{:} --> a, b = {:}, {:}'.format(cls.__name__, x.a, x.b))
except TypeError as err:
print('[ERR] {:} raised a TypeError: {:}'.format(cls.__name__, err))
test(MyThing, is_cool=True)
test(FixedMyThing, is_cool=True)
test(MyThing2, is_cool=True)
test(FixedMyThing2, is_cool=True)
test(FixedMyThing2, z=3.4)
這給:
[ERR] MyThing raised a TypeError: object.__init__() takes no parameters
FixedMyThing --> a, b = True, False
[ERR] MyThing2 raised a TypeError: object.__init__() takes no parameters
FixedMyThing2 --> a, b = True, True
[ERR] FixedMyThing2 raised a TypeError: object.__init__() takes no parameters
現在我的問題不是關於爲什麼發生這種情況,有可能在網絡上找到解釋。儘管我無法判斷這些原因,但我想知道爲什麼object
類型/類的行爲不同於接受任何額外參數。
所以真正的問題是:由以下類代替object
一個壞主意?如果是的話爲什麼?
class BaseObject(object):
def __init__(self, *args, **kwargs):
try:
# this is necessary to pass along parameters for next-in-line inherited classes
super().__init__(*args, **kwargs)
except TypeError as err:
if err.__str__().startswith('object.__init__() takes no parameters'):
pass
else:
raise
代替object
使用這個類無處不在允許交換的邏輯上可以互換(即他們沒有在他們的屬性/方法重疊)班順序,並允許無任何使用額外的參數麻煩,但仍然會在任何時候出現錯誤。
MyThing --> a, b = True, True
FixedMyThing --> a, b = True, False
MyThing2 --> a, b = True, True
FixedMyThing2 --> a, b = True, True
FixedMyThing2 --> a, b = False, False
對此的任何想法都不錯。
感謝您的回答。是的,這些例子當然可以更乾淨,我只是寫得很快。我同意不應該默默地忽略錯誤,但在'object'的情況下,我沒有看到提出錯誤的觀點:它意味着在到達'object'之前必須消耗所有參數。儘管不推薦使用'*參數,** kwargs',它是完全有效的,並且對於您所提到的靈活性和中間件非常有用。但'物體'或多或少地阻止了它們的使用。它認爲這應該留給編碼人員,並且這將與Python的其他部分更加一致(例如,函數) – mhavel