Thanks for the extensive feedback.  Here's my thoughts on how to address these 

On Saturday, 7 June 2014 20:20:48 UTC+1, Ian  wrote:
> It's a nice feature in a statically typed language, but I'm not sure
> how well it would work in a language as dynamic as Python.  There are
> some questions that would need to be addressed.
> 1) Where should the function (or perhaps callable) be looked for?  The
> most obvious place is the global scope.  I think it would be a bit too
> far-reaching and inconsistent with other language features to reach
> directly inside imported modules (not to mention that it could easily
> get to be far too slow in a module with lots of imports). As a result
> it would have to be imported using the "from module import function"
> syntax, rather than the somewhat cleaner "import module" syntax.
> While there's nothing wrong with such imports, I'm not sure I like the
> thought of the language encouraging them any more than necessary.

It would only work on functions in scope. x.len() would only work if len(x) 
would work.  I actually think this would work better in Python than in D.  In 
D, "import module;" imports all the symbols from the module, so it is easier to 
invoke a function unexpectedly.  In Python, "import module" does not fill the 
namespace with lots of callable symbols, so UFCS would generally work with 
built-ins, local functions, or functions explicitly imported with "from module 
import...".   In this case, the need to use the "from module import fname" form 
can document that something unusual is happening.

> 2) What about getattr and hasattr?  If I call hasattr(x,
> "some_method"), and x has no such attribute, but there is a function
> in the global scope named "some_method", should it return True?  

> If we instead have hasattr return False though, and have getattr raise 
> an exception, then we have this very magical and confusing
> circumstance where getattr(x, 'method') raises an exception but
> x.method does not.  So I don't think that's really a good scenario
> either.

AS you suggest, the preferable route is that hasattr should return False.  The 
object clearly does not have that attribute.  It is a property of the current 
module that the object can use "instance.fname".  While the behaviour that 
hasattr("fname") returns False, but instance.fname works is an exception, and a 
function could be added to test this quickly, so new code that cares could use:
if hasattr(instance, "fname") or inscopecallable('fname'):

The bigger problem I find is reading other code that uses UFCS and not 
realising that a "method" is not actually a method of the class, but requires 
importing a module.  That can cause confusion when trying to use it in your own 
code.  However, the need to use "from module import fname" would at least link 
the method name and the module.

> Also the idea makes me nervous in the thought that an incorrect
> attribute access could accidentally and somewhat randomly pick up some
> object from the environment.  

As before, I think the limited number of strange callable objects in most 
modules in Python protects against this.  Of course, "from module import *" 
might cause problems, but that is already true.  You need to be extra careful 
doing this, and should only do it for modules when you have a reasonable 
understanding of their exported names.

> But if you want to experiment with the idea, here's a (lightly tested)
> mixin that implements the behavior:

Thanks for the headstart! I'll need to read up on descriptors to understand 
that last bit fully (when a function has a __get__ method).

One problem with your untested code, the superclasses would need to be checked 
before using UFCS, so the structure is:

    return super().__getattr__(attr)
except AttributeError:
    # resolve using UFCS

Reply via email to