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,
Given UFCS, that ought to return the global squared function, curried
with 3 as its first (and only) argument.
UFCS would be a pretty big design change to Python, but I don't think it
would be a *problem* as such. It just means that x.y, hasattr(x, y) etc.
would mean something different to what they currently mean.
which means that hasattr (which is defined by
attempting to get the attribute and seeing if an exception is thrown)
has to return True.
Yes. And this is a problem why?
Obviously it would mean that the semantics of hasattr will be different
than they are now, but it's still a coherent set of semantics.
In fact, one can already give a class a __getattr__ method which provides
UFCS functionality. (Hmmm, you need a way to get the caller's globals.
You know, this keeps coming up. I think it's high-time Python offered
this as a supported function.) That's no more a problem than any other
dynamically generated attribute.
Stick that __getattr__ in object itself, and UFCS is now language wide.
That would make an awesome hack for anyone wanting to experiment with
this!
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.
hasattr doesn't care about globals, nor does it need to. hasattr behaves
like the equivalent to:
def hasattr(obj, name):
try:
obj.name
except AttributeError:
return False
return True
give or take. And yes, if accessing your attribute has side effects,
using hasattr does too:
py> class Spam(object):
.... @property
.... def spam(self):
.... print("Spam spam spam spam LOVERLY SPAAAAM!!!!")
.... return "spam"
....
py> x = Spam()
py> hasattr(x, "spam")
Spam spam spam spam LOVERLY SPAAAAM!!!!
True
If that's a worry to you, you can try inspect.getattr_static.
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.
*cough*
class Example:
def __getattr__(self, name):
if name == 'module_name':
if __name__ == '__main__':
return "NOBODY expects the Spanish Inquisition!"
else:
return __name__
raise AttributeError("no attribute %r" % name)