2

我正在查看一些带有拼写错误的__init__()函数的代码(它总共有 3 个下划线而不是 4 个)。我意识到当一个对象找不到合适的__init__().

是否有自动插入的默认初始化程序?有没有__new__被调用的默认值?为什么带参数的构造函数会失败?

class TestClass(object):
    def do_something(self):
        print("Hello From TestClass!")

# What is called here?
instance = TestClass()
instance.do_something()

# Why does this fail?
argument = 100
instance = TestClass(argument)
4

3 回答 3

2

是否有自动插入的默认初始化程序?

是的。除非被覆盖/继承,否则每个类都有一个默认的虚拟构造函数。

有没有__new__被调用的默认值?

是的。同样默认情况下,除了实际创建对象并设置其类型外,它并没有做太多事情。

为什么带参数的构造函数会失败?

因为默认构造函数不接受任何参数并且什么也不做。

于 2015-12-31T16:47:33.433 回答
1

你问的是两个经常混淆的话题:初始化器__init__和构造器__new__。另外,我认为您将 Python 与 Java 之类的语言混淆了,其中构造函数很重要,不像其他语言,构造和初始化混合在一个进程中(因此对于 Python 新手来说是一个常见的误解)。

在 Python 中,构造器和初始化器都是方法,与任何其他方法没有区别。有一个命名约定,解释器将调用两端带有双下划线的(某些)方法,而无需您显式地调用它们,但是如果您愿意,您可以自己调用它们,并且您会得到相同的结果。

简而言之:如果你理解__str__,你就会理解__init____new__

(我认为对于一些有经验的程序员来说,Python 最奇怪的地方在于它不会每时每刻都用特殊情况来伏击你。)

方法解析顺序

Python 2.3 的新型类通过从对象的类开始查找方法名称,然后遍历该类的祖先,直到找到要查找的名称或用完要搜索的类。

您可以通过查看其__mro__字段来查看任何类的方法解析顺序:

>>> TestClass.__mro__
(<class '__main__.TestClass'>, <class 'object'>)
>>> float.__mro__
(<class 'float'>, <class 'object'>)
>>> object.__mro__
(<class 'object'>,)

(Python 使用C3 Method Resolution Order algorithm构造元组,但对于像你这样的简单案例来说,它是矫枉过正的。C3 主要关注将复杂的多重继承图线性化为可以按可预测顺序搜索的类元组。 )

任何方法查找,甚至__init____new__,都遵循方法解析顺序。对于元组中的每个类,Python'__init__'在其类中查找字符串(如 )__dict__;如果该字符串存在,则其关联值——无论它是什么——将作为方法查找的结果返回。

在您的情况下,Python 查找TestClass.__new__, 不存在,然后object.__new__, 存在。然后它会执行object.__new__(自动填充一些参数)来构造你的新TestClass对象。初始化__init__是相同的,只是您可以自己提供一些额外的参数。

证明(在 Python 3 中):

>>> class A: pass
...
>>> A.__init__ is object.__init__
True
>>> A.__new__ is object.__new__
True
>>> A.__repr__ is object.__repr__
True
>>> A.__str__ is object.__str__
True

参数__init__

您尝试将参数传递给object.__init__失败,因为该方法不接受任何参数(除了self已经为您填写的参数)。它或多或少看起来像这样:

class Object:
    def __init__(self):
        return

结论

Python 不会像(IIRC)Java 那样为您的类“创建”默认构造函数或初始化程序。但它在课堂上各有一个object,所以如果你不自己写,你会继承它们。

于 2015-12-31T19:08:53.753 回答
1

一切都继承自objectPython 3,因此如果没有提供基类,则将使用默认值。

@freakish 回答了您的问题,但我将添加一个编码示例来补充他的解释。

让我们创建一个baseClass新类myclass将继承的新类:

class baseClass:
    def __init__(self):
        print("Inherited __init__ got called")

    def __new__(self, *args, **kwargs):
        print("Inherited __new__ got called")
        return object.__new__(self, *args, **kwargs)

这个类重载__init____new__因此任何子类的类都将获得这些方法。baseClass让我们创建一个从现在继承的示例类:

class mycls(baseClass):
    pass

空的,没有定义dunders。但是,当我们创建这个类的一个实例时,我们可以看到baseClass被调用的继承方法:

m = mycls()
Inherited __new__ got called
Inherited __init__ got called

如您所见,我们将获得继承树中父项定义的所有内容。但是,由于没有显式父类的类总是继承自object

class f: pass    
f.__bases__  # bases gives us the inherited classes

Out[218]: (object,)

我们将始终使用定义的默认行为来处理这些方法。默认情况下,__init__ 这意味着没有参数,您需要重新定义它才能自定义它!

于 2015-12-31T17:29:56.650 回答