Python __init__和varargs

时间:2017-07-25 04:02:40

标签: python python-2.7 class

根据python documentation__init__可以接受varargs:

object.__init__(self[, ...])
  

在创建实例(由__new__())之后调用,但在将其返回给调用者之前调用。参数是传递给类构造函数表达式的参数。如果基类具有__init__()方法,则派生类的__init__()方法(如果有)必须显式调用它以确保实例的基类部分的正确初始化;例如:

BaseClass.__init__(self, [args...]).

我很难理解如何在直接派生自object的类上使用varargs。特别是,当试图实例化一个调用super(<classname>, self).__init__(*args, **kwargs)的类时,我发现如果我实例化没有参数的类,一切都没问题。

但是,如果我将参数传递给init函数,我会收到错误:

super(A,self).__init__(*args,**kwargs)
TypeError: object.__init__() takes no parameters

据我所知,object.__init__应该能够根据文档获取参数 - 它还使编写代码更容易,因为每个类都可以将其参数传递给类层次结构。

文档是否不正确,或object.__init__是特例?

代码如下:

class A(object):
    def __init__(self, *args, **kwargs):
        for i,a in enumerate(args):
            print "arg", i,a
        for k,v in kwargs.iteritems():
            print "kwarg", k,v

        super(A,self).__init__(*args,**kwargs)




a=A()
print "Done with first one"
a2=A(5,4,5,3)

3 个答案:

答案 0 :(得分:1)

object只是一个特例,因为没有什么可以初始化,所以它的构造函数不接受任何args或kwargs。由于__init__上的object方法无法执行任何操作,因此如果您仅从object继承,则无需调用它。

答案 1 :(得分:1)

您对文档的解释存在问题。在你的第一行object.__init__(self[, ...])并不是指varargs。只是说可能self__init__以外的参数。

__init__与其他任何方法的区别在于super或其他任何方法。对于一个不那么令人困惑的例子,请采取abs之类的方法。如果您将多个参数传递给abs,则会引发TypeError: abs() takes exactly one argument (2 given)。这是正常的和预期的。如果您向__init__方法传递的参数多于预期,则会出现相同的错误。

未声明能够接受varargs的Python方法将无法接受任意参数。 object.__init__不接受任何参数。您可以使用__init__作为object.__init__(self)super(type(self), self).__init__()来完成所有操作。

您可以将示例重写为:

class A(object):
    def __init__(self, *args, **kwargs):
        for i, a in enumerate(args):
            print "arg", i, a
        for k, v in kwargs.iteritems():
            print "kwarg", k, v

        super(A, self).__init__()

这首先看起来毫无意义,因为您正在扩展一个不需要初始化的类(object)。但是,即使这个简单的示例也表明您可以为您的类处理一些参数,并将其他参数传递给基类。

一个常见的习惯用法是显式地命名所有子类的参数(仅位置和仅限关键字),并让父构造函数处理余数。例如,请考虑以下事项:

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Citizen(Person):
    def __init__(self, nationality, *args, **kwargs):
        self.nationality = nationality
        super(Citizen, self).__init__(*args, **kwargs)

在这种情况下,声明Citizen接受varargs 以及正常参数非常方便。您不必记住Person接受的参数,或者在更改时修改任何内容。但是,这并不意味着您可以使用完全随意的参数调用Citizen.__init__。您仍然必须传入总共三个参数,关键字只能包含名称。 nationalitynameage。请注意,Person.__init__甚至无法拨打object.__init__

要记住的重要一点是,您要处理的所有真正的类,无论是来自Python库还是外部源,都应该有足够的文档记录,告诉您可以和不能传递的内容。只是因为技术上接受varargs的东西,并不意味着它对传入的内容没有限制。

答案 2 :(得分:-3)

设计恕我直言,

super()有缺陷。见这里:https://fuhm.net/super-harmful/

简而言之:

  • 它与旧的呼叫方式非常互操作
  • 你绝不能忘记它
  • 您无法更改子类上的方法签名
  • 你甚至无法确定这些方法的调用顺序
  • 多重继承无论如何都会引起混淆。

您编写了代码,您应该知道层次结构。只需使用MySuperclass.__foobar__(self, etc)即可。一直有效。