On May 9, 2020, at 19:43, Christopher Barker <python...@gmail.com> wrote:
> 
> On Sat, May 9, 2020 at 1:03 PM Andrew Barnert <abarn...@yahoo.com> wrote:
> > https://github.com/PythonCHB/islice-pep/blob/master/pep-xxx-islice.rst
> 
> I haven’t read the whole thing yet, but one thing immediately jumped out at 
> me:
> 
> > and methods on containers, such as dict.keys return iterators in Python 3, 
> 
> No they don’t. They return views—objects that are collections in their own 
> right (in particular, they’re not one-shot; they can be iterated over and 
> over) but just delegate to another object rather than storing the data.
> 
> Thanks -- that's that kind of thing that led me to say that this is probably 
> not ready for a PEP.
> 
> but I don't think that invalidates the idea at all -- there is debate about 
> what an "islice" should return, but an iterable view would be a good option.

I don’t think it invalidates the basic idea at all, just that it suggests the 
design should be different.

Originally, dict returned lists for keys, values, and items. In 2.2, iterator 
variants were added. In 3.0, the list and iterator variants were both replaced 
with view versions, which were enough of an improvement that they were 
backported to 2.x. Because a view does cover almost all of the uses of both a 
sequence copy and an iterator. And I think the same is true here.

> I'm inclined to think that it would be a bad idea to have it return a full 
> sequence view object, and not sure it should do anything other than be 
> iterable.

Why? What’s the downside to being able to do more with them for the same 
performance cost and only a little more up-front design work?

> > And this is important here, because a view is what you ideally _want_. The 
> > reason range, key view, etc. are views rather than iterators isn’t that 
> > it’s easier to implement or explain or anything, it’s that it’s a little 
> > harder to implement and explain but so much more useful that it’s worth it. 
> > It’s something people take advantage of all the time in real code.
> 
> Maybe -- but "all the time?" I'd vernture to say that absolutiely the most 
> comon thing done with, e.g. dict.keys() is to iterate over it.

Really? When I just want to iterate over a dict’s keys, I iterate the dict 
itself. 

> > For prior art specifically on slicing as a view, rather than just views in 
> > general, see memoryview (which only works on buffers, not all sequences) 
> > and NumPy (which is weird in many ways, but people rely on slicing giving 
> > you a storage-sharing view)
> 
> I am a long-time numpy user, and yes, I very much take advantage of the 
> memory sharing view.
> 
> But I do not think that that would be a good idea for the standard libary. 
> numpy slices return a full-fledged numpy array, which shares a data view with 
> the it's "host" -- this is really helpful for performance reasons -- moving 
> large blocks of data around is expensive, but it's also pretty confusing. And 
> it would be a lot more problematic with, e.g. lists, as the underlying buffer 
> can be reallocated -- numpy arrays are mutable, but not re-sizable, once 
> you've made one its data buffer does not change.

That’s no more of a problem for a list slice view than for any of the existing 
views. The simplest way to implement a view is to keep a reference to the 
underlying object and delegate to it, which is effectively what the dict views 
do.

(Well, did from 2.x to 3.5. The dict improvements in 3.6 opened up an 
optimization opportunity, because in the split layout a dict is effectively a 
wrapper around a keys view and a separate table, so the keys view can refer 
directly to that thing that already exists. But that isn’t relevant here.)

(You _could_ instead refuse to allow expanding a sequence when there’s a live 
view, as bytearray does with memoryview, but I don’t think that’s necessary 
here. It’s only needed there a consequence of the fact that the buffer protocol 
is provided in C rather than in Python. For a slice view, it would just make 
things more complicated and less functional for no good reason.)

> > But just replacing islice is a much simpler task (mainly because the input 
> > has to be a sequence and the output is always a sequence, so the only 
> > complexity that arises is whether you want to allow mutable views into 
> > mutable sequences), and it may well be useful on its own.
> 
> Agreed. And while yes, dict_keys and friends are not JUST iterartors, they 
> also aren't very functional views, either. They are not sequences, 

That’s not true. They are very functional—as functional as reasonably makes 
sense. The only reason they’re not Sequences is that they’re views on dicts, so 
indexing makes little sense, but set operations do—and they are in fact Sets. 
(Except for values.)

> certainly not mutabe sequences.

Well, yes, but mutating a dict through its views wouldn’t make sense in the 
first place:

    >>> d = {1: 2}
    >>> k = dict.keys()
    >>> k |= 3

You’ve told it to add an item with key 3 without telling it what the value is, 
and there’s no reasonable thing that could mean. A slice view would have no 
such problem, so mutation is sensible.

That being said, mutation could easily be added later without breaking 
anything, and it does raise some nontrivial design issues (most obviously, 
notice that my implementation only allows non-size-changing mutations, because 
otherwise you have to decide whether it remains a view over seq[3:5] or becomes 
a view over seq[3:6]; all three options seem reasonable there, so I just went 
with the simplest, and have no good argument for why it’s the best…). So I 
think it might be better to leave mutation out of the original version anyway 
unless someone has a need to it (at which point we can use the examples to 
think through the best answers to the design issues).

> And:
> 
> > (in particular, they’re not one-shot; they can be iterated over and over) 
> 
> yes, but they are only a single iterator -- if you call iter() on one you 
> always get the same one back, and it's state is preserved.

No, that’s not true. Each call to iter() returns a completely independent 
iterator each time, with its own independent state that starts at the head of 
the view. It works exactly the same way as a set, a tuple, or any other normal 
collection:

    >>> d = {1: 2, 3: 4, 5: 6
    >>> k = d.keys()
    >>> i1 = iter(k)
    >>> next(i1)
    1
    >>> i2 = iter(k)
    >>> next(i2)
    1
    >>> list(i1)
    [3, 5]
    >>> next(i2)
    3

(This was a bit harder to see, and to explain, before 3.6, because that order 
was intentionally arbitrary, but it was guaranteed to be consistent until you 
mutated the dict.)

Also notice, while the views’ iterators are just like dict iterators, and list 
iterators for that matter, in that they can’t handle the dict being resized 
during iteration, the views themselves have no such trouble:

    >>> d[7] = 8
    >>> next(i1)
    RuntimeError: dictionary changed size during iteration
    >>> i3 = iter(k)
    >>> next(i3)
    1

Basically, views are not like iterators at all, except in that they save time 
and space by being lazy.

> So yes, you can iterate over more than once, but iter() only resets after 
> it's been exhausted before.

Such a resettable-iterator thing (which would have some precedent in file 
objects, I suppose) would actually be harder to Implement, on top of being less 
powerful and potentially confusing. And the same is true for slices.

> In short -- not having thought about it deeply at all, but I'm thinking that 
> making an SliceIterator very similar to dict_keys and friends would make a 
> lot of sense.

Yes, as long that means being a full-featured normal collection (in this case a 
Sequence rather than a Set), not a resettable iterator.

_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/POCUH7IXXY4HB6GJ2KZVBXV3AF4AUMA6/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to