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
[email protected]
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/