Питонский способ проверки параметра-это последовательность, но не строка
У меня есть функция, которая получает список таблиц БД в качестве параметра и возвращает командную строку для выполнения на этих таблицах, например:
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 предлагает использовать утверждения типа, но я не уверен, что он достаточно Питонский, потому что он нарушает соглашение типа утки.
Какие-нибудь идеи?
Адам
4 ответа:
Утверждение типа кажется уместным в этом случае-обработка распространенного неправильного использования, которое кажется законным из-за типизации утки.
Еще один способ обработки этого общего случая-проверить строку и правильно обработать ее как частный случай.
Наконец, можно было бы рекомендовать передавать имена таблиц в качестве позиционных параметров, что сделало бы этот сценарий менее вероятным:
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')
Вы можете использовать азбуку, чтобы утверждать, что объект является итерационным, но не строкой:
from types import StringType from collections import Iterable assert isinstance(x, Iterable) and not isinstance(x, StringType)
Распространенная идиома 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]
.