For sake of completeness, here is another possible problem I've found with it.
I was afraid of making something context-dependent, and therefore breaking its consistency. Here is the use of slices in the current language that breaks my rules: my_array[:,2] # valid syntax, though I've typically only seen it used in numpy This is interpreted as a tuple of a slice object and an integer. But with the new syntax, the slice has to be surrounded by parentheses if not the sole element in brackets: my_array[(:),2] This is a sticking point and would destroy backwards compatibility. I realize that the context-dependence is due to the behavior of the current slice syntax. For example, my_array[(:,2)] is invalid even though it looks like a tuple of a slice object and an integer. So I'm actually OK with parentheses not being mandatory in a slicing role, since that is already correct syntax in the current language. This might be a reconciliation. That would mean parentheses are not required when it is the sole argument to a function or when it is used inside indexing. my_array[:,2] # fine my_indexing = (:), 2 # parentheses required my_array[((:), 2)] # same here, but no need to do this This actually makes building up index expressions easier and gives a possible solution to the recent slice literals discussion. Maybe this should be the only context when parentheses aren't required, so you know you're dealing with an object inside of a function call. sum((1:6)) Anyways, just some more thoughts. On Sat, Nov 10, 2018 at 10:58 PM Nicholas Harrison < nicholasharrison...@gmail.com> wrote: > I'm aware that syntax for ranges and slices has been discussed a good > amount over the years, but I wanted to float an idea out there to see if it > hasn't been considered before. It's not really original. Rather, it's a > combination of a couple parts of Python, and I find it > fascinatingly-consistent with the rest of the language. This will look > similar to PEP 204, but there are some important differences and > clarifications. > > (start:stop:step) > > > Meet a range/slice object. Parentheses are required. (Its syntax in this > regard follows exactly the same rules as a generator expression.) I say > both range and slice because it can be used in either role. On the one > hand, it is iterable and functions exactly like range(start, stop, step) in > those contexts. On the other, it can also be passed into list indexing > exactly like slice(start, stop, step). This is a proposal that range and > slice are really the same thing, just in different contexts. > > Why is it useful? I at least find its syntax to be simple, intuitive, and > concise -- more so than the range(...) or slice(...) alternatives. It's > quite obvious for an experienced Python user and just as simple to pick up > as slice notation for a beginner (since it *is* slice notation). > > It condenses and clears up sometimes-cumbersome range expressions. A > couple examples: > > > sum(1:6) # instead of sum(range(1, 6)) > > list(1:6) > > > for i in (1:6): > > print(i**2) > > > (i**2 for i in (1:6)) > > > It also makes forming reusable slices clearer and easier: > > my_slice = (:6:2) # instead of slice(None, 6, 2) > my_list[my_slice] > > > It has a couple of siblings that should be obvious (think list or set > comprehension): > > [start:stop:step] # gives a list > {start:stop:step} # gives a set > > > This is similar to passing a range/slice object into the respective > constructor: > > > [1:6] # list(1:6) or [1, 2, 3, 4, 5] > {1:6} # set(1:6) or {1, 2, 3, 4, 5} > > > Note that the parentheses aren't needed when it is the only argument of a > function call or is the only element within brackets or braces. It takes on > its respective roles for these bracket and brace cases, just like > comprehensions. This also gives rise to the normal slice syntax: > > my_list[1:6:2] # What is inside the brackets is a slice object. > my_list[(1:6:2)] # Equivalent. The parentheses are valid but unnecessary. > > > So here's the part that requires a little more thought. Any of the values > may be omitted and in the slice context the behavior has no changes from > what it already does: start and stop default to the beginning or end of the > list depending on direction and the step defaults to 1. In the range > context, we simply repeat these semantics, but noting that there is no > longer a beginning or end of a list. > > Step defaults to 1 (just like range or slice). > Start defaults to 0 when counting up and -1 when counting down (just like > slice). > If stop is omitted, the object will act like an itertools.count object, > counting indefinitely. > > I have found infinite iteration to be a natural and oft-desired extension > to a range object, but I can understand that some may want it to remain > separate and pure within itertools. I also admit that the ability to form > an infinite list with only three characters can be a scary thought (though > we are all adults here, right? ;). Normally you have to take a couple extra > keystrokes: > > from itertools import count > list(count()) > # rather than just [:] > > > If that is the case, then raising an error when iter() is called on a > range/slice object with no stop value could be another acceptable course of > action. The syntax will still be left valid. > > And that's mainly it. Slice is iterable or range is "indexable" and the > syntax can be used anywhere successive values are desired. If you want to > know what it does or how to use it in some case, just think, "what would a > slice object do?" or "what would a range object do?" or "how would I write > a generator expression/list comprehension here?". > > Here are a few more examples: > > > for i in (:5): # 5 elements 0 to 4, i.e. range(5) > > print(i**2) > > > for i in (1:): # counts up from one for as long as you want, i.e. count(1) > > print(i**2) > > if i == 5: break > > it = iter(:) # a convenient usage for an infinite counter > > next(it) > > > ' '.join(map(str, (:5:2))) # gives '0 2 4' > > [(:5), (5:10)] # list of range/slice objects > [[:5], [5:10]] # list of lists > [*(:5), *(5:10)] # uses unpacking to get flat list > [*[:5], *[5:10]] # same unpacking to get flat list > > > Otherwise you'd have to do: > > [list(range(5)), list(range(5, 10))] # list of lists > [*range(5), *range(5, 10)] # flat list > > > Tuples: > > tuple(1:6:2) # (1, 3, 5) > *(1:6:2), # same > > > I don't actually have experience developing the interpreter and underlying > workings of Python, so I don't know how much of a change this requires. I > thought it might be possible since the constructs already exist in the > language. They just haven't been unified yet. I also realize that there are > a few other use-cases that need to be ironed out. The syntax might also be > too minimal in some cases to be obvious. One of the trickiest things may be > what it will be called, since the current language has the two different > terms. > > In the end it's just another range/slice idea, and the idea has probably > already been proposed sometime in the past few decades, but what thoughts > are there? > > - Nicholas >
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/