Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在python中用父类方法重写uuu init uuuuu_Python_Inheritance_Constructor_Class Method - Fatal编程技术网

在python中用父类方法重写uuu init uuuuu

在python中用父类方法重写uuu init uuuuu,python,inheritance,constructor,class-method,Python,Inheritance,Constructor,Class Method,我希望执行以下操作(在Python 3.7中): 基本上,我想用父类的工厂类方法覆盖子类的\uuuu init\uuu方法。但是,编写的代码不起作用,并引发: TypeError: __init__() takes 1 positional argument but 3 were given 我认为这意味着super()。有两条腿('Human')作为cls变量传递Human 1) 为什么这不符合书面要求?我假设super()将返回超类的代理实例,因此cls将是Animal对吗 2) 即使是这

我希望执行以下操作(在Python 3.7中):

基本上,我想用父类的工厂类方法覆盖子类的
\uuuu init\uuu
方法。但是,编写的代码不起作用,并引发:

TypeError: __init__() takes 1 positional argument but 3 were given
我认为这意味着
super()。有两条腿('Human')
作为
cls
变量传递
Human

1) 为什么这不符合书面要求?我假设
super()
将返回超类的代理实例,因此
cls
将是
Animal
对吗

2) 即使是这种情况,我也不认为这段代码达到了我想要的效果,因为classmethod返回了
Animal
的一个实例,但我只想用classmethod的方法初始化
Human
,有什么方法可以达到我想要的行为吗


我希望这不是一个很明显的问题,我发现on
super()
有些混乱。

调用
super
得到的代理对象仅用于定位要调用的具有两条腿的
方法(由于您没有在
Human
中覆盖它,您可以使用
self.with_两条腿
获得相同的结果)

如前所述,您的具有两条腿的替代构造函数
不起作用,因为
Human
类具有不同的构造函数签名,从而打破了传统的构造函数。即使您可以让代码调用
Animal
来构建您的实例,您也会遇到问题,因为您最终会得到一个
Animal
实例,而没有t a
Human
one(因此
Human
中的其他方法,如果您编写了一些,将不可用)

请注意,这种情况并不少见,许多Python子类的构造函数签名与其父类不同。但这确实意味着您不能自由地使用一个类来代替另一个类,就像尝试构造实例的
classmethod
一样。您需要避免这些情况

在这种情况下,最好为
Animal
构造函数的
legs
参数使用一个默认值。如果没有传递替代数字,它可以默认为
2
legs。这样,您就不需要
类方法
,并且在重写
\uuu init\uuu
时也不会遇到问题:

class Animal:
    def __init__(self, name, legs=2): # legs is now optional, defaults to 2
        self.legs = legs
        print(name)

class Human(Animal):
    def __init__(self):
        super().__init__('Human')

john = Human()

通过调用
super
获得的代理对象仅用于定位要调用的
with_two_legs
方法(由于您没有在
Human
中重写它,因此您可以使用
self.with_two_legs
获得相同的结果)

如前所述,您的具有两条腿的替代构造函数
不起作用,因为
Human
类具有不同的构造函数签名,从而打破了传统的构造函数。即使您可以让代码调用
Animal
来构建您的实例,您也会遇到问题,因为您最终会得到一个
Animal
实例,而没有t a
Human
one(因此
Human
中的其他方法,如果您编写了一些,将不可用)

请注意,这种情况并不少见,许多Python子类的构造函数签名与其父类不同。但这确实意味着您不能自由地使用一个类来代替另一个类,就像尝试构造实例的
classmethod
一样。您需要避免这些情况

在这种情况下,最好为
Animal
构造函数的
legs
参数使用一个默认值。如果没有传递替代数字,它可以默认为
2
legs。这样,您就不需要
类方法
,并且在重写
\uuu init\uuu
时也不会遇到问题:

class Animal:
    def __init__(self, name, legs=2): # legs is now optional, defaults to 2
        self.legs = legs
        print(name)

class Human(Animal):
    def __init__(self):
        super().__init__('Human')

john = Human()
super()。用两条腿('Human')
实际上用两条腿调用
Animal
,但它传递
Human
,因为
cls
,而不是
Animal
super()
使代理对象仅用于协助方法查找,它不会更改传递的内容(它仍然是源于
self
cls
)在这种情况下,
super()
甚至没有做任何有用的事情,因为
Human
不会用两条腿覆盖
,所以:

super().with_two_legs('Human')
表示“在定义它的层次结构中,从上面的第一个类
Human
调用
,使用两条腿
”,并且:

意思是“在层次结构中以定义它的
cls
开头的第一个类上用两条腿调用
”。只要
动物
下面没有定义它的类,它们就做同样的事情

这意味着您的代码在
返回cls(name\u full,2)
时中断,因为
cls
仍然是
Human
,而您的
也是Human.\uuuu init\uuuu
不接受任何超出
self
的参数。即使您费尽心思让它工作(例如,通过添加两个您忽略的可选参数),这将导致一个无限循环,如
人类。
称为
动物。它有两条腿,然后试图构造一个
人类
,再次调用
人类。

您试图做的不是一个好主意;备用构造函数本质上依赖于类的核心构造函数/初始值设定项。如果您试图创建一个依赖于备用构造函数的核心构造函数/初始值设定项,那么您已经创建了一个循环依赖项

在这种特殊情况下,我建议避免使用替代构造函数,要么显式地始终提供
legs
计数,要么使用执行替代构造函数任务的中间
TwoLeggedAnimal
类。如果要重用代码,第二个选项只意味着“从name生成name_full的超长代码”可以进入第一个选项中的TwoLeggedAnimal,
cls.with_two_legs('Human')
class Animal:
    def __init__(self, name, legs):
        self.legs = legs
        print(name)

class TwoLeggedAnimal(Animal)
    def __init__(self, name):
        # extremely long code to generate name_full from name
        name_full = name
        super().__init__(name_full, 2)

class Human(TwoLeggedAnimal):
    def __init__(self):
        super().__init__('Human')
class Animal:
    def __init__(self, name, legs):
        self.legs = legs
        print(name)

    @staticmethod
    def _make_two_legged_name(basename):
        # extremely long code to generate name_full from name
        return name_full

    @classmethod 
    def with_two_legs(cls, name):
        return cls(cls._make_two_legged_name(name), 2)

class Human(Animal):
    def __init__(self):
        super().__init__(self._make_two_legged_name('Human'), 2)
def __init__(self):
    self_template = super().with_two_legs('Human')
    # Cheaty way to copy all attributes from self_template to self, assuming no use
    # of __slots__
    vars(self).update(vars(self_template))
class Animal:
    def __new__(cls, name, legs):  # Use __new__ instead of __init__
        self = super().__new__(cls)  # Constructs base object
        self.legs = legs
        print(name)
        return self  # Returns initialized object
    @classmethod
    def with_two_legs(cls, name):
        # extremely long code to generate name_full from name
        name_full = name
        return Animal.__new__(cls, name_full, 2)  # Explicitly call Animal's __new__ using correct subclass

class Human(Animal):
    def __new__(cls):
        return super().with_two_legs('Human')  # Return result of alternate constructor