Вопрос по inheritance, python – Как правильно наследовать от суперкласса, который имеет метод __new__?

9

Предположим, что у нас есть класс Parent. , что по какой-то причине имеет__new__ определен и класс «ребенок»; что наследует от этого. (В моем случае я пытаюсь наследовать от стороннего класса, который я не могу изменить)

class Parent:
    def __new__(cls, arg):
        # ... something important is done here with arg

Моя попытка была:

class Child(Parent):
    def __init__(self, myArg, argForSuperclass):
         Parent.__new__(argForSuperclass)
         self.field = myArg

Но пока

p = Parent("argForSuperclass")

работает как положено

c = Child("myArg", "argForSuperclass")

терпит неудачу, потому что «ребенок» пытается позвонить__new__ метод, который он наследует от «родителя» вместо своего__init__ метод.

Что я должен изменить в «Ребенке»? получить ожидаемое поведение?

Посмотрите на связанные:stackoverflow.com/questions/674304/pythons-use-of-new-and-init charleyc

Ваш Ответ

3   ответа
4

Ребенок не звонит родителям__new__ вместо своего__init__он называет его собственным__new__ (наследуется от Parent)before __init__.

Вы должны понимать, для чего эти методы и когда Python будет их вызывать.__new__ вызывается, чтобы придумать объект экземпляра, а затем__init__ вызывается для инициализации атрибутов этого экземпляра. Python передает одинаковые аргументы обоим методам: аргументы передаются классу, чтобы начать процесс.

Итак, что вы делаете, когда наследуетесь от класса с__new__ это именно то, что вы делаете, когда наследуете любой другой метод, у которого родительская подпись отличается от той, которую вы хотите иметь. Вы должны переопределить__new__ получить аргументы и вызов ребенкаParent.__new__ с аргументами, которые он ожидает. Тогда вы переопределите__init__ полностью отдельно (хотя с тем же списком аргументов и той же необходимостью вызывать Parent__init__ со своим собственным списком ожидаемых аргументов).

Есть две потенциальные трудности, которые могут возникнуть на вашем пути, потому что__new__ для настройки процесса получения нового экземпляра. Нет причин, по которым он должен создаватьnew экземпляр (он может найти тот, который уже существует), и на самом деле ему даже не нужно возвращать экземпляр класса. Это может привести к следующим проблемам:

If __new__ returns an existing object (say, because Parent expects to be able to cache its instances), then you may find your __init__ being called on already existing objects, which can be very bad if your objects are supposed to have changeable state. But then, if Parent expects to be able to do this sort of thing it will probably expect a bunch of constraints on how it is used, and sub-classing it in ways that arbitrarily break those constraints (e.g. adding mutable state to a class that expects to be immutable so it can cache and recycle its instances) is almost certainly not going to work even if you can get your objects initialised correctly. If this sort of thing is going on, and there isn't any documentation telling you what you should be doing to subclass Parent, then the third-party probably doesn't intend you to be subclassing Parent, and things are going to be difficult for you. Your best bet though would probably be to try moving all of your initialisation to __new__ rather than __init__, ugly as that is.

Python only calls the __init__ method if the class' __new__ method actually returns an instance of the class. If Parent is using its __new__ method as some sort of "factory function" to return objects of other classes, then subclassing in a straightforward fashion is very likely to fail, unless all you need to do is change how the "factory" works.

1

Как я понимаю, когда звонишьChild("myarg", "otherarg"), это на самом деле означает что-то вроде этого:

c = Child.__new__(Child, "myarg", "otherarg")
if isinstance(c, Child):
    c.__init__("myarg", "otherarg")

Вы могли бы:

Write an alternative constructor, like Child.create_with_extra_arg("myarg", "otherarg"), which instantiates Child("otherarg") before doing whatever else it needs to.

Override Child.__new__, something like this:

.

def __new__(cls, myarg, argforsuperclass):
    c = Parent.__new__(cls, argforsuperclass)
    c.field = myarg
    return c

Я не проверял это. Переопределение__new__ может быстро запутаться, поэтому лучше избегать этого, если это возможно.

9

Во-первых, не рекомендуется переопределять__new__ именно для того, чтобы избежать этих проблем ... Но это не твоя вина, я знаю. Для таких случаев передовой опыт по переопределению__new__ чтобы заставить его принимать необязательные параметры ...

class Parent(object):
    def __new__(cls, value, *args, **kwargs):
        print 'my value is', value
        return object.__new__(cls, *args, **kwargs)

... чтобы дети могли получать свои собственные

class Child(Parent):
    def __init__(self, for_parent, my_stuff):
        self.my_stuff = my_stuff

Тогда это будет работать:

>>> c = Child(2, "Child name is Juju")
my value is 2
>>> c.my_stuff
'Child name is Juju'

Тем не менее, автор вашего родительского класса не был таким разумным и дал вам эту проблему:

class Parent(object):
    def __new__(cls, value):
        print 'my value is', value
        return object.__new__(cls)

В этом случае просто переопределить__new__ в дочернем элементе, заставляя его принимать необязательные параметры и вызывать родительский элемент__new__ там:

class Child(Parent):
    def __new__(cls, value, *args, **kwargs):
        return Parent.__new__(cls, value)
    def __init__(self, for_parent, my_stuff):
        self.my_stuff = my_stuff
Ребенок буквально платит за (кодирование) грехи Родителя.

Похожие вопросы