Allow callables in slices

Such as that: def starting_when(element): ... a_list[starting_when:] Is equivalent to: from itertools import dropwhile def starting_when(element): ... list(dropwhile(lambda x: not starting_when(x), a_list)) And def ending_when(element: ... a_list[:ending_when] To: from itertools import dropwhile def ending_when(element: ... list(itertools.takwhile(lambda x: not condition(x), a_list))

On Sat, Jun 09, 2018 at 11:17:05AM +0200, Michel Desmoulin wrote:
That looks like a slice from an index to the end of the list. Things which are similar should look similar, but things which are different should NOT look similar. What would: alist[callable:callable:callable] do? How about this one? alist[7:callable:-1] If there are not meaningful interpretations of callables as part of general slice notation, then we shouldn't use slice notation as a shortcut for dropwhile. Rather than give this syntactic support, I'd rather add a new function to itertools that composes takewhile and dropwhile: def between(iterable, startcondition, endcondition): it = iter(iterable) return takewhile(lambda x: not endcondition(x), dropwhile(lambda x: not startcondition(x), it) ) If the caller wants to convert to a list (or some other sequence), they can, otherwise they can keep it as an iterator. -- Steve

Le 09/06/2018 à 11:47, Steven D'Aprano a écrit :
The semantic is [start_condition:stop_condition:step]. Here condition can be an index or something more complex. Just like you can do dictionary[key], but key can be a complex object with a custom __hash__ executing weird computation. Just like you can sorted() on natural values or pass a callable as a key. Just like you can re.replace() with a string or a function.
ValueError("Step cannot be used when callables are part of a slice") However: alist[7:callable] Would be: list(dropwhile(lambda x: not starting_when(x), islice(alist, 7, None))) Example, open this files, load all lines in memory, skip the first line, then get all the line until the first comment: import itertools def is_commented(line): return lines.startwith('#') def lines(): with open('/etc/fstab'): lines = f.readlines()[1:] return list(itertools.dropwhile(lines, is_commented) Becomes: def is_commented(line): return lines.startwith('#') def lines(): with open('/etc/fstab'): return f.readlines()[1:is_commented] It's not about how much shorter is is, but it is very nice to read. Of course I'd prefer to have slicing on generators, since here we load the entire file in memory. But I already suggested it several times on python-idea and the dictator killed it. For files like fstab it's ok since they are small.
It's not syntaxic support. You can already pass callables and the interpreter accept it fine. But the underlying types raise TypeError because they don't know how to use that. Just like you can pass tuples, but CPython can't use them, while numpy can.
Or accept callables in islice.

On Sat, Jun 09, 2018 at 11:17:05AM +0200, Michel Desmoulin wrote:
That looks like a slice from an index to the end of the list. Things which are similar should look similar, but things which are different should NOT look similar. What would: alist[callable:callable:callable] do? How about this one? alist[7:callable:-1] If there are not meaningful interpretations of callables as part of general slice notation, then we shouldn't use slice notation as a shortcut for dropwhile. Rather than give this syntactic support, I'd rather add a new function to itertools that composes takewhile and dropwhile: def between(iterable, startcondition, endcondition): it = iter(iterable) return takewhile(lambda x: not endcondition(x), dropwhile(lambda x: not startcondition(x), it) ) If the caller wants to convert to a list (or some other sequence), they can, otherwise they can keep it as an iterator. -- Steve

Le 09/06/2018 à 11:47, Steven D'Aprano a écrit :
The semantic is [start_condition:stop_condition:step]. Here condition can be an index or something more complex. Just like you can do dictionary[key], but key can be a complex object with a custom __hash__ executing weird computation. Just like you can sorted() on natural values or pass a callable as a key. Just like you can re.replace() with a string or a function.
ValueError("Step cannot be used when callables are part of a slice") However: alist[7:callable] Would be: list(dropwhile(lambda x: not starting_when(x), islice(alist, 7, None))) Example, open this files, load all lines in memory, skip the first line, then get all the line until the first comment: import itertools def is_commented(line): return lines.startwith('#') def lines(): with open('/etc/fstab'): lines = f.readlines()[1:] return list(itertools.dropwhile(lines, is_commented) Becomes: def is_commented(line): return lines.startwith('#') def lines(): with open('/etc/fstab'): return f.readlines()[1:is_commented] It's not about how much shorter is is, but it is very nice to read. Of course I'd prefer to have slicing on generators, since here we load the entire file in memory. But I already suggested it several times on python-idea and the dictator killed it. For files like fstab it's ok since they are small.
It's not syntaxic support. You can already pass callables and the interpreter accept it fine. But the underlying types raise TypeError because they don't know how to use that. Just like you can pass tuples, but CPython can't use them, while numpy can.
Or accept callables in islice.
participants (3)
-
Michael Selik
-
Michel Desmoulin
-
Steven D'Aprano