Python: включить последний элемент (где i = 0) при повторении в обратном порядке

#python #for-loop #range

Вопрос:

У меня есть строка string = 'cbAfeDihGlkJonMrqPutS' , и я хотел бы выполнить итерацию по ней, чтобы получить 'AbcDefGhiJklMnoPqrStu' результат.

Я использую следующий цикл for для достижения 'DefGhiJklMnoPqrStu , однако ожидаемое Abc отсутствует.

 for i in (range(0, len(string), 3)):
    print(string[i 2 : i-1 : -1], end='')
 

Кажется, это проблема из-за -1 в моем конечном элементе : i-1 : , однако мне интересно, почему повторение в обратном порядке (с размером шага -1 ) и использование :i-1: для захвата первого элемента (где i = 0) не работает аналогично итерации вперед и использованию : i 1 : для захвата последнего элемента.

Я могу достичь желаемого решения с помощью

 for i in (range(0, len(string), 3)):
    print(string[i:i 3:1][::-1], end='')
 

но итерация вперед и печать назад кажется неуклюжим способом сделать это. Необходимость добавления [::-1] кажется излишним шагом, который [i 2 : i-1 : -1] должен был сработать.

Есть ли более чистый способ сделать это, кроме [i:i 3:1][::-1] ?

Комментарии:

1. ''.join(sorted(list(string), key=str.lower))

2. «.join(sorted(string , key=str.casefold)) должен дать желаемый результат.

Ответ №1:

Вероятно, самый элегантный способ сделать это — использовать ''.join() и понимание, чтобы сжать цикл, который вы уже придумали, в одну строку.

 string = 'cbAfeDihGlkJonMrqPutS'
print(''.join(string[i:i 3][::-1] for i in range(0, len(string), 3)))
# AbcDefGhiJklMnoPqrStu
 

Можно подумать, что вы сможете избавиться от лишнего [::-1] , выполнив like string[i 2:i-1:-1] , но, как вы заметили, это отсекает первый символ в строке. Это особенность синтаксиса среза: end граница среза является исключающей, а не включающей, но в то же время индекс ссылается на конец строки, а не на «символ перед началом» (что было бы за пределами, скажем, в Java, но в котором мы бы не сталиу меня нет -1 этой проблемы). Насколько мне известно, обойти это невозможно.

Вы также можете попробовать использовать reversed() вместо [::-1] , но я не думаю, что это делает его проще или быстрее:

 print(''.join(c for i in range(0, len(string), 3) for c in reversed(string[i:i 3])))
 

РЕДАКТИРОВАТЬ: поскольку @don’t talk just code указано в комментарии, на самом деле проблему можно обойти, поскольку включение None в качестве второго аргумента фрагмента приведет к переходу фрагмента в любой конец списка, который подходит (до этого комментария я этого не знал). Имея это в виду, мы действительно можем сделать это только в одном фрагменте:

 print(''.join(string[i 2:i-1 if i else None:-1] for i in range(0, len(string), 3)))
# AbcDefGhiJklMnoPqrStu
 

Это немного неэлегантно, но, похоже, работает, и было бы больше того, что вы ищете, чем указано выше.

Комментарии:

1. Я, конечно, рассмотрю ваше подтверждение того, что «нет никакого способа обойти это» в качестве удовлетворительного ответа. Облом, что этот случай попадает через трещины интуитивного нарезания. Спасибо

2. @ZwiTrader Вы могли бы сделать i-1 if i else None вместо i-1 . Или сохранить i-1 , но удвоить отрицательные значения, используя range(-len(string), 0, 3) .

3. @don’tt tal justcode, ПОКА это не произойдет. Я соответствующим образом отредактировал свой ответ, спасибо, что напомнили об этом

4. Да, None это просто значение по умолчанию, эквивалентное пропуску значения ( демонстрация ). Вы также пробовали отрицательный диапазон? Это необычно, но также просто и, возможно, немного быстрее.

5. Спасибо за оба предложения @ не говорите только о коде. Об этом интересно узнать i-1 if i else None как о опции, но «удвоение негативов», как вы говорите, компактно и соответствует тому, что я искал в первую очередь. Идеальный.