2009-05-07 18 views
60

Tengo una rutina que toma una lista de cadenas como parámetro, pero me gustaría admitir pasar una sola cadena y convertirla en una lista de una cadena. Por ejemplo:¿Cómo puedo saber si una variable de python es una cadena o una lista?

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

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

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

¿Cómo puede mi función indicar si se ha pasado una cadena o una lista? Sé que hay una función type, pero ¿hay una forma "más pythonic"?

Respuesta

36

Bueno, no hay nada de antipático sobre el tipo de comprobación. Una vez dicho esto, si usted está dispuesto a poner una pequeña carga sobre la persona que llama:

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

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

yo diría que esto es más Pythonic en que "explícita es mejor que implícito". Aquí hay al menos un reconocimiento por parte de la persona que llama cuando la entrada ya está en forma de lista.

+9

Sin duda, el método más "explícito" sería exigir al usuario pasar el archivo único en una lista? Como func (['archivo1']) – dbr

+1

Estoy de acuerdo en que eso es explícito, pero no veo cómo es más explícito. Ambas técnicas usan la misma lógica; uno es el reverso del otro. Creo que lo anterior es un poco más intuitivo, porque enfatiza que los archivos en la lista en lugar de la lista en sí son relevantes para func. –

43
isinstance(your_var, basestring) 
+3

Esta solución necesita alguna explicación, algún contexto. Ni tu_var ni la cadena de bases están en la publicación del OP. –

+2

Esto no funciona desde Python 3. Use 'isinstance (your_var, str)'. –

32

Personalmente, realmente no me gusta este tipo de comportamiento, ya que interfiere con el tipado de pato. Se podría argumentar que no obedece al mantra "lo explícito es mejor que lo implícito". ¿Por qué no utilizar la sintaxis varargs:

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

func('file1', 'file2', 'file3') 
func('file1') 
func(*listOfFiles) 
+1

"Personalmente, realmente no me gusta este tipo de comportamiento", ¿a qué se refiere específicamente? –

+0

@nemo Creo que se refería a la pregunta original, que podría interpretarse como pedir una función cuyo parámetro sea una cadena o una lista de cadenas en las que la llamada de función no ofrece ninguna mención de qué tipo de parámetro se pasa. Estoy de acuerdo con esto . –

+0

FWIW, varargs solo funciona si no hay otros argumentos: 'func (file, someflag)', no se puede convertir en 'func (* files, someflag)' sin convertir 'someflag' en una palabra clave arg – pjz

15

Yo diría que la forma más Python'y es hacer que el usuario siempre pasar una lista, incluso si sólo hay un artículo en él. Esto hace que sea muy obvio func() puede tomar una lista de archivos

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

func(['file1']) 

Como sugirió a Dave, se puede utilizar la sintaxis func(*files), pero nunca me ha gustado esta característica, y parece más explícita ("explícito es mejor que implícito") simplemente requerir una lista. También está convirtiendo su caso especial (llamada func con un solo archivo) en el caso por defecto, porque ahora usted tiene que utilizar la sintaxis extra para llamar func con una lista ..

Si desea hacer un caso especial para una discusión que es una cadena, utilice el isinstance() builtin, y compare con basestring (que tanto str() y unicode() se derivan de), por ejemplo:

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

Realmente, sugiero requiriendo simplemente una lista, incluso con sólo un archivo (¡después de todo, solo requiere dos caracteres adicionales!)

+10

El problema es que si solo confías en pato escribiendo aquí, una cadena * es * tratada como una lista con mecanografía de pato, y no hace lo correcto. En lugar de tratarlo como un solo elemento, Python tratará la cadena como una lista de caracteres. Whups. –

10
if hasattr(f, 'lower'): print "I'm string like" 
+0

Una solución muy simple que funcionó perfectamente para mí. ¡Gracias! –

+3

que funciona hasta que alguien le transfiere una colección con un método 'inferior': -/ – AlexChaffee

+1

^como un objeto matemático, o uno que interactúa con una función del mundo real como subir o bajar una ventana controlada mecánicamente. :) –

3

Varargs fue confuso para mí, así que lo probé en Python para aclararlo por mí mismo.

En primer lugar, el PEP para varargs es here.

Aquí hay un programa de ejemplo, basado en las dos respuestas de Dave y David Berger, seguido de la salida, solo por aclaración.

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

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

Y la salida resultante;

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

Espero que esto sea útil para otra persona.

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

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

func('file1') 
+0

POP 8 parece preferir isinstance (u'abc ', basetring) en lugar de usar el módulo types. – max

+0

@mdorseif: He editado la respuesta para usar el módulo 'basetring' en lugar de' types'. – jfs

6

Si tiene más control sobre la persona que llama, entonces una de las otras respuestas es mejor. Yo no tengo ese lujo en mi caso por lo que se establecieron en la siguiente solución (con salvedades):

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 

Este enfoque de trabajo para los casos en que está tratando con un conjunto de tipos de know-lista como que cumplen los criterios anteriores Sin embargo, algunos tipos de secuencia se perderán.

Cuestiones relacionadas