2

I was wondering if there is generic inheritance in python.

For example,

Class A(object):
  def foo():

Class B(object):
  def foo():


Class C(<someParentClass>):
  def bar():

so effectively, I would like to do something like

  myClass1 = C()<A>
  myClass2 = C()<B>

Im guessing this is not possible in python, but is there any other way to have a similar effect?

Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
Karan
  • 14,824
  • 24
  • 91
  • 157
  • 4
    Python doesn't even have generics. Generics only make sense when there are static types and you don't want to write the same code for several types. Python code always accepts any objects (although it can and will error later if the given objects aren't usable). What are you really trying to do? –  Jun 11 '11 at 12:35
  • 1
    Sounds like you should read about "mix-in" classes: http://en.wikipedia.org/wiki/Mixin – Jim Dennis Jun 11 '11 at 12:53

2 Answers2

10

There's nothing preventing it. Everything in Python is essentially generic. Everything is runtime, including class statements, so you can do something like:

def make_C(parent):
    class C(parent):
        def bar(self):
            ...
    return C

myClass1 = make_C(A)
myClass2 = make_C(B)

If you want the C class to be a little more descriptive in name or documentation, you can assign the __name__ and __doc__ attributes, or use the three-argument form of type() instead of the class statement to create it.

Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
  • Note, though, while nothing prevents a person from doing something like this, it isn't normal practice for Python. There are better, clearer, more Pythonic solutions to this issue. Generics are really a solution to a problem that doesn't exist in Python. Consider that in Python you can test your objects for the "foo" function easily and call it with the expected arguments regardless of parent classes. – yam655 Jun 11 '11 at 20:24
  • I agree that generics are not a meaningful thing in Python, but creating a class in a function like this does have its uses. It's just a solution to a different kind of problem :) – Thomas Wouters Jun 11 '11 at 20:27
2

You could derive C from object and use

class MyClass1(A, C):
    pass
class MyClass2(B, C):
    pass

There are many ways achieving the exact effect you described, but I think defining C as a mix-in class is the most idiomatic approach.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • If the intent is for C to specialize `A` and `B`, you should inherit from `C` first (or it won't be able to override methods defined in `A` or `B`. – Thomas Wouters Jun 11 '11 at 12:34
  • @Thomas: I interpreted the OP's intent in the way that `C` extends `A` and `B`. In his example, it does not overwrite any methods. – Sven Marnach Jun 11 '11 at 12:39
  • Yes, so did I. And I'm saying "you should inherit from C, or you will get different behaviour". If `C` extends `A` and `B`, it should come *before* `A` and `B` in the hierarchy. – Thomas Wouters Jun 11 '11 at 12:40
  • @Thomas: Depends on the semantics you want. I tend to put the actual base class first and the mix-ins later. I usually don't want mix-ins to overwrite methods, and I definitely want the derived class to use the metaclass of the actual base class, not the one of some mix-in. – Sven Marnach Jun 11 '11 at 12:59
  • But if `C` is a *mixin*, it's not a *subclass*. It's different behaviour than the OP seems to want. It's not *extending* `A` or `B`. As for the metaclass, that's not how it works. The metaclass is determined by *all* the baseclasses; the most specialized metaclass is used (with a specialcase for classic classes, which always lose to a new-style class.) If the baseclasses have conflicting metaclasses (not counting classic classes), it'll be an error. – Thomas Wouters Jun 11 '11 at 13:39
  • @Thomas: Thanks for resolving my misconceptions about metaclasses -- I must have got [the documentation](http://docs.python.org/reference/datamodel.html#__metaclass__) wrong some time back. Is there any official documentation explaining this behaviour? -- As to what the OP wants: we just don't seem to know. I don't think you are wrong, I just think there are multiple valid interpretations. – Sven Marnach Jun 11 '11 at 14:07
  • I don't think there's proper documentation on the details of metaclasses, unfortunately. Some of it is covered in PEPs 252 and 253, some of it in http://www.python.org/2.2.3/descrintro.html, but most of it's only obvious from experimentation or the source (and this particular part of the CPython source is *not* obvious :) As for the OP's intention -- I'm not even arguing about that, I was just pointing out the difference, and then later pointing out that your use of the word 'extending' does not accurately describe what your code actually does :) – Thomas Wouters Jun 11 '11 at 14:33
  • @Thomas: Thanks for the pointers! Helped me to find where I got the assumption that the first base is used to determine the metaclass from: [PEP 253](http://www.python.org/dev/peps/pep-0253/), search for "first base". The PEP states that only the constructor of the first base class is called, and that it is up to this constructor to decide which metaclass to return. According to the PEP, `type.__new__()` implements the behaviour you stated. But from experimentation it seems the PEP is wrong and you are right -- I did not look at the source code yet. – Sven Marnach Jun 11 '11 at 15:21
  • @Thomas: I made a mistake in my experiments. The PEP is right. I also confirmed this by reading the source. – Sven Marnach Jun 11 '11 at 15:49
  • @Thomas: No, it doesn't. None of your metaclasses overwrite `type.__new__()`, and `type.__new__()` indeed implements the behaviour you stated (see above). Also, see the [relevant lines](http://paste.pound-python.org/show/8001/) from the current development version of `bltinmodule.c`. – Sven Marnach Jun 11 '11 at 16:00
  • I'm not sure how you think that changes what I said, sorry. The *mechanics* are such that type.__new__ will figure out which class to actually instantiate, but it still takes *all* baseclasses' metaclasses into account. As far as I can tell the only way to create a class the way you expect (ignoring the metaclasses of anything but the first baseclass) is by creating the class *without* those bases in a custom metaclass's `__new__`, and then re-assigning the real list of bases to the class's `__bases__`... which seems like it *should* be an error (and it may well be, in other implementations :) – Thomas Wouters Jun 11 '11 at 18:06
  • @Thomas: I don't think we disagree in any way. I'm just saying Python behaves exactly as specified in PEP 253, and your tests don't say otherwise. (You can [make up silly examples](http://paste.pound-python.org/show/8003/) which the order of base classes matters for, but there probably aren't useful examples.) – Sven Marnach Jun 11 '11 at 19:13