62 votes

vérifier efficacement que la chaîne est composée d'un caractère en Python

Ce qui est un moyen efficace pour vérifier qu'une chaîne de caractères s en Python se compose d'un seul caractère, disons 'A'? Quelque chose comme all_equal(s, 'A') qui se comporte comme ceci:

all_equal("AAAAA", "A") = True

all_equal("AAAAAAAAAAA", "A") = True

all_equal("AAAAAfAAAAA", "A") = False

Deux apparemment inefficace: d'abord convertir la chaîne en une liste et vérifier chaque élément, ou de seconde pour utiliser une expression régulière. Existe-il des moyens plus efficaces ou sont-ils les meilleurs, on peut le faire en Python? Merci.

113voto

Ellioh Points 2277

C'est de loin le plus rapide, plusieurs fois plus rapide que même count(), juste le temps avec cet excellent mgilson du calendrier de la suite:

s == len(s) * s[0]

Ici, la vérification se fait à l'intérieur de l'Python code C qui vient:

  • alloue len(s) de caractères;
  • remplit l'espace avec le premier caractère;
  • compare deux chaînes de caractères.

Plus la série est longue, plus grande est des bonus de temps. Cependant, comme mgilson écrit, il crée une copie de la chaîne, donc si votre chaîne est de longueur plusieurs millions de signes, il peut devenir un problème.

Comme nous pouvons le voir à partir des résultats de minutage, généralement des moyens les plus rapides pour résoudre la tâche de ne pas exécuter n'importe quel code Python pour chaque symbole. Cependant, l' set() solution aussi fait tout le travail à l'intérieur C le code de la bibliothèque Python, mais il est encore lent, probablement en raison de l'exploitation de la chaîne par le biais de Python interface d'un objet.

UPD: Concernant la chaîne vide en cas. Quoi faire avec elle, dépend fortement de la tâche. Si la tâche est de "vérifier si tous les symboles en une chaîne de caractères sont les mêmes", s == len(s) * s[0] est une réponse valide (pas de symboles signifient une erreur, et l'exception est ok). Si la tâche est de "vérifier si il y a exactement un symbole unique", une chaîne vide devrait nous donner de Faux, et la réponse est - s and s == len(s) * s[0]ou bool(s) and s == len(s) * s[0] si vous préférez recevoir des valeurs booléennes. Enfin, si nous comprenons la tâche comme "vérifier si il n'y a pas de symboles", le résultat pour la chaîne vide est Vrai, et la réponse est - not s or s == len(s) * s[0].

42voto

mgilson Points 92954
>>> s = 'AAAAAAAAAAAAAAAAAAA'
>>> s.count(s[0]) == len(s)
True

Ce n'est pas de court-circuit. Une version qui ne court-circuit serait:

>>> all(x == s[0] for x in s)
True

Cependant, j'ai le sentiment qu'en raison de la l'C optimisés mise en œuvre, la non-court-circuit version sera probablement plus performant sur certaines chaînes (en fonction de la taille, etc)


Voici un simple timeit script pour tester quelques-uns des autres options de mise en ligne:

import timeit
import re

def test_regex(s,regex=re.compile(r'^(.)\1*$')):
    return bool(regex.match(s))

def test_all(s):
    return all(x == s[0] for x in s)

def test_count(s):
    return s.count(s[0]) == len(s)

def test_set(s):
    return len(set(s)) == 1

def test_replace(s):
    return not s.replace(s[0],'')

def test_translate(s):
    return not s.translate(None,s[0])

def test_strmul(s):
    return s == s[0]*len(s)

tests = ('test_all','test_count','test_set','test_replace','test_translate','test_strmul','test_regex')

print "WITH ALL EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="AAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("AAAAAAAAAAAAAAAAA") != True:
        print globals()[test]("AAAAAAAAAAAAAAAAA")
        raise AssertionError

print
print "WITH FIRST NON-EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="FAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("FAAAAAAAAAAAAAAAA") != False:
        print globals()[test]("FAAAAAAAAAAAAAAAA")
        raise AssertionError

Sur ma machine (OS X 10.5.8, core2duo, python2.7.3) avec ces artificiel (court) les chaînes de caractères, str.count fume set et all, et les battements str.replace par un peu, mais il est devancé par str.translate et strmul est actuellement en tête par une bonne marge:

WITH ALL EQUAL
test_all 5.83863711357
test_count 0.947771072388
test_set 2.01028490067
test_replace 1.24682998657
test_translate 0.941282987595
test_strmul 0.629556179047
test_regex 2.52913498878

WITH FIRST NON-EQUAL
test_all 2.41147494316
test_count 0.942595005035
test_set 2.00480484962
test_replace 0.960338115692
test_translate 0.924381017685
test_strmul 0.622269153595
test_regex 1.36632800102

Les horaires peuvent être légèrement (ou même de manière significative?) différents entre les différents systèmes et différentes chaînes, alors que ce serait intéressant de regarder dans une chaîne de caractères que vous avez l'intention de passer.

Finalement, si vous frappez le meilleur des cas pour all assez, et vos chaînes sont assez longtemps, vous pourriez envisager qu'un. C'est un meilleur algorithme ... je voudrais éviter de l' set solution mais comme je ne vois pas de cas où il pourrait battre la count de la solution.

Si la mémoire pourrait être un problème, vous devrez éviter d' str.translate, str.replace et strmul que ceux de créer une deuxième chaîne, mais ce n'est pas un sujet de préoccupation ces jours-ci.

16voto

Daniel Roseman Points 199743

Vous pouvez convertir en un ensemble et vérifier qu'il n'y a qu'un seul membre:

 len(set("AAAAAAAA"))
 

12voto

Mark Byers Points 318575

Essayez d’utiliser la fonction intégrée all :

 all(c == 'A' for c in s)
 

6voto

Abhijit Points 24122

Ajouter une autre solution à ce problème

 >>> not "AAAAAA".translate(None,"A")
True
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X