【问题标题】:Single string or list of strings in a method [duplicate]方法中的单个字符串或字符串列表[重复]
【发布时间】:2011-12-11 21:18:04
【问题描述】:

我已经遇到过好几次了。我正在处理很多可以接受字符串列表的方法。有几次我不小心传递了一个字符串,它被分解成一个列表,每个字符都被使用,这不是我们想要的行为。

def test(a,b):
    x = []
    x.extend(a)
    x.extend(b)
    return x

x = [1,2,3,4]

我不想发生的事情:

test(x,'test')
[1, 2, 3, 4, 't', 'e', 's', 't']

我不得不求助于一种奇怪的语法:

test(x,['list']) 

我希望这些隐式工作:

test(x,'list')
[1, 2, 3, 4, 'test']

test(x,['one', 'two', 'three'])
[1, 2, 3, 4, 'one', 'two', 'three']

我真的觉得有一种“pythonic”的方法可以做到这一点或涉及鸭子打字的东西,但我没有看到。我知道我可以使用 isinstance() 来检查它是否是一个字符串,但我觉得有更好的方法。

编辑:我使用的是 python 2.4.3

【问题讨论】:

  • 重新解释list.extend() 的预期行为以克服与意外 输入相关的问题可能不是一个好主意。

标签: python


【解决方案1】:

使用这个

def test( x, *args ):

现在你可以做

test( x, 'one' )

test( x, 'one', 'two' )

test( x, *['one', 'two',] )

【讨论】:

  • @NullUserExceptionఠ_ఠ:你是对的。单元测试比这个级别的语法废话更重要。但是,我想我会采用“无单元测试”的方法,因为问题似乎暗示了这一点。
  • @NullUserException: Downvotes 表示“这是错误的”或“这在给定的上下文中没有帮助”。他们的意思不是“我在风格上不同意”。这个答案肯定很有帮助。
  • @SvenMarnach 是的,我删除了反对票。但我仍然不喜欢这种方法。
  • +1 使用 API 设计去除歧义是一个非常合理和干净的解决方案。
【解决方案2】:

我很抱歉这么说,但这确实是使用isinstance() 有意义的少数情况之一。然而,建议对 list 进行测试的其他答案之一是倒退:str(以及 Python 2.x 的 unicode)是异常类型,因此您要测试这些类型:

def test(a,b):
    x = []
    if isinstance(b, str): b = [b]
    x.extend(a)
    x.extend(b)
    return x

或用于 Python 2.x 测试 isinstance(b, basestring)

【讨论】:

  • 我有几个实例,其中我传递了一个参数,其中列表是字典键列表(作为字符串)。通过 'test' 将尝试查找 mydict['t']、mydict['e'] 等。也许 isinstance 最适合上面的示例。这无疑是查看意图的最简单方法。
  • 更进一步,API 应该提供一个默认参数,以便调用者可以决定哪些可迭代类型被认为是原子的。这避免了硬连线行为。 def test(a, b, atomic=(basestring, buffer)): ...
【解决方案3】:

来自The Zen of Python

Explicit is better than implicit.

在我看来,你的第一个例子是明确的。它采用两个值并以易于理解的方式处理它们。尽管对于新的 Python 程序员来说它“感觉”有点奇怪,但它的行为就像期望一样。 list.extend 接受列表,因此它将字符串视为字符列表。

您的提议将list.extend 的语义更改为等同于list.append,但仅在处理字符串时。对于那些希望你的函数将你的字符串视为字符列表并且不得不像test(x,list('test')) 这样调用它的人来说,这将是一个真正的惊喜。

可以按照您的要求做,但请不要这样做。 :-)

【讨论】:

  • 同意。如果可以避免,最好不要对输入强加明确的类型要求。
  • 我认为 pod2metra 的响应满足了这一点,你不同意吗?它利用__iter__ 属性并且不强制执行任何特定类型。
  • 我没有任何问题。
【解决方案4】:

嗯,这很简单:

>>> def a(*args):
...     x=[]
...     for i in args:
...             if '__iter__' in dir(i):
...                     x+=list(i)
...             else:
...                     x.append(i)
...     return x
... 
>>> a(1,2,3,4)
[1, 2, 3, 4]
>>> a(1,2,[3,4])
[1, 2, 3, 4]
>>> a(1,2,[3,4],'123')
[1, 2, 3, 4, '123']
>>> a(1,2,[3,4],'123', 1231, (1, 2, 3, 4))
[1, 2, 3, 4, '123', 1231, 1, 2, 3, 4]

【讨论】:

  • 我什至没有想过要偷看 dir() 看看它是否包含 iter。这很棒。
  • 在 python3 上这会失败,因为字符串有 'iter'
  • 为了 2/3 的兼容性,使用six,它提供了一种跨版本的方式来检查字符串类型:if isinstance(value, six.string_types): value = [value]
【解决方案5】:

对 pod2metra 的回答稍作调整,即可使该函数能够处理所有级别的湮灭:

>>> def b(*args):
...     x=[]
...     for i in args:
...             if '__iter__' in dir(i):
...                     x += b(*i)
...             else:
...                     x.append(i)
...     return x
... 
>>> b(1, (2, (3, 4)))
[1, 2, 3, 4]

你有函数'a':

>>> a(1, (2, (3, 4)))
[1, 2, (3, 4)]

我知道这不是最初问题的意图,但也许这种概括可能有用。

【讨论】:

    【解决方案6】:

    简单测试参数的类型:

    def test(a,b):
        x = a if isinstance(a, list) else [a]
        x += b if isinstance(b, list) else [b]
        return x
    

    【讨论】:

    • -1 这正是 OP 想要避免的。
    • 另外,我使用的是 python 2.4.3(归咎于 RedHat 的低版本)。 “如果”的这种用法似乎不起作用。
    • @user179256 它没有。此语法是在 2.5 上添加的
    • 我刚刚注意到上面的代码似乎修改了变量'a',因为当a是一个列表时使用了x = a。我认为当使用 x = [a] 时,不会修改 a 方法的外部。
    猜你喜欢
    • 2015-09-11
    • 2014-03-30
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 2010-11-17
    • 2019-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多