TL;DR

If people really object to & doing double-duty as bitwise-AND and 
chaining, there's always && as a possibility.


On Tue, Jun 27, 2017 at 12:47:40AM -0700, David Mertz wrote:

> > - keep the existing __and__ and __rand__ behaviour;
> > - if they are not defined, and both operands x, y are iterable,
> >   return chain(x, y);
> >
> 
> I understand.  But it invites confusion about just what the `&` operator
> will do for a given iterable.

With operator overloading, that's a risk for any operator, and true for 
everything except literals.

What will `x * y` do? How about `y * x`? They're not even guarenteed to 
call the same dunder method.

But this is a problem in theory far more than in practice. In practice, 
you know what types you are expecting, and if you don't get them, you 
either explicitly raise an exception, or wait for something to fail. 
"Consenting adults" applies, and we often put responsibility back on the 
caller to do the right thing.

If you expect to use & on iterable arguments, it is reasonable 
to put the responsibility on the caller to only provide "sensible" 
iterables, not something wacky like an infinite generator (unless your 
code is explicitly documented as able to handle them) or one that 
overrides __(r)and__. Or you could check for it yourself, if you don't 
trust the argument:

if hasattr(arg, '__and__') or hasattr(arg, '__rand__'):
    raise Something

But that strikes me as overkill. You don't normally check for dunders 
before using an operator, and we already have operators that can return 
different types depending on the operands:

% can mean modulo division or string interpolation
* can mean sequence repetition or numeric multiplication
+ can mean numeric addition or sequence concatenation

Why is 

& can mean ierable chaining or bitwise-AND

uniquely confusing?


> For NumPy itself, you don't really want to
> spell `chain(a, b)` so much.  But you CAN, they are iterables.  The
> idiomatic way is:
> 
>     >>> np.concat((a,b))
>     array([1, 2, 3, 4, 5, 6])

That's not really chaining, per say -- it is concatenating two arrays to 
create a third array. (Probably by copying the array elements.)

If you wanted to chain a numpy array, you would either use 
itertools.chain directly, or call iter(myarray) before using the & 
operator.


> However, for "any old iterable" it feels very strange to need to inspect
> the .__and__() and .__rand__ ()  methods of the things on both sides before
> you know WHAT operator it is.

Do you inspect the dunder methods of objects before calling + or * or & 
currently? Why would you need to start now?

Since there's no way of peering into an object's dunder methods and 
seeing what they do (short of reading the source code), you always have 
an element of trust and hope whenever you call an operator on anything 
but a literal.


> Maybe if you are confident a and b are exactly NumPy arrays it is obvious,
> but what about:
> 
>     from some_array_library import a
>     from other_array_library import b
> 
> What do you think `a & b` will do under your proposal? Yes, I understand
> it's deterministic... but it's far from *obvious*.

It's not "obvious" now, since a and b can do anything they like in their 
dunder methods. They could even inspect the call stack from __next__ and 
if they see "chain" in the stack, erase your hard drive. Does that mean 
we don't dare call chain(a, b)?

I don't think this proposal adds any new risk. All the risk already 
exists as soon as you allow method overloading. All we're doing is 
saying is "try the method overloading first, and if that isn't 
supported, try iterable chaining second before raising TypeError". 



> This isn't even doing
> something pathological like defining both `.__iter__()` and `.__and__()` on
> the same class... which honestly, isn't even all that pathological; I can
> imagine real-world use cases.

Indeed -- numpy arrays probably do that, as do sets and frozensets. I 
didn't say it was pathological, I said it was uncommon.


> I think that chaining iterators is common enough and important enough in
> > Python 3 to deserve an operator. While lists are still important, a lot
> > of things which were lists are now lazily generated iterators, and we
> > often need to concatenate them. itertools.chain() is less convenient
> > than it should be.
> >
> 
> I actually completely agree! I just wish I could think of a good character
> that doesn't have some very different meaning in other well-known contexts
> (even among iterables).

There's always && for iterator chaining.



-- 
Steve
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to