2015-12-25 22 views
5

當我嘗試設置從str繼承的類的參數值時會引發錯誤。只有當我嘗試使用myClass(arg = 'test')訪問參數時纔會出現錯誤。錯誤是:設置從int或float或str繼承的類中參數的值

TypeError: 'arg' is an invalid keyword argument for this function 

這個問題在本例所示:

class A(str):  
    def __init__(self, arg): 
     pass 

A("test") # Works well 
A(arg = "test") # Fails 

只有最後一行引發錯誤。前一行運行良好。 問題與從intfloat繼承的類相同。

UPDATE(溶液):

我發現這些鏈接的解決方案:

的解決方案是:

class A(str): 

    def __new__(cls, *args, **kwargs): 
     return str.__new__(cls) 

    def __init__(self, arg01): 
     print(arg01) 

A(arg01= "test") 

我不確切知道爲什麼這樣做,我會對此進行調查。如果有人有清楚的解釋,我很感興趣,我提前感謝他。

UPDATE(我的解釋):

我不知道所有的東西,我會說,但是這是我的理解。

想象一下沒有遺傳的類'myClass'。 當我這樣做myInstance = myClass(),這裏發生了什麼:

方法myClass.__new__被執行。此方法將創建對象myInstance__new__是真正的構造函數(__init__不是構造函數!)。在僞代碼,__new__是這個樣子:

def __new__ (cls, *args, **kwargs): 

    myInstance = # Do some stuff to create myInstance, an object of the type passed as argument (cls). 
    # Add the method __init__ to myInstance. 
    # Call __init__ and pass to it the list 'args' and the dictionary 'kwargs' (both come from arguments of __new__). Pass to it also the object itself (self) : 
    obj.__init__(self, args, kwargs) : 
     # Do some stuff. 

的情況有點不同的,當我們使用不變類型(STR,整型,浮點,數組)。在之前的僞代碼中,我寫了def __new__(cls, *args, **kwargs)。對於不可變類型,方法__new__的僞代碼更像此def __new__(cls, anUniqueValue)。我沒有真正理解爲什麼immutableTypes.__new__的行爲是不同的,但事實如此。你可以看到它在本例:

class foo(): 

    def __init__(self): 
     pass 

foo.__new__(foo, arg = 1) 
# That works because the method __new__ look like this : def __new__(*args, **kargs). 

str.__new__(str, arg = 1) 
# That fails because we are trying to pass the attribute 'arg' to a method which look like this : def __new__(anUniqueValue). 

從這裏,我們可以理解,爲什麼前面提供的解決方案工作。我們所做的就是編輯一個不可變類型的方法__new__,就像一個可變類型一樣工作。

def __new__(cls, *args, **kwargs): 
    return str.__new__(cls) 

這兩條線轉換def __new__ (cls, anUniqueValue)def __new__ (cls, *args, **kwargs)

我希望我的解釋是幾乎清晰,沒有這麼多的失誤。如果你講法語,你可以在這個鏈接上了解更多:http://sametmax.com/la-difference-entre-new-et-init-en-python/

+1

可能與http://stackoverflow.com/questions/3748635/adding-optional-parameters-to-the-constructors-of-multiply-inheriting-subclasses有關,雖然我不確定Alex Martelli的意思是'同樣任意的超級「。 – timgeb

+1

@摩根,解釋是在相關的問題中,也可能有一個*她*,可以爲您提供一個清晰的解釋;) –

+0

@摩根毫不猶豫地回答你自己的問題,當你發現究竟是怎麼回事上。我自己並沒有完全理解相關問題的解釋。 – timgeb

回答

2

你寫的東西基本上是正確的,這裏和那裏都有一些錯誤。


class A(str): 

    def __new__(cls, *args, **kwargs): 
     return str.__new__(cls) 

    def __init__(self, arg01): 
     print(arg01) 

這不完全正確的:如果你沒有任何參數傳遞給str.__new__,新的實例將是一個空字符串的等價物。

class A(str): 

    def __new__(cls, arg): 
     return str.__new__(cls, arg) 

    def __init__(self, arg): 
     print(arg) 

一般來說,__new____init__應該具有相同的簽名。這不是要求,但在這種情況下,您需要將arg傳遞給str.__new__,因此您必須攔截arg


方法myClass.__new__被執行。此方法將創建對象myInstance__new__是真正的構造函數(__init__不是構造函數!)。在僞代碼,__new__是這個樣子:

這不是__new__責任調用__init__,通過這個簡單的例子作爲證明:

class C: 

    def __new__(cls): 
     print('entering __new__') 
     self = super().__new__(cls) 
     print('exiting __new__') 
     return self 

    def __init__(self): 
     print('entering __init__') 
     super().__init__() 
     print('exiting __init__') 


C() 

# Output: 
# entering __new__ 
# exiting __new__ 
# entering __init__ 
# exiting __init__ 

正如你所看到的,我__new__我沒明確地打電話__init__,並且object.__new__也不打電話__init__

__init__只要__new__返回一個類型的實例就被Python解釋器自動調用。


的情況有點不同的,當我們使用不變類型(STR,整型,浮點,數組)。

這不完全正確。當我們從不使用默認的__new__實現的類型繼承時,情況不同

默認的__new__實現(即object.__new__)非常寬容,並忽略每個位置參數和關鍵字參數。如果你用一個不太寬容的實現來替換它,那麼你正在觀察的問題就會發生。

重要的是要明白的是,這個問題不是由非默認__new__本身帶來的,而是由一個事實,即我們__init____new__兼容。


foo.__new__(foo, arg = 1) 
# That works because the method __new__ look like this : def __new__(*args, **kargs). 
str.__new__(str, arg = 1) 
# That fails because we are trying to pass the attribute 'arg' to a method which look like this : def __new__(anUniqueValue). 

你猜對了。只是一個位是錯誤的:爲str.__new__正確的簽名是:

def __new__(cls[, object[, encoding[, errors]]]) 

除了cls所有參數都是位置和關鍵字參數。其實你可以這樣做:

>>> class C(str): 
...  def __init__(self, object): 
...   pass 
... 
>>> C('abc') 
'abc' 
>>> C(object='abc') 
'abc' 

你看?我們使用了與str.__new__兼容的__init__簽名,現在我們可以避免重寫__new__

+0

感謝這些精度! – Morgan

相關問題