Питонский способ проверки параметра-это последовательность, но не строка


У меня есть функция, которая получает список таблиц БД в качестве параметра и возвращает командную строку для выполнения на этих таблицах, например:

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 6

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].

Нельзя ли использовать список, а не Кортеж?

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