在python中为字符串和列表编写通用函数

时间:2012-01-19 19:32:16

标签: python string list type-conversion

所以我像草一样绿色,从How to think like a computer scientist: Learn python 3学习编程。我能够回答这个问题(见下文),但我担心我错过了这一课。

编写一个函数(称为insert_at_end),它将为所有三个函数传递(返回前面给出两个参数的粗体):

test(insert_at_end(5, [1, 3, 4, 6]), **[1, 3, 4, 6, 5]**)
test(insert_at_end('x', 'abc'),  **'abcx'**)
test(insert_at_end(5, (1, 3, 4, 6)), **(1, 3, 4, 6, 5)**)

本书给出了这样的提示:“这些练习很好地说明了序列抽象是通用的,(因为切片,索引和连接是如此通用),因此可以编写一般函数适用于所有序列类型。“。

这个版本没有在线解决方案(我可以找到),但在我发现某人对该文本的先前版本(对于python 2.7)的答案时,他们这样做了:

def encapsulate(val, seq):
    if type(seq) == type(""):
        return str(val)
    if type(seq) == type([]):
        return [val]
    return (val,)

def insert_at_end(val, seq): 
    return seq + encapsulate(val, seq)

这似乎是通过区分列表和字符串来解决问题......违背提示。那怎么样有没有办法回答这个问题(还有大约10个类似问题)而没有区分?即不使用“type()”

9 个答案:

答案 0 :(得分:2)

我说这个例子不是对称的,这意味着它要求读者处理两种不同的情况:

  • int,list
  • str,str

在我看来,练习应该要求实施:

  • list,list:insert_at_end([5],[1,3,4,6])
  • str,str:insert_at_end(' x',' abc')

在这种情况下,阅读器必须只使用两个使用相同序列类型的参数,并且提示更有意义。

答案 1 :(得分:2)

我尽力而为:

def insert_at_end(val, seq):
    t = type(seq)
    try:
        return seq + t(val)
    except TypeError:
        return seq + t([val])

这将尝试创建type(seq)的序列,如果val不可迭代,则生成列表并连接。

答案 2 :(得分:1)

该问题是一个长列表之一,提示适用于所有这些问题。我认为编写encapsulate函数可以重用insert_at_front之类的函数是合理的,其余的实现是类型无关的。

但是,我认为encapsulate的更好实现可能是:

def encapsulate(val, seq):
    if isinstance(seq, basestring):
        return val
    return type(seq)([val])

使用更少的代码处理更多类型。

答案 3 :(得分:1)

不是解决方案,而是解释为什么真正优雅的解决方案看起来不可能。

  • +连接序列,但只连接相同类型的序列。
  • 作为第一个参数传递给insert_at_end的值是'标量',因此您必须将它们转换为第二个参数所具有的序列类型。
  • 要做到这一点,你不能简单地用标量参数调用序列构造函数并创建该类型的单项序列:tuple(1)不起作用。
  • str与其他序列类型的工作方式不同:tuple(["a"])("a",)list(["a"])["a"],但str(["a"]))"['a']"而不是"a"

这使得+在这种情况下无效,即使您只需使用instanceof就可以轻松地构建一个给定类型的序列,而不是type()

您也不能使用切片分配,因为只有列表是可变的。

在这种情况下,@ Hamish的解决方案看起来最干净。

答案 4 :(得分:0)

这个问题的挑战(在Python 2.7中,我现在正在测试3.2验证)是seq的两种可能的输入类型是不可变的,并且你应该返回相同的类型传入。对于字符串,这不是一个问题,因为你可以这样做:

return seq + char

因为这将返回一个新字符串,它是输入序列和附加字符的串联,但这对列表或元组不起作用。您只能将列表连接到列表或将元组连接到元组。如果你想避免“类型”检查,你可以用这样的东西到达那里:

if hasattr(seq, 'append'): # List input.
  seq.append(char)
elif hasattr(seq, 'strip'): # String input.
  seq = seq + char
else: # Tuple
  seq = seq + (char,)

return seq

这与实际检查类型确实没什么不同,但确实避免直接使用type函数。

答案 5 :(得分:0)

此解决方案仍然需要一些单独的字符串代码而不是列表/元组,但它更简洁,不会检查特定类型。

def insert_at_end(val, seq):
    try:
        return seq + val
    except TypeError:   # unsupported operand type(s) for +
        return seq + type(seq)([val])

答案 6 :(得分:0)

也许这更接近答案:

def genappend(x, s):
    if isinstance(s, basestring):
        t = s[0:0].join
    else:
        t = type(s)
    lst = list(s)
    lst.append(x)
    return t(lst)

print genappend(5, [1,2,3,4])    
print genappend(5, (1,2,3,4))
print genappend('5', '1234')

也可能存在完全由用户定义的序列类型。只要可以转换为列表,它们也可以工作。这也有效:

print genappend('5', set('1234'))

答案 7 :(得分:0)

我同意这一点是item是否可以迭代。

所以我的解决方案就是这样:

def iterate(seq, item):
    for i in seq:
        yield i
    yield item

def insert_at_end(seq, item):
    if hasattr(item, '__iter__'):
        return seq + item
    else:
        return type(seq)(iterate(seq, item))

示例:

>>> insert_at_end('abc', 'x')
'abcx'
>>> insert_at_end([1, 2, 4, 6], 5)
[1, 2, 4, 6, 5]
>>> insert_at_end((1, 2, 4, 6), 5)
(1, 2, 4, 6, 5)

由于insert_at_end可以处理iterable而不是,因此即使使用:

也能正常工作
>>> insert_at_end('abc', 'xyz')
'abcxyz'
>>> insert_at_end([1, 2, 4, 6], [5, 7])
[1, 2, 4, 6, 5, 7]
>>> insert_at_end((1, 2, 4, 6), (5, 7))
(1, 2, 4, 6, 5, 7)

答案 8 :(得分:-1)

虽然封装依赖于类型,但是直接在insert_at_end中的代码却没有,并依赖于所有3种类型的+含义相关的东西,因此从这个意义上讲,它符合提示。