[Python-ideas] Eliminating special method lookup (was Re: Missing Core Feature: + - * / | & do not call __getattr__)

Steven D'Aprano steve at pearwood.info
Fri Dec 4 21:38:14 EST 2015


On Fri, Dec 04, 2015 at 12:02:46PM -0800, Andrew Barnert via Python-ideas wrote:

> So, maybe the way to get from here to there is to explicitly document 
> the methods CPython treats as magic methods, 

That's probably a good idea regardless of anything else.


> and allow only allow 
> other implementations to do the same for (a subset of) the same, and 
> people can gradually tackle and remove parts of that list as people 
> come up with ideas, and if the list eventually becomes empty (or gets 
> to the point where it's 2 rare things that aren't important enough to 
> keep extra complexity in the language), then the whole notion of 
> special method lookup can finally go away. 

Well, maybe. I haven't decided whether special method lookup is an 
annoying inconsistency or a feature. It seems to me that operators, at 
least, are somewhat different from method calls, and the special 
handling should be considered a deliberate design feature. My 
reasoning goes like this:

Take a method call:

    obj.spam(arg)

That's clearly called on the instance itself (obj), and syntactically we 
can see that the method call "belongs" to obj, and so semantically we 
should give obj a chance to use its own custom method before the method 
defined in the class.

(At least in languages like Python where this feature is supported. I 
believe that the "Design Patterns" world actually gives this idiom a 
name, and in Java you have to jump through flaming hoops to make it 
work, but I can never remember what it is called.)

So it makes sense that ordinary methods should be first looked up on the 
instance, then the class. But now take an operator call:

    obj + foo

(where, for simplicity, both obj and foo are instances of the same 
class). Syntactically, that's *not* clearly a call on a specific 
instance. There's no good reason to think that the + operator "belongs" 
to obj, or foo, or either of them.

We could nevertheless arbitrarily decide that the left hand instance obj 
gets first crack at this, and if it doesn't customise the call to 
__add__, the right hand instance foo is allowed to customise __radd__, 
and if that doesn't exist we go back to the class __add__ (and if that 
also fails to exist we try the class __radd__). That's okay, I guess, 
but the added complexity feels like it would be a bug magnet.

So *maybe* we should decide that the right design is to say that 
operators don't belong to either instance, they belong to the class, and 
neither obj nor foo get the chance to override __[r]add__ on a 
per-instance basis.

Similar reasoning applies to __getattr__ and other special methods. We 
can, I think, convince ourselves that they belong to the class, not the 
instance, in a way that ordinary methods are not.

We define ordinary methods in the class definition for convenience and 
efficency, but syntactically and semantically obj.spam looks up spam in 
the instance obj first, so obj has a chance to override any spam defined 
on the class. That's a good thing.

But syntactically, special dunder methods like __getattr__ and __add__ 
don't *clearly* belong to the instance, so maybe we should decide that 
it is a positive feature that they can't be overridden by the instance. 
By analogy, if we use an unbound method instead:

TheClass.spam(obj, arg)

then I think we would all agree that the per-instance obj.spam() method 
should *not* be called. It seems to me that operator methods "feel like" 
they are closer to unbound method calls than bound method calls. And 
likewise for __getattr__ etc.

As I said, I haven't decided myself which way I lean. If Python didn't 
already support per-instance method overriding, I would argue strongly 
that it should. But per-instance operator overloading? I'm undecided.


-- 
Steve


More information about the Python-ideas mailing list