web-dev-qa-db-fra.com

Comment savoir si une variable python est une chaîne ou une liste?

J'ai une routine qui prend une liste de chaînes comme paramètre, mais j'aimerais prendre en charge le passage d'une seule chaîne et la convertir en une liste d'une chaîne. Par exemple:

def func( files ):
    for f in files:
        doSomethingWithFile( f )

func( ['file1','file2','file3'] )

func( 'file1' ) # should be treated like ['file1']

Comment ma fonction peut-elle savoir si une chaîne ou une liste a été transmise? Je sais qu'il existe une fonction type, mais existe-t-il une méthode "plus Pythonique"?

61
Graeme Perrow

Eh bien, il n'y a rien de contraire à la vérification du type. Cela dit, si vous êtes prêt à mettre un petit fardeau sur l'appelant:

def func( *files ):
    for f in files:
         doSomethingWithFile( f )

func( *['file1','file2','file3'] ) #Is treated like func('file1','file2','file3')
func( 'file1' )

Je dirais que c'est plus Pythonic dans la mesure où "explicite vaut mieux qu'implicite". Ici, il y a au moins une reconnaissance de la part de l'appelant lorsque l'entrée est déjà sous forme de liste.

37
David Berger
isinstance(your_var, basestring)
43
Ayman Hourieh

Personnellement, je n'aime pas vraiment ce genre de comportement - il interfère avec la frappe de canard. On pourrait dire qu'il n'obéit pas au mantra "Explicit is better than implicit". Pourquoi ne pas utiliser la syntaxe varargs:

def func( *files ):
    for f in files:
        doSomethingWithFile( f )

func( 'file1', 'file2', 'file3' )
func( 'file1' )
func( *listOfFiles )
32
Dave

Je dirais que la façon la plus Python'y est de faire en sorte que l'utilisateur passe toujours une liste, même s'il n'y a qu'un seul élément dedans. Cela rend vraiment évident func() peut prendre une liste de fichiers

def func(files):
    for cur_file in files:
        blah(cur_file)

func(['file1'])

Comme Dave l'a suggéré, vous pouvez utiliser la syntaxe func(*files), mais je n'ai jamais aimé cette fonctionnalité, et il semble plus explicite ("explicite vaut mieux qu'implicite") d'exiger simplement une liste. Cela transforme également votre cas spécial (appelant func avec un seul fichier) en cas par défaut, car vous devez maintenant utiliser une syntaxe supplémentaire pour appeler func avec une liste ..

Si vous voulez créer un cas spécial pour un argument étant une chaîne, utilisez le isinstance() builtin , et comparez à basestring (qui les deux str() et unicode() sont dérivés de) par exemple:

def func(files):
    if isinstance(files, basestring):
        doSomethingWithASingleFile(files)
    else:
        for f in files:
            doSomethingWithFile(f)

Vraiment, je suggère simplement d'exiger une liste, même avec un seul fichier (après tout, cela ne nécessite que deux caractères supplémentaires!)

16
dbr
def func(files):
    for f in files if not isinstance(files, basestring) else [files]:
        doSomethingWithFile(f)

func(['file1', 'file2', 'file3'])

func('file1')
11
jfs
if hasattr(f, 'lower'): print "I'm string like"
11
limscoder

Si vous avez plus de contrôle sur l'appelant, alors l'une des autres réponses est meilleure. Je n'ai pas ce luxe dans mon cas, j'ai donc opté pour la solution suivante (avec des mises en garde):

def islistlike(v):
   """Return True if v is a non-string sequence and is iterable. Note that
   not all objects with getitem() have the iterable attribute"""
   if hasattr(v, '__iter__') and not isinstance(v, basestring):
       return True
   else:
       #This will happen for most atomic types like numbers and strings
       return False

Cette approche fonctionnera dans les cas où vous avez affaire à un ensemble connu de types de liste qui répondent aux critères ci-dessus. Certains types de séquences seront cependant manqués.

6
Dana the Sane

Varargs était déroutant pour moi, donc je l'ai testé en Python pour le clarifier par moi-même.

Tout d'abord, le PEP pour varargs est ici .

Voici un exemple de programme, basé sur les deux réponses de Dave et David Berger, suivi de la sortie, juste pour clarification.

def func( *files ):
    print files
    for f in files:
        print( f )

if __== '__main__':
    func( *['file1','file2','file3'] ) #Is treated like func('file1','file2','file3')
    func( 'onestring' )
    func( 'thing1','thing2','thing3' )
    func( ['stuff1','stuff2','stuff3'] )

Et la sortie résultante;

('file1', 'file2', 'file3')
file1
file2
file3
('onestring',)
onestring
('thing1', 'thing2', 'thing3')
thing1
thing2
thing3
(['stuff1', 'stuff2', 'stuff3'],)
['stuff1', 'stuff2', 'stuff3']

J'espère que cela sera utile à quelqu'un d'autre.

3
James McMahon