2010-11-21 7 views
6

tengo una función que obtiene una lista de tablas de BD como parámetro y devuelve una cadena de comandos para ejecutar en estas tablas, por ejemplo:manera Pythonic para verificar parámetro es una secuencia pero no cadena

pg_dump(file='/tmp/dump.sql', 
     tables=('stack', 'overflow'), 
     port=5434 
     name=europe) 

En caso devolver algo como:

pg_dump -t stack -t overflow -f /tmp/dump.sql -p 5434 europe 

Esto se hace usando tables_string='-t '+' -t '.join(tables).

La diversión comienza cuando la función es llamada con: tables=('stackoverflow') (una cadena) en lugar de tables=('stackoverflow',) (una tupla), lo que da:

pg_dump -t s -t t -t a -t c -t k -t o -t v -t e -t r -t f -t l -t o -t w 
     -f /tmp/dump.sql -p 5434 europe 

Debido a la propia cadena está siendo iterado.

This SO question sugiere que se utilice afirma en el tipo, pero no estoy seguro de que es lo suficientemente Pythonic porque rompe la convención de tipo pato.

¿Algún conocimiento?

Adam

+0

Usted se refiere a un usuario usando '('foo')' por error en lugar de '('foo',) ', ¿verdad? – orip

+0

@orip Ese es el punto: los usuarios podrían hacer este error, y quiero evitar que suceda. –

Respuesta

6

Afirmar que el tipo parece apropiado en este caso - manejando un mal uso común que parece legal debido a la mecanografía pato.

Otra forma de manejar este caso sería común para la prueba de cuerda y manejarlo correctamente como un caso especial.

Por último, se podría alentar a pasar los nombres de las tablas como parámetros posicionales que harían este escenario menos probable:

def pg_dump(*tables, **kwargs): 
    file = kwargs['file'] 
    port = kwargs['port'] 
    name = kwargs['name'] 
    ... 

pg_dump('stack', 'overflow', file='/tmp/dump.sql', port=5434, name='europe') 
+0

+1 Agradable. Creo que probaré el parámetro y simplemente convertiré una cadena en 1-tupla. Mantendría a los usuarios más felices. Nota al margen: –

0

¿Puede usted no utilizar una lista en lugar de un par de valores?

pg_dump(file='/tmp/dump.sql', 
     tables=['stack', 'overflow'], 
     port=5434, 
     name='europe') 
+0

no resuelve el problema - si el usuario pasa una cadena que sigue fallando. –

1

un lenguaje Python común para detectar si un argumento es una secuencia (una lista o tupla) o una cadena es comprobar si tiene el atributo __iter__:

def func(arg): 
    if hasattr(arg, '__iter__'): 
     print repr(arg), 'has __iter__ attribute' 
    else: 
     print repr(arg), 'has no __iter__ attribute' 

func('abc') 
# 'abc' has no __iter__ 

func(('abc')) 
# 'abc' has no __iter__ 

func(('abc',)) 
# ('abc',) has __iter__ 

Cuando no es una secuencia, también es común para convertirla en uno de simplificar el resto del código (que sólo tiene que lidiar con un tipo de cosa). En la muestra se podría haber hecho con un simple arg = [arg].

+5

cadenas no tienen un atributo '__iter__' era una inconsistencia y se ha cambiado en Python 3. como debe ser. –

+0

@Jim Brissom: Siempre sentía que esto era incompatible desde cadenas son después de todas las secuencias de caracteres. ¿Conoces un buen idioma PY3K? ¿Qué hay de verificar si el 'tipo (arg)' tiene un método de cadena, como 'más bajo'? – martineau

+1

puede verificar 'not isinstance (foo, str)' como se sugiere en la pregunta al asker vinculado ('basetring' en lugar de' str' para Python <3) – orip

2

Puede utilizar ABC afirmar que un objeto es iterable, pero no una cadena:

from types import StringType 
from collections import Iterable 
assert isinstance(x, Iterable) and not isinstance(x, StringType) 
Cuestiones relacionadas