web-dev-qa-db-fra.com

Coupe en python à pas inversé

Un exemple spécifique de ma question est, "Comment puis-je obtenir '3210' dans cet exemple?"


>>> foo = '0123456'
>>> foo[0:4]
'0123'
>>> foo[::-1]
'6543210'
>>> foo[4:0:-1] # I was shooting for '3210' but made a fencepost error, that's fine, but...
'4321'
>>> foo[3:-1:-1] # How can I get '3210'?
''
>>> foo[3:0:-1]
'321'

Il semble étrange que je puisse écrire foo [4: 0: -1], foo [5: 1: -1], etc. '.

Une méthode de fortune pour ce faire serait foo [0: 4] [:: - 1], mais cela crée deux objets chaîne dans le processus. Je vais effectuer cette opération littéralement des milliards de fois, donc chaque opération de chaîne coûte cher.

Je dois manquer quelque chose de bête et facile. Merci de votre aide!

28
eblume

Exclure simplement l'index de fin de plage ...

>>> foo[3::-1]
'3210'

Ironiquement, à propos de la seule option, je pense que vous n'avez pas essayé.

35
Andrew White

Si vous recherchez quelque chose d'un peu plus lisible par l'homme que la notation de tranche étendue:

>>> foo = '0123456'
>>> ''.join(reversed(foo[0:4]))
'3210'
8
Aaron Dufour

Omettez l'index de fin dans votre notation de tranche:

>>> foo = '0123456'
>>> foo[3::-1]
'3210'

Si vous devez le faire plusieurs fois, créez un objet slice que vous pourrez utiliser plusieurs fois.

>>> i = slice(3,None,-1)
>>> foo[i]
'3210'
6
PaulMcG

Après avoir lu la "documentation technique" ( ici ) - plus précisément la phrase: 

Si l’une ou l’autre des bornes est négative, la longueur de la séquence y est ajoutée.

J'ai décidé d'essayer ceci et cela a fonctionné:

>>> foo = '0123456'
>>> foo[3:-1-len(foo):-1]
'3210'
>>>

Je pense donc que la meilleure solution pour déterminer le "point final" par programme serait de fournir une fonction d'assistance bien nommée qui indique clairement que ses arguments sont toujours traités comme des décalages positifs, peut-être special_slice()

Je pense que la clarté de ce cas "spécial" est extrêmement importante car de nombreux cas d'utilisation courants et importants dépendent du comportement par défaut des compensations négatives (c'est-à-dire en leur ajoutant la longueur). Personnellement, j’utilise souvent un point final «-1» pour signifier: arrêter juste avant le dernier élément.

Donc, en fonction de votre commentaire:

... algorithme qui fonctionne un peu comme suit: foo [i: i-4: -1], qui commence par un 'i' élevé et qui descend.

Je pourrais faire ce qui suit:

def slice_by_len(data, start, length, step=1):
    end = start + length if step > 0 else start - length
    if end < 0:
        # Fix the negative offset to get what we really want
        end -= len(data)
    return data[start:end:step]

Et appelez-le pour chaque tranche requise:

foo_part = slice_by_len(foo, i, 4, -1)

Ce qui précède pourrait facilement passer en boucle sur les valeurs de 'i'

3
CrashNeb

Vous pouvez utiliser s[::-1] pour inverser la chaîne entière. Mais si vous souhaitez inverser chaque sous-chaîne avec une longueur fixe, vous pouvez d'abord extraire la sous-chaîne, puis inverser toute la sous-chaîne. Par exemple, supposons que nous devions vérifier si chaque sous-chaîne de longueur 3 de la chaîne foo est un palindrome, nous pouvons le faire comme suit:

>>> foo = '0102030'
>>> for i in range(len(foo)-3):
...     if foo[i:i+3] == foo[i:i+3][::-1]:
...         print(foo[i:i+3], 'is a palindrome')
...     else:
...         print(foo[i:i+3], 'is not a palindrome')
...
010 is a palindrome
102 is not a palindrome
020 is a palindrome
203 is not a palindrome
030 is a palindrome

Si vous voulez vérifier si une sous-chaîne est en palindrome, procédez comme suit:

if foo[i:i+3] == foo[i+2:i-1:-1]:
    ...

vous ne pourrez pas traiter le cas où i est 0, car vous comparez en fait foo[0:3] avec foo[2:-1:-1], ce qui équivaut à foo[2:n-1:-1], qui est une chaîne vide.

Le seul inconvénient de la première solution est qu’elle utilise un peu plus de mémoire, mais ce n’est pas grave.

2
Luan Gong

En plus des solutions ci-dessus, vous pouvez faire quelque chose comme:

foo = '0123456'
foo[-4::-1]

Je suppose que si foo change de longueur, ce n’est peut-être pas la meilleure solution, mais si elle est statique, cela fonctionnerait.

1
jack

Donné:

>>> foo = '0123456'

La chaîne désirée 3210 va de l'index 3ème au 0ème caractère:

>>> stop_idx=0
>>> start_idx=3

Voici deux solutions génériques:

  1. Prenez la tranche avant puis inversez-la:

    >>> foo[stop_idx:start_idx+1][::-1]
    '3210'
    
  2. Sur la base de cette réponse , utilisez un pas négatif et arrêtez 1 élément avant le premier élément (plus le décalage de fin):

    >>> foo[start_idx:stop_idx-len(foo)-1:-1]
    '3210'
    
    >>> a[start_idx:stop_idx-len(a)-1:-1]
    [2, 1]
    

En comparant les temps d'exécution, la première version est plus rapide: 

>>> timeit.timeit('foo[stop_idx:start_idx+1][::-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
1.7157553750148509
>>> timeit.timeit('foo[start_idx:stop_idx-len(foo)-1:-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
1.9317215870250948
0
Tom Hale