Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return a subclass from __new__ parameter

I have a class parent and two subclasses child1(parent) and child2(parent) sort of like the following near code. (edited to more properly show that the parent class is doing something)

class parent(object):
  name = None

  def __init__(self,e):
    # process the common attributes
    name = e.attrib['name']

  def __new__(cls,e):
    if e.attrib['type'] == 'c1':
      return child1(e)
    elif e.attrib['type'] == 'c2':
      return child2(e)
    else:
      raise 

class child1(parent):
  extra1 = None
  def __init__(self,e):
    super(e)
    # set attributes from e that are specific to type c1

class child2(parent):
  extra2 = None
  def __init__(self,e):
    super(e)
    # set attributes from e that are specific to type c2

The goal is to be able to get the "right" class based on the value of the parameter. So if I can say obj = parent(element) and obj will be either child1 or child2 depending on what the value of element.attrib['type'] is.

like image 771
ctjctj2 Avatar asked Feb 16 '23 14:02

ctjctj2


1 Answers

The problem is that inside parent.__new__, you're calling child1(e), while calls child1.__new__, which finds the implementation in parent.__new__ and calls it with the same e, which calls child1(e), which… so you get infinite recursion.

There are better ways to design this, but if you just want to fix your design, there are three options:


If you define __new__ in all of your subclasses, it won't fall through the parent.__new__. You can do this in a single step by interposing an intermediate class between parent and childN, so you only need intermediate.__new__. Or use a mixin that they all inherit, or …


Get rid of the inheritance. Is there really any reason child1 is-a parent here?

You seem to be looking for what in Smalltalk/ObjC terms is called a "class cluster", and you don't need the "visible face" of the cluster to be the base class in Python any more than you do in those languages.

For example:

class base(object):
    pass

class parent(base):
    def __new__(cls, e):
        # same as before

class child1(base):
    # etc.

In Python, you can even make parent an ABC, and register each childN with it so you can use isinstance and friends with it.


Finally, you can just catch the recursion by only handling __new__ on parent, not its subclasses:

def __new__(cls, e):
    if cls is not parent:
        return super(parent, cls).__new__(cls)
like image 51
abarnert Avatar answered Feb 28 '23 14:02

abarnert