Python重写__init__

时间:2017-01-05 18:11:42

标签: python

我知道有很多人就这个问题提出问题,但我没有看到我的具体问题。子类化时,您可以覆盖__init__(),就像覆盖任何其他方法一样。我的问题是为什么在下面的例子中这似乎没有正常工作:

import random
class MyRand(random.Random):
    def __init__(self, myvar1, myvar2, x=None):
        # ( ... my code ...)
        super(MyRand, self).__init__(x)

请记住,Random的构造函数具有以下签名:__init__(self, x=None)其中x是可选种子。我想在我的子类中保留该功能,但我还想要两个必需变量myvar1myvar2

但是,当您尝试实例化(没有种子)时会出现错误:

MyRand('var1', 'var2')
TypeError: seed expected at most 1 arguments, got 2

这是因为python认为你想要Random的构造函数并将你的两个参数'var1'和'var2'作为种子传递。种子(从Random的构造函数中调用)只需要1个参数,因此会出错。

但是,如果你这样做

MyRand(myvar1='var1', myvar2='var2')

这是有效的,这里python知道你传递了两个必需的变量而没有将它传递给可选的种子。

但我认为第一种情况也应该有效。发生了什么事?

2 个答案:

答案 0 :(得分:11)

在Python中,在创建对象时会调用两个方法。 __new____init__。与C中实现的许多类一样,random.Random使用__new__初始化自身(请参阅random_new)。您必须覆盖它并使用适当的参数调用它:

import random

class MyRand(random.Random):
    def __new__(cls, myvar1, myvar2, x=None):
        return random.Random.__new__(cls, x)

    def __init__(self, myvar1, myvar2, x=None):
        # ( ... my code ...)

答案 1 :(得分:8)

你错误地诊断了这个问题。问题是random.Random的初始化并未完全包含在random.Random.__init__中。它还继承了_random.Random.__new__

static PyObject *
random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    RandomObject *self;
    PyObject *tmp;

    if (type == &Random_Type && !_PyArg_NoKeywords("Random()", kwds))
        return NULL;

    self = (RandomObject *)type->tp_alloc(type, 0);
    if (self == NULL)
        return NULL;
    tmp = random_seed(self, args);
    if (tmp == NULL) {
        Py_DECREF(self);
        return NULL;
    }
    Py_DECREF(tmp);
    return (PyObject *)self;
}

您也必须覆盖__new__,并且只能在位置上传递它所期望的seed参数(因为它不会将其理解为关键字参数)

他们真的不应该像这样混合__init____new__。当你这样做时,初始化顺序变得非常奇怪。