当我尝试设置继承自str
的类的参数值时,会引发错误。仅当我尝试使用myClass(arg = 'test')
访问参数时才会发生错误。错误是:
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
只有最后一行才会引发错误。前一行效果很好。
对于从int
或float
继承的类,问题是相同的。
更新(解决方案):
我找到了这些链接的解决方案:
解决方案是:
class A(str):
def __new__(cls, *args, **kwargs):
return str.__new__(cls)
def __init__(self, arg01):
print(arg01)
A(arg01= "test")
我不确切知道为什么会有效,我会对此进行调查。如果有人有明确的解释,我很感兴趣,我提前感谢他。
更新(我的解释):
我不确定我会说什么,但这就是我所理解的。
想象一下没有任何遗产的班级'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,int,float,tuple)时,情况会有所不同。在之前的伪代码中,我写了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/
答案 0 :(得分: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__
返回该类型的实例,Python解释器就会自动调用 __new__
。
当我们使用不可变类型(str,int,float,tuple)时,情况会有所不同。
这不完全正确。当我们从未使用默认__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'
你知道吗?我们使用与__init__
兼容的str.__new__
签名,现在我们可以避免覆盖__new__
!