>>>>> "G" == Guido van Rossum <[EMAIL PROTECTED]> writes:
G> There are different philosophies about the correct style for G> cooperative super calls. G> The submitter of the bug report likes to remove "consumed" arguments G> and pass the others on, having something at the root that complains G> about any unused arguments. It has the problem that you mention: if G> multiple classes might be interested in the *same* argument they G> won't see it. The other style is to pass *all* arguments down, and G> let everyone cherry-pick them. The last call then just throws them G> away. This has the problem that misspelled arguments are silently G> ignored rather than being diagnosed at the point where you can do G> something about it. G> I don't know what the "best practice" is (like Greg Ewing, I don't G> use either style myself) but I've got a feeling that it must be G> easier to solve the former problem than the latter (also I don't G> know that the former actually occurs in practice). I'm following up to the above, as I'm not sure where the best place to jump into this discussion is. I think the problem is more general that just consuming and/or passing on. In the typical style of passing args to super classes that I've seen, __init__ methods in the call chain also don't really even know *whether* they should use an argument. There is also the problem of argument change over time. E.g., suppose I'm the writer of a class X and, unbeknownst to me, someone writes class Y(X,Z), where Z is some other class. Suppose Z's __init__ expects an argument called z. If I one day add an argument called z, that has totally different semantics, things get really hairy. I'm the writer of X, I'm unaware of classes Y and Z, and if someone passes me an z argument then naturally I want to (try to) use it. There are other examples like this, and argument name clashes can be more specific (like 'limit' or 'debug'). In general, we're faced with a situation that is also both time-sensitive and semantics-sensitive. Class writers shouldn't need to know all future argument changes to other classes potentially upstream or downstream from them in the call chain. Given an unstructured mess of arguments, you don't know what to consume, what to ignore, what to pass on, etc., and you don't know the future. Although this sounds much more difficult than simple diamond inheritance, I think there's an easy way to solve it. When writing a class, you know 1) what arguments your __init__ method wants, and 2) what arguments your superclass(es) __init__ methods want. You know the second thing from reading the API/docs of your superclasses - that's why you're calling them. You want to be able, for example, to route a 'limit' argument to one of your super classes and another different 'limit' argument to another of your super classes. You want this to work even if one or more of your superclasses (now or in the future) in turn decides to call some other super class that also has a 'limit' argument with entirely different (or the same) semantics. Etc. An easy solution is for __init__ to receive and pass a dictionary whose keys are class names and whose values are dictionaries of arguments intended for that class. That way, an __init__ method in a class knows exactly which arguments are intended for it. It can detect extra args, missing args, misspelt args, etc. It can also prepare args for its known immediate superclasses (those from which it subclasses) - and it does know these classes as it is explicitly subclassing from them. It can leave arguments that are intended for the __init__ methods in other classes alone. It can create different arguments of the same name for its different superclasses. It can change its signature (adding and dropping args) without affecting the args passed to its superclass. Classes earlier in the call chain can do the same without affecting the args received by this class. It is also immune to differences in superclass name ordering by subclasses (i.e., a subclass of X and Y should be able to be class Z(X,Y) or class(Y,Z) without screwing up the args received by either X.__init__ or Y.__init__. An __init__ could also safely del its own arguments once it has extracted/used them. In some odd cases it might like to pass them along, perhaps even adding something (like a None key to its sub-dictionary) to indicate that it has already been called (yes, this sounds like weird - I'm just mentioning that it would be possible). There's also no need for any special top-level subclasses of object that simply ignore all their arguments (though you _could_ add one if you wanted to be strict and insist that all __init__ methods del their args). This approach adheres nicely to the explicit is better than implicit maxim. I think the implicit situation is unsolvable. I guess this could all be dressed up nicely using a metaclass that pulled out any available args, made them available to the __init__ as regular keyword args, and made available the special dictionary that holds arguments for later classes (any given __init__ may want to add/alter this). I don't know enough about metaclasses to know if this could be done completely cleanly though. I'd probably prefer it all to be explicit and manual. Sorry for such a long post without much code. I can write some example classes if the above is unclear or people think this worth looking at further. Terry _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com