?

Log in

entries friends calendar profile Scribbles in the Dark Previous Previous Next Next
solve for X - Please Visit http://glyph.twistedmatrix.com/ - This Blog Is Closed.
Sorry about the ads, I can't turn them off.
glyf
glyf
solve for X

class A(object):
    def m(self, alpha):
        print 'A.m', alpha

class B(A):
    def m(self, alpha, beta):
        print 'B.m', alpha, beta
        super(B, self).m(alpha)

class C(B):
    def m(self, alpha, beta, gamma):
        print 'C.m', alpha, beta, gamma
        super(C, self).m(alpha, beta)

class D(B):
    def m(self, alpha, beta, gamma, delta):
        print 'D.m', alpha, beta, gamma, delta
        super(D, self).m(alpha, beta)

class E(C, D):
    def m(self, alpha, beta, gamma, delta, epsilon):
        print 'E.m', alpha, beta, gamma, delta, epsilon
        x = ???
        super(E, self).m(alpha, *x)

E().m(1, 2, 3, 4, 5)
14 comments or Leave a comment
Comments
From: jerub Date: April 7th, 2007 01:29 am (UTC) (Link)
Super Considered Useless.
From: mithrandi Date: April 8th, 2007 11:15 pm (UTC) (Link)
seems more like __init__ In Python Considered Sucktastic.
From: jerub Date: April 9th, 2007 12:08 am (UTC) (Link)
This isn't an __init__ method being considered.
From: mithrandi Date: April 9th, 2007 12:14 am (UTC) (Link)
Uhm, oops. In that case, this seems to be insanity; having a bunch of incompatible method signatures all in the same inheritance tree makes it rather impossible to sanely invoke all superclass versions, unless all the implementations cooperate. I'm not sure that this is a problem unique to Python, or to super(), but feel free to enlighten me.
glyf From: glyf Date: April 9th, 2007 03:38 pm (UTC) (Link)
__init__ creates confusion by the fact that object either does or doesn't define it, and either is or isn't permissive, according to http://python.org/sf/1683368. However, the point I was trying to make was mainly about __init__.
From: terrycojones Date: April 7th, 2007 04:34 am (UTC) (Link)

One way to do it

glyf From: glyf Date: April 9th, 2007 03:42 pm (UTC) (Link)

Re: One way to do it

I think that this is the only correct solution (james indicates something similar in his age-old post about super()) but it also has a problem: it's not cooperative with classes that don't use this style.

The point of this post is that I am slowly realizing that it is impossible to correctly write mixins in Python. You can't really re-define methods lower in the inheritance hierarchy, you can only overload them, so mixins can pretty much never define __init__. Except, they also can't not define __init__, because then they inherit object's __init__, whose behavior is uncertain and changing.

Your post indicates that Python does provide nice support for inheritance diamonds, as long as the root of the diamond isn't "object".
From: mesozoic Date: April 9th, 2007 04:49 pm (UTC) (Link)

Re: One way to do it

So doesn't that just mean you can write mixins, but you have to pass all arguments as a single keyword dict instead of as a tuple?
glyf From: glyf Date: April 9th, 2007 05:01 pm (UTC) (Link)

Re: One way to do it

A "mixin", almost by definition, shouldn't know anything about the properties of the classes it might be mixed in to. It might be necessary, practically speaking, to stipulate that it has to be mixed in to a new-style class or a classic class, since they are subtly incompatible inheritance models, but there should be a "right way" for either one, and I can't see it.
From: terrycojones Date: April 9th, 2007 08:30 pm (UTC) (Link)

Re: One way to do it

Hi Glyph.

This sort of thing is better done with a whiteboard and code and maybe beer. There were lots of other things I could have said in my original post (some are mentioned in my python-dev posts) about why what I sent is pretty flexible.

First of all, if classes don't use this style then yes you can't do anything except try to pass them their arguments as they're wanted (positional, keyword) and as they're named. OTOH, if you're using my dictionary style you can still pass args _through_ an non-cooperating class if it's polite enough to pass on its **kwargs. Also, you've very unlikely to break a non-cooperating class if you put something obscure like __margs__ into the keywords. I guess all this is obvious, and your general point is taken.

I'm not sure why you say it's impossible to write (implying you write the whol hierarchy, all classes cooperate) mixins. Why not describe something that you think can't be done and I'll have a go? I certainly feel like I can re-define lower methods (re-define = change signature?) and have things continue to work, and even that I can do that in isolation without informing the authors of subclasses that I'm doing it, or even knowing if any subclasses do or will exist. That's part of the attraction of what I posted. Of course if you make an incompatible signature change (as opposed to a signature change that makes an arg optional or adds an optional keyword arg) then people calling your class are going to have to adjust - there's no way around that.

Why can mixins pretty much never define __init__? You don't inherit object's __init__ unless you call it. I don't understand your final comment either. Is this because object's __init__ does (or will soon) raise an exception if its argument list isn't empty?

If that's what you're meaning, you can have cooperating classes along the chain del their args out of the dictionary. It's perfectly safe and reasonable for them to do that - in fact to be most safe they should pull out their args, del from the dictionary, then call their superclasses, then use their own args. This makes sure that a super class can't mess with your args. Hopefully this makes sense - I can give an example if not. So if all classes cooperate and del their key from the dict, then when you hit object's __init__ the dict is (almost) empty. It actually contains the single key (like __margs__) which is an empty dict... Maybe that's what you meant?

Thinking about this sort of thing was what lead me to say in the python-dev mailing that if python were changed to allow None as a key in keyword args, then all classes could stash their args in there and object's __init__ could verify that it had no positional args and that its **kwargs contained (at most) None, and that that was an empty dict. That would work, it would be backward compatible (since Python currently does not allow None as a keyword arg), but of course it requires changing Python itself. I can give an example if that's not clear (in summary, use None instead of __margs__ in my example here).

All this was mentioned briefly in my original posting.

This morning I was thinking (again) about trying to write a decorator to hide some of the boilerplate code involved in doing this stuff. But I'm also trying to avoid thinking about this stuff in general :-)

Terry
glyf From: glyf Date: April 10th, 2007 04:23 am (UTC) (Link)

Re: One way to do it

The key phrase I don't understand here is:
an non-cooperating class if it's polite enough to pass on its **kwargs

Non-cooperating classes are, almost by definition, impolite :).

The key feature of a "mixin", for me (as opposed to some other kind of inherited class which isn't a "mixin"), is that it doesn't really care about other parts of its inheritance hierarchy, or it is designed to implement only one superclass in a hierarchy of many. For example, I want to temporarily mix in a trace helper in a class to help me debug an object which subclasses things from two different libraries written in two different styles. I don't think that I can sanely do that with subclassing in python; metaclasses would seem to have a better chance of working, because then I can sidestep having an __init__ on my helper class entirely, and explicitly initialize it separately.
From: terrycojones Date: April 10th, 2007 11:41 am (UTC) (Link)

Re: One way to do it

> Non-cooperating classes are, almost by definition, impolite :)

Well, yes. I meant non-cooperating in the sense of non-aware. If a class is being "polite" (in some informal sense) it will pass on *args and **kwargs, and doesn't have to be aware of what may be passing by. If it's actively non-cooperating then you're clearly SOL.

I realized after my earlier posting that you can still have a chain of cooperating calls arrive at object.__init__ with no args. You just also need a "last one out turns off the lights" policy in your boilerplate. That's to say that if a class notes that after del'ing its own args that **kwargs has just the args dictionary in it, and that that's empty, it dels that too before calling super with the (now totally empty) **kwargs.

Anyway, I agree with you, the overall situation is ugly and there doesn't seem to be a general solution.
moshez From: moshez Date: April 7th, 2007 06:52 am (UTC) (Link)
Incompatibly changing method signatures is probably not a good idea.
glyf From: glyf Date: April 9th, 2007 03:43 pm (UTC) (Link)
Every time you define __init__ you are incompatibly changing its signature.
14 comments or Leave a comment