部分列表在Python中解压缩

时间:2009-04-14 19:44:55

标签: python

在Python中,赋值运算符可以将列表或元组解包为变量,如下所示:

l = (1, 2)
a, b = l # Here goes auto unpack

但我需要在左侧指定与左侧列表中的项目计数完全相同的名称。但有时候我不知道右边列表的大小,例如,如果我使用split()。

示例:

a, b = "length=25".split("=") # This will result in a="length" and b=25

但是以下代码会导致错误:

a, b = "DEFAULT_LENGTH".split("=") # Error, list has only one item

是否有可能以某种方式解压缩上面示例中的列表,这样我就可以得到一个=“DEFAULT_LENGTH”并且b等于None或者没有设置?直截了当的方式看起来有点长:

a = b = None
if "=" in string :
  a, b = string.split("=")
else :
  a = string

11 个答案:

答案 0 :(得分:56)

除非你使用的是Python 3,否则这对你没用。但是,为了完整起见,值得注意的是那里引入的extended tuple unpacking允许你做以下事情:

>>> a, *b = "length=25".split("=")
>>> a,b
("length", ['25'])
>>> a, *b = "DEFAULT_LENGTH".split("=")
>>> a,b
("DEFAULT_LENGTH", [])

即。现在,元组解包与它在参数解包中的工作方式类似,因此您可以使用*表示“其余项目”,并将它们作为(可能为空)列表。

分区可能是您正在做的最佳解决方案。

答案 1 :(得分:48)

# this will result in a="length" and b="25"
a, b = "length=25".partition("=")[::2]

# this will result in a="DEFAULT_LENGTH" and b=""
a, b = "DEFAULT_LENGTH".partition("=")[::2]

答案 2 :(得分:7)

这比你的解决方案略胜一筹,但仍然不是很优雅;如果有更好的方法,我不会感到惊讶。

a, b = (string.split("=") + [None])[:2]

答案 3 :(得分:6)

最好的方法是使用partition string method

  

在第一次出现sep时拆分字符串,并返回包含分隔符之前的部分的3元组,分隔符本身以及分隔符之后的部分。如果找不到分隔符,则返回一个包含字符串本身的3元组,后跟两个空字符串。

     

2.5版中的新功能。

>>> inputstr = "length=25"
>>> inputstr.partition("=")
('length', '=', '25')
>>> name, _, value = inputstr.partition("=")
>>> print name, value
length 25

它也适用于不包含=的字符串:

>>> inputstr = "DEFAULT_VALUE"
>>> inputstr.partition("=")
('DEFAULT_VALUE', '', '')

如果由于某种原因你在2.5之前使用的是Python版本,你可以使用list-slicing来做同样的事情,如果稍微不整齐:

>>> x = "DEFAULT_LENGTH"

>>> a = x.split("=")[0]
>>> b = "=".join(x.split("=")[1:])

>>> print (a, b)
('DEFAULT_LENGTH', '')

..以及x = "length=25"时:

('length', '25')

轻松变成函数或lambda:

>>> part = lambda x: (x.split("=")[0], "=".join(x.split("=")[1:]))
>>> part("length=25")
('length', '25')
>>> part('DEFAULT_LENGTH')
('DEFAULT_LENGTH', '')

答案 4 :(得分:4)

您可以编写辅助函数来执行此操作。

>>> def pack(values, size):
...     if len(values) >= size:
...         return values[:size]
...     return values + [None] * (size - len(values))
...
>>> a, b = pack('a:b:c'.split(':'), 2)
>>> a, b
('a', 'b')
>>> a, b = pack('a'.split(':'), 2)
>>> a, b
('a', None)

答案 5 :(得分:1)

  

但有时候我不知道右边列表的大小,例如我是否使用split()。

是的,当我有限制> 1的情况下(所以我不能使用分区)我通常会喜欢:

def paddedsplit(s, find, limit):
    parts= s.split(find, limit)
    return parts+[parts[0][:0]]*(limit+1-len(parts))

username, password, hash= paddedsplit(credentials, ':', 2)

parts[0][:0]可以得到一个空的'str'或'unicode',匹配分割产生的那些。如果你愿意,可以使用None。)

答案 6 :(得分:0)

不要使用这个代码,它是一个笑话,但它可以做你想要的:

a = b = None
try: a, b = [a for a in 'DEFAULT_LENGTH'.split('=')]
except: pass

答案 7 :(得分:0)

已经提出了许多其他解决方案,但我必须说对我来说最直接的仍然是

a, b = string.split("=") if "=" in string else (string, None)

答案 8 :(得分:0)

作为替代方案,也许使用正则表达式?

>>> import re
>>> unpack_re = re.compile("(\w*)(?:=(\w*))?")

>>> x = "DEFAULT_LENGTH"
>>> unpack_re.match(x).groups()
('DEFAULT_LENGTH', None)

>>> y = "length=107"
>>> unpack_re.match(y).groups()
('length', '107')

如果确保re.match()始终成功,.groups()将始终返回正确数量的元素以解压缩到您的元组中,这样您就可以安全地进行

a,b = unpack_re.match(x).groups()

答案 9 :(得分:0)

我不建议使用此功能,但只是为了好玩,这里有一些代码可以实现您想要的功能。当您调用unpack(<sequence>)时,unpack函数使用inspect模块查找调用函数的实际源代码行,然后使用ast模块解析该行并计算要解压缩的变量数。

注意事项:

  • 对于多项作业(例如(a,b) = c = unpack([1,2,3])),它仅使用作业中的第一项
  • 如果无法找到源代码,则无效(例如,因为您是从代理中调用的)
  • 如果赋值语句跨越多行
  • ,则无效

代码:

import inspect, ast
from itertools import islice, chain, cycle

def iter_n(iterator, n, default=None):
    return islice(chain(iterator, cycle([default])), n)

def unpack(sequence, default=None):
    stack = inspect.stack()
    try:
        frame = stack[1][0]
        source = inspect.getsource(inspect.getmodule(frame)).splitlines()
        line = source[frame.f_lineno-1].strip()
        try:
            tree = ast.parse(line, 'whatever', 'exec')
        except SyntaxError:
            return tuple(sequence)
        exp = tree.body[0]
        if not isinstance(exp, ast.Assign):
            return tuple(sequence)
        exp = exp.targets[0]
        if not isinstance(exp, ast.Tuple):
            return tuple(sequence)
        n_items = len(exp.elts)
        return tuple(iter_n(sequence, n_items, default))
    finally:
        del stack

# Examples
if __name__ == '__main__':
    # Extra items are discarded
    x, y = unpack([1,2,3,4,5])
    assert (x,y) == (1,2)
    # Missing items become None
    x, y, z = unpack([9])
    assert (x, y, z) == (9, None, None)
    # Or the default you provide
    x, y, z = unpack([1], 'foo')
    assert (x, y, z) == (1, 'foo', 'foo')
    # unpack() is equivalent to tuple() if it's not part of an assignment
    assert unpack('abc') == ('a', 'b', 'c')
    # Or if it's part of an assignment that isn't sequence-unpacking
    x = unpack([1,2,3])
    assert x == (1,2,3)
    # Add a comma to force tuple assignment:
    x, = unpack([1,2,3])
    assert x == 1
    # unpack only uses the first assignment target
    # So in this case, unpack('foobar') returns tuple('foo')
    (x, y, z) = t = unpack('foobar')
    assert (x, y, z) == t == ('f', 'o', 'o')
    # But in this case, it returns tuple('foobar')
    try:
        t = (x, y, z) = unpack('foobar')
    except ValueError as e:
        assert str(e) == 'too many values to unpack'
    else:
        raise Exception("That should have failed.")
    # Also, it won't work if the call spans multiple lines, because it only
    # inspects the actual line where the call happens:
    try:
        (x, y, z) = unpack([
            1, 2, 3, 4])
    except ValueError as e:
        assert str(e) == 'too many values to unpack'
    else:
        raise Exception("That should have failed.")

答案 10 :(得分:-2)

你试过这个吗?

values = aString.split("=")
if len(values) == 1:
   a = values[0]
else:
   a, b = values