Pythonic способ проверки параметра - это последовательность, но не строка
У меня есть функция, которая получает список таблиц DB в качестве параметра и возвращает строку команд, которая должна быть выполнена в этих таблицах, например:
pg_dump( file='/tmp/dump.sql',
tables=('stack', 'overflow'),
port=5434
name=europe)
Должен вернуть что-то вроде:
pg_dump -t stack -t overflow -f /tmp/dump.sql -p 5434 europe
Это делается с помощью tables_string='-t '+' -t '.join(tables)
.
Веселье начинается, когда вызывается функция: tables=('stackoverflow')
(строка) вместо tables=('stackoverflow',)
(кортеж), которая дает:
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
Поскольку сама строка повторяется.
Этот вопрос SO предлагает использовать утверждения по типу, но я не уверен, что он достаточно Pythonic, потому что он нарушает соглашение типа утки.
Любые идеи?
Адам
Ответы
Ответ 1
Утверждение типа кажется подходящим в этом случае - обращение с распространенным неправильным использованием, которое кажется законным из-за утиного ввода.
Еще один способ справиться с этим обычным случаем - проверить строку и корректно обрабатывать ее как особый случай.
Наконец, вы могли бы рекомендовать передавать имена таблиц в качестве позиционных параметров, которые сделали бы этот сценарий менее вероятным:
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')
Ответ 2
Вы можете использовать ABC, чтобы утверждать, что объект итерируется, но не строка:
from types import StringType
from collections import Iterable
assert isinstance(x, Iterable) and not isinstance(x, StringType)
Ответ 3
Общая идиома Python для определения того, является ли аргумент последовательностью (список или кортеж) или строкой, чтобы проверить, имеет ли он атрибут __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__
Если это не последовательность, то также можно изменить ее на единицу, чтобы упростить остальную часть кода (который должен иметь дело только с одним видом вещи). В образце это можно было бы сделать с помощью простого arg = [arg]
.
Ответ 4
Не можете ли вы использовать список, а не кортеж?
pg_dump( file='/tmp/dump.sql',
tables=['stack', 'overflow'],
port=5434,
name='europe')