On Mon, Jun 9, 2014 at 1:39 AM, jongiddy <jongi...@gmail.com> wrote: > e.g. I could define: > > def squared(x): > return x * x > > i = 3 > i.squared() => 9 > > j = AClassThatImplements__mul__() > j.squared() => whatever j * j returns > > but also: > class AnotherClass: > def __mul__(self, other): > ... > def squared(self): > return specialised_method_for_calculating_squares() > > k = AnotherClass() > k.squared() => calls method, not function > > In this case, there is a problem with letting hasattr('squared') return True > for these first two instances. See Ian's post for a description of the > problem.

## Advertising

class Circle: def squared(self): raise NotImplementedError("Proven impossible in 1882") The trouble is that logically Circle does have a 'squared' attribute, while 3 doesn't; and yet Python guarantees this: foo.squared() # is equivalent [1] to func = foo.squared func() Which means that for (3).squared() to be 9, it has to be possible to evaluate (3).squared, which means that hasattr (which is defined by attempting to get the attribute and seeing if an exception is thrown) has to return True. Except that it's even more complicated than that, because hasattr wasn't defined in your module, so it has a different set of globals. In fact, this would mean that hasattr would become quite useless. (Hmm, PEP 463 might become a prerequisite of your proposal...) It also means that attribute lookup becomes extremely surprising any time the globals change; currently, "x.y" means exactly the same thing for any given object x and attribute y, no matter where you do it. The only way I can think of for all this to make sense is actually doing it the other way around. Instead of having x.y() fall back on y(x), have y(x) attempt x.y() first. To pull this off, you'd need a special bouncer around every global or builtin... which may be tricky. class MagicDict(dict): def __getitem__(self, item): # If this throws, let the exception propagate obj = super().__getitem__(item) if not callable(obj): return obj def bouncer(*a, **kw): if len(a)==1 and not kw: try: return getattr(a[0], item)() except AttributeError: pass return obj(*a, **kw) return bouncer import __main__ # Except that this bit doesn't work. __main__.__dict__ = MagicDict(__main__.__dict__) It's theoretically possible, along these lines, I think. Whether it's actually any good or not is another question, though! ChrisA [1] Modulo performance. CPython, AFAIK, does this exactly as written, but other Pythons may and do optimize the actual "foo.squared()" form to reduce heap usage. But in terms of visible effects, equivalent. -- https://mail.python.org/mailman/listinfo/python-list