2

I have got a harmless list "mylist" of six items. Never empty, never any beastly items.

mylist = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']

I found that there are two ways to index a list for slicing purposes. For this question I shall call them upper index (from 0 to 5) and lower index (from -1 to -6). Here is a quick diagram (I had posted a photo with colors, which was replaced by another user):

 <<<<|_________mylist[5:?:-1]_____________|

     |___mylist[0:3:1]______|>>>>>>
 ?
(x)   0      1      2      3      4      5     (6)

     aaa    bbb    ccc    ddd    eee    fff

(-7) -6     -5     -4     -3     -2     -1     (y)
                                                ?
             <<<<<<|___mylist[-1:-5:-1]___|

     |______mylist[-6:?:1]________________|>>>>>

For easily slicing at the start of my list, I can use the upper index like this:

>>> mylist[0:3:1]
['aaa', 'bbb', 'ccc']

For easily slicing near the end, I find the lower index helpful, like:

>>> mylist[-1:-5:-1]
['fff', 'eee', 'ddd', 'ccc']

By "easily" I mean for example the aspect of not concerning my code with the length of the list.

I learnt that Python slicing is "always holding back" (also known as "up to but not including"). I am showing this in my diagram as the "arrow-heads" pointing from the end of a slice to the "next item" ahead ("ahead" in the sense/direction of the slicing).

For my users I need to show the first so many items from my list. (Those are the results from a recursive search and evaluate and sort run.) Because normally this list is rather long, I do print it backwards (in the console), so that when the script ends, the best n results will still be visible on the screen. So my step is -1 in this context.

I would like to use variables, to allow for different needs for different users (and different screen sizes). For debugging and for certain users I want to be able to print all results, i.e. the entire list backwards. I would like something in this syntax:

start = 5     # I only need to know "5" for debugging, normally I can use
              # any small number to show a few selected items of mylist

end = x       # this is my main question, see photo

mylist[start:end:-1]

My question is please, how do I write the first(topmost) slice in the diagram, by using variables and by using the upper list of indices? Or in other words, what is the numerical value of x (and y) in the diagram?

Semi-solutions that I do not want to use:

mylist[5::-1]       # no variable at all

mylist[5:None:-1]   # does not fit in my normal function for printing

I have not managed to use None in any simple operations with my variables, it gives me errors like:

    end = None - 20 
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'

And I have not managed to convert None to any integer or other number. Is there a way? Would probably be a sacrilege in Pythonland...

If I cannot find the secret number of x, then I might need to work with this:

mylist[-1:-7:-1]  # I want to use the upper index rather

But this approach involves checking the length of mystring.

end = -1 * (len(mystring)+1)  # extra-explicit as an example
returns  -7

mystring[-1:end:-1]

returns  ['fff', 'eee', 'ddd', 'ccc', 'bbb', 'aaa']  # this is the output I want, just not the method

What I have done before asking you:
I have written a testing-script and have tried guessing x. I have searched and read up plenty on slicing and variables here on Stack Overflow (like when use negative number to slice a string in Python, 0 is disabled?) and online (like https://docs.python.org/3/library/functions.html?highlight=slice#slice) and I searched through ebooks like Introducing Python and Learning Python.

A plea:
Please do not tell me that what I want is wrong. I can slice mylist[0:6:1] although there is no item indexed 6 in my example list, same for -7. This is why I have this hunch that there might be numbers for x and for y too, which I could use in my variables.

You are welcome to tell me that it does not exist or cannot be done the way I would prefer. In the latter case, I am also asking you for alternatives, as close to my request as possible.

Background for those who want more:
This question is more about "figuring out slicing" than about making the printout possible. I do have workarounds, but would love to find out more.

Community
  • 1
  • 1
Martin Zaske
  • 529
  • 5
  • 21
  • 1
    ...what?! Are you saying that you want the first `n` items of your sequence, in reverse order? Why not `seq[:n][::-1]` (i.e. get the first `n` items, *then* reverse them - note that you can leave out some of the numbers, see e.g. http://stackoverflow.com/q/509211/3001761)? And could you transfer the content from the photo to actual text, please; the former is profoundly unhelpful for users with e.g. screenreaders. – jonrsharpe Sep 03 '15 at 13:00
  • @jonrsharpe: thanks, yes the first n items normally, and sometimes all items, and always in reverse. I do not understand how "leaving out some of the numbers" will help me for using variables - my question is not about typing less code; please explain. – Martin Zaske Sep 03 '15 at 13:10
  • @jonrsharpe: Do you have a link to some syntax help for making my graph into text, keeping the alignements and the colours and arrow-heads, etc.? How would it be read out by a screenreader then? Do you have any examples which you consider good or helpful? I am willing to learn, even though I spent a lot of time on my question to make clear what I have and what I search for. Is there a rule here that questions must be accessible to a maximum number of users (or all users?), including those with special setups? – Martin Zaske Sep 03 '15 at 13:16
  • 1
    If you want to have inclusive slice you need to use `None`. If by using `None` you're getting errors, I think you should be looking at that part of the problem and not trying to replace `None`. So it's the way that you're generating those indices that needs to change to support `None`. – Kassym Dorsel Sep 03 '15 at 13:20
  • 1
    You wrote quite a lot so I don't quite understand what you're asking, but if you always need the items in reverse why don't you just access them as normal and use `reversed` such as `reversed(mylist[:x])`? – postelrich Sep 03 '15 at 13:20
  • @jonrsharpe: And sorry, I have not yet answered your "why not": Yes, indeed why not. No reason to not change my approach. I just have never used "seq", so I will need time to look it up and see what it has got to offer for variables. Yould you give an actual example with mylist that would return the equivalent of the green slice please? – Martin Zaske Sep 03 '15 at 13:21
  • @MartinZaske `seq` is just a more generic name than `mylist`, as slicing also applies to tuples, strings, ... – jonrsharpe Sep 03 '15 at 13:25
  • Related: http://stackoverflow.com/q/30221432/3001761, http://stackoverflow.com/q/30765493/3001761 – jonrsharpe Sep 03 '15 at 13:40

3 Answers3

4

The short answer is: No.


You have to use None if you don't want to calculate the length of the list (although that's an O(1) operation in Python):

>>> mylist = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']
>>> mylist[2:None:-1]  # first 3 items backwards (note off-by-one indexing)
['ccc', 'bbb', 'aaa']
>>> mylist[None:None:-1]  # all items backwards
['fff', 'eee', 'ddd', 'ccc', 'bbb', 'aaa']

You could use a placeholder for "the whole thing", e.g. 0:

>>> end = 3
>>> mylist[end - 1 if end else None::-1]
['ccc', 'bbb', 'aaa']
>>> end = 0
>>> mylist[end - 1 if end else None::-1]
['fff', 'eee', 'ddd', 'ccc', 'bbb', 'aaa']

You could also consider two steps, i.e. take the slice you want then reverse it:

>>> mylist[:3][::-1]
['ccc', 'bbb', 'aaa']
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Yes, None is the closest I have come myself, as I have written in my question. I just cannot do anything mathematical with it, like I can with integers in variables. That is why I have asked whether there is a "number" or numerical equivalent (or alternative expression) for None. At least I can assign None to my end-variable, so that is one step towards a nice solution. – Martin Zaske Sep 03 '15 at 13:25
  • @MartinZaske then the answer is *"no"*. Frankly, I think you may have overcomplicated what is in essence a simple question, making it much more difficult than it needs to be to answer. – jonrsharpe Sep 03 '15 at 13:26
  • @MartinZaske I suppose could use a dummy value (say, use `0` for *"all items"*), then have e.g. `mylist[end - 1 if end else 0::-1]`. It's also not clear what the problem with leaving out values is. – jonrsharpe Sep 03 '15 at 13:29
  • @MartinZaske although frankly, the *slice then reverse*, either with a second slice or `reversed`, is **so much easier**. – jonrsharpe Sep 03 '15 at 13:32
  • sorry, if you perceive it as too complicated. I see so many questions where a lot of information is missing ("what does your data look like?" "how do you want your output?") and then any later reader needs to puzzle it together from the initial question and from the exchange in the comments. I was afraid that the answer is "no" but if that is the case, at least I was not too stupid to find it. I wanted to avoid that users would give me tangent answers like "use the backwards index", or "use None" by assuming that I had not done my homework. – Martin Zaske Sep 03 '15 at 13:34
  • @MartinZaske I'd rather you err on this side than the other, thank you for your effort. But I think you could probably have asked the same question in about 100 words! – jonrsharpe Sep 03 '15 at 13:35
  • @jonsharpe: thank you for all your input and feedback. you can see that I am a new user here and (hopefully) learning things. I am even still struggling with the callouts, sometimes they just disappear. Proposal: If you dare, you can write "no" as an official answer; and we will let it stand for a few days. And I can pick it as the answer about the numerical reference for xx. Thank you for the idea to use two slices in succession [:n][::-1]. I want variables, because each run of my script is different and I want the user to be able to select easily how much (and where from) he/she gets. – Martin Zaske Sep 03 '15 at 13:41
  • @MartinZaske *"No"* is too short to be an answer! I will edit it into this one, and pull in my comment about the two-step method. – jonrsharpe Sep 03 '15 at 13:44
2

Just make a default value and if the user changes the default (max) then change your variable.

>>> mylist = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']
>>> display_num = len(mylist)
>>> mylist[display_num-1::-1]
['fff', 'eee', 'ddd', 'ccc', 'bbb', 'aaa']

Change the display_num variable to the number of results you want to show.

>>> display_num = 4
>>> mylist[display_num-1::-1]
['ddd', 'ccc', 'bbb', 'aaa']

I think your options are None or the length of the list.

... or just use a try catch

>>> display_num = None
>>> try:
>>>     mylist[display_num-1::-1]
>>> except TypeError:
>>>     mylist[::-1]
justengel
  • 6,132
  • 4
  • 26
  • 42
  • I like your help with the actual examples how to handle it. And I observe that you always leave the "critical place" (end of slice) empty as in :: Which is maybe your indirect way of saying "There is no numerical alias for None in the context of Python list-slicing"? – Martin Zaske Sep 03 '15 at 13:51
1

If I understood your question correctly, you want y such that

mylist[2:y:-1] == ['ccc', 'bbb', 'aaa']

where y must be an integer.

The y you are looking for is -len(mylist)-1 or a lower integer. If you need a constant, you could use -sys.maxsize (as it is impossible to have lists larger than maxsize).


If we look at CPython's source code, we can see that list objects internally use the function PySlice_GetIndicesEx. In order to do what you want, PySlice_GetIndicesEx must set step = -1, start = 2 and slicelength = 3.

start = 2 and step = -1 are already set by your code. Are there values for y that can lead to slicelength = 3? Let's see...

When step is negative, the line that calculates slicelength is this one:

*slicelength = (*stop-*start+1)/(*step)+1;

From here, we can see that in order to have slicelength = (stop - start + 1) / step + 1 = 3, we must have stop = -1. However, the case stop < 0 is treated specially:

if (*stop < 0) *stop += length;
if (*stop < 0) *stop = (*step < 0) ? -1 : 0;

So, if we provide a negative stop, it gets increased by len(mylist). If stop is still negative it is set to -1, which is the value we need.

Andrea Corbellini
  • 17,339
  • 3
  • 53
  • 69
  • awesome. It is risky to declare that no solution exists to a given question with its specific context (and you are taking my curiosity about slicing-syntax seriously). So your approach to look at the source and the way you clearly show it to not-developers is most helpful. I was afraid that there is no answer, and I am glad that I did not just overlook something. I was hoping for some inbuilt hidden "alias" or whatever, as those exist in languages and normally one needs to be told; hard to guess those. – Martin Zaske Sep 03 '15 at 14:29
  • @MartinZaske: I updated the answer. Probably it contains the solution you need, though I'm not sure. – Andrea Corbellini Sep 03 '15 at 14:56
  • I was not criticising your initial opening statement, but rather complementing you. A doctor can never say "You got no sickness." or "There is no cure for this." but a mathematician can say "There is no solution." when he can prove it. Thank you again for giving the evidence. – Martin Zaske Sep 03 '15 at 15:08
  • @MartinZaske: I appreciated your comment. I was just pointing out that I didn't carefully read all the source before answering. – Andrea Corbellini Sep 03 '15 at 15:19