Pythonic 方法验证参数是序列而非字符串

7 投票
5 回答
1194 浏览
提问于 2025-04-16 07:24

我有一个函数,它接收一个数据库表的列表作为参数,然后返回一个要在这些表上执行的命令字符串,比如:

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

因为字符串本身是可以被迭代的。

这个问题建议使用断言来检查类型,但我不确定这样做是否符合Python的风格,因为这打破了“鸭子类型”的原则。

有什么见解吗?

亚当

5 个回答

1

在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]来实现。

2

你可以使用ABCs(抽象基类)来确认一个对象是可迭代的,但它不是字符串:

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

在这种情况下,确认类型是合适的,因为这可以处理一个常见的误用问题,这种误用看起来是合法的,主要是因为鸭子类型的特性。

另一种处理这种常见情况的方法是检查是否是字符串,并将其作为特殊情况正确处理。

最后,你可以建议将表名作为位置参数传递,这样就不太可能出现这种情况:

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')

撰写回答