代码之家  ›  专栏  ›  技术社区  ›  Adam Matan

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

  •  6
  • Adam Matan  · 技术社区  · 14 年前

    我有一个函数,它获取数据库表列表作为参数,并返回要在这些表上执行的命令字符串,例如:

    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
    

    因为字符串本身正在被迭代。

    This SO question 建议对类型使用asserts,但我不确定它是否足够python,因为它违反了duck类型约定。

    有什么见解吗?

    亚当

    4 回复  |  直到 7 年前
        1
  •  6
  •   orip    14 年前

    在这种情况下断言类型似乎是合适的——处理一个常见的滥用,因为duck类型看起来是合法的。

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

    最后,您可以鼓励将表名作为位置参数传递,这样就不太可能发生这种情况:

    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
  •  2
  •   Katriel    14 年前

    可以使用ABCs断言对象是iterable而不是string:

    from types import StringType
    from collections import Iterable
    assert isinstance(x, Iterable) and not isinstance(x, StringType)
    
        3
  •  1
  •   martineau    14 年前

    检测参数是序列(列表或元组)还是字符串的常用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
  •  0
  •   grifaton    14 年前

    你不能用列表而不是元组吗?

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