Uniform Function Call Syntax (UFCS)

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun Jun 8 23:53:13 EDT 2014


On Mon, 09 Jun 2014 02:48:13 +1000, Chris Angelico wrote:

> 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)


:-)


-- 
Steven D'Aprano
http://import-that.dreamwidth.org/



More information about the Python-list mailing list