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"?
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.
isinstance(your_var, basestring)
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 )
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!)
def func(files):
for f in files if not isinstance(files, basestring) else [files]:
doSomethingWithFile(f)
func(['file1', 'file2', 'file3'])
func('file1')
if hasattr(f, 'lower'): print "I'm string like"
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.
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.