Pythonic 方法验证参数是序列而非字符串
我有一个函数,它接收一个数据库表的列表作为参数,然后返回一个要在这些表上执行的命令字符串,比如:
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')