[Python-ideas] Allow popping of slices
Steven D'Aprano
steve at pearwood.info
Mon Jun 4 20:11:57 EDT 2018
On Mon, Jun 04, 2018 at 03:23:07PM -0700, Ben Rudiak-Gould wrote:
> The `pop` method of built-in sequences is basically an atomic version of
>
> val = self[pos]
> del self[pos]
> return val
Aside from the atomicness, for testing we can subclass list:
# Untested
class MyList(list):
def pop(self, pos):
if isinstance(pos, slice):
temp = self[pos]
del self[pos]
return temp
return super().pop(pos)
Is that what you have in mind?
> If this behavior was extended to the case where `pos` is a slice, you
> could write things like:
>
> def cut_deck(deck, pos):
> deck.extend(deck.pop(slice(0, pos)))
I'm not sure that's an advantage over:
deck[:] = deck[pos:] + deck[:pos]
but I suppose one might be faster or slower than the other. But I think
the version with slices is much more clear.
> def bfs(roots):
> depth, frontier = 0, list(roots)
> while frontier:
> depth += 1
> for item in frontier.pop(slice(None)):
> ...
> frontier.append(...)
> ...
If that's a breadth-first search, I've never seen it written like that
before. The classic bfs algorithm is at Wikipedia (conveniently written
in Python):
https://en.wikipedia.org/wiki/Breadth-first_search#Pseudocode
and yours is very different. I'm not saying yours is wrong, but its not
obviously right either.
It might help your argument if you show equivalent (but working) code
that doesn't rely on popping a slice.
> Similar functionality is found in many other languages (e.g. Perl and
> JavaScript's `splice`).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Do you have a link for the Perl version?
> I think it's useful not just because it's more
> concise, but because it's linear/reversible: it moves data rather than
> duplicating and then destroying it, which makes it less prone to bugs.
I don't see how that is possible. You still have to move the data out of
the sequence into a new sequence before deleting it from the original.
Maybe there are optimizations if the slice is at the end of the
sequence, but in general you have to make a copy of the items.
(Well, not the items themselves, just the references to them.)
> The syntax is a bit odd since you have to construct the slice by hand.
> Here are three solutions for that from least to most extravagant:
>
> 1. Don't worry about it. It's still useful, and the syntax, though
> verbose, makes sense. (The "reference implementation" of pop is
> literally unchanged.)
Indeed. If this functionality can be justified, we could start with
this, and think about a neater syntax later (if required).
> 2. Give pop methods a __getitem__ that does the same thing as
> __call__, so you can write xs.pop[-1] or xs.pop[:].
That's interesting.
But it would mean that pop, and only pop, would allow pop(n) and pop[n]
to be the same thing. That's going to confuse people who wonder why they
can't call other methods like that:
mylist.pop[0] # okay
mylist.append[item] # fails
and why they can't use slice syntax in the round-bracket call syntax:
mylist.pop[1:-1] # okay
mylist.pop(1:-1) # not okay
While I'm intrigued by this, I think it will be too confusing.
> 3. Promote del statements to expressions that return the same values
> as the underlying __delitem__, __delattr__, etc., and make those
> methods of built-in types return the thing that was deleted. (Or
> introduce __popitem__, __popattr__, etc. which return a value.)
I don't get how this allows us to pass slices to pop.
You missed one, allow slice literals:
https://mail.python.org/pipermail/python-ideas/2015-June/034086.html
--
Steve
More information about the Python-ideas
mailing list