Python 设置从int、float或str继承的类中参数的值

Python 设置从int、float或str继承的类中参数的值,python,class,inheritance,python-internals,Python,Class,Inheritance,Python Internals,当我试图设置从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"

当我试图设置从
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'
。 当我执行此操作时,会发生以下情况:

执行方法
myClass.\uuuu new\uuuu
。此方法将创建对象
myInstance
\uuuu new\uuuu
是真正的构造函数(
\uuuuuu init\uuuu
不是构造函数!)。在伪代码中,
\uuuuu new\uuuu
如下所示:

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\uuuu new\uuu(cls,*args,**kwargs)
。对于不可变类型,方法
\uuuu new\uuu
的伪代码更像是
def\uuuu new\uuu(cls,anUniqueValue)
。我真的不明白为什么
immutableTypes.\uuuu new\uuuu
的行为与其他行为不同,但事实就是如此。您可以在这个示例中看到:

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).
从这里,我们可以理解为什么早期提出的解决方案有效。我们要做的是编辑一个不可变类型的方法
\uuuuu new\uuuuu
,使其像一个可变类型一样工作

def __new__(cls, *args, **kwargs):
    return str.__new__(cls)
这两行将
def\uuuu new\uuuu(cls,anUniqueValue)
转换为
def\uu new\uuuu(cls,*args,**kwargs)


我希望我的解释几乎清楚,没有那么多错误。如果你说法语,你可以在这个链接上了解更多信息:

你写的基本上是正确的,这里和那里有一些错误


这并不完全正确:如果不将任何参数传递给
str.\uuuu new\uuuu
,则新实例将相当于一个空字符串

class A(str):

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

    def __init__(self, arg):
        print(arg)
通常,
\uuuuuu new\uuuu
\uuuuu init\uuuuu
应具有相同的签名。这不是一个要求,但在这种情况下,您需要将
arg
传递到
str.\uuu new\uuuu
,因此您必须拦截
arg


执行方法
myClass.\uuuu new\uuuu
。此方法将创建对象
myInstance
\uuuu new\uuuu
是真正的构造函数(
\uuuuuu init\uuuu
不是构造函数!)。在伪代码中,
\uuuuu new\uuuu
如下所示:

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.
调用
\uuuuu init\uuuu
不是
\uuuu new\uuuu
的责任,如下简单示例所示:

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__
正如您所看到的,在我的
\uuuuuuu new\uuuuu
中,我没有显式调用
\uuuuuuu init\uuuuuu
,并且
对象

每当
\uuuuuu new\uuuuu
返回该类型的实例时,Python解释器就会自动调用
\uuuuuu init\uuuu


当我们使用不可变类型(str、int、float、tuple)时,情况略有不同

这并不完全正确。当我们从未使用默认
\uuuuuu new\uuuuuu
实现的类型继承时,情况就不同了

默认的
\uuuuuu new\uuuuu
实现(即
对象.\uuuuu new\uuuu
)非常宽松,会忽略每个位置参数和关键字参数。如果您用一个不太宽容的实现来替换它,那么您所观察到的问题就会发生

需要了解的是,问题并不是由非默认的
\uuuuuuuuuuuuuuuuuuuuuuuu
本身带来的,而是因为我们的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu init
不兼容


你明白了。只有一位是错误的:
str.\uuu new\uuu
的正确签名是:

def __new__(cls[, object[, encoding[, errors]]])
除了
cls
之外的所有参数都是位置参数和关键字参数。事实上,你可以做到:

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

看到了吗?我们为
\uuuuu init\uuuu
使用了一个签名,该签名与
str.\uuuu new\uuuu
兼容,现在我们可以避免覆盖
\uuu new\uuuu

虽然我不确定Alex Martelli所说的“同样任意的超类”到底是什么意思,但可能与此有关。@Morgan,解释在链接的问题中,可能还有一个her可以为您提供清晰的解释;)@摩根,当你知道到底发生了什么事时,请毫不犹豫地回答你自己的问题。我自己并没有完全理解链接问题中的解释。@Morgan,没问题,答案中解释得很好,所以如果你有更简单的解释,你一定要把它作为答案写下来
>>> class C(str):
...     def __init__(self, object):
...         pass
... 
>>> C('abc')
'abc'
>>> C(object='abc')
'abc'