部分列表在Python中解压

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

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等于“无”或没有设置? 一个简单的方法看起来很长:

 a = b = None if "=" in string : a, b = string.split("=") else : a = string 
 # 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] 

这对你来说可能是没有用的,除非你使用Python 3.不过,为了完整起见,值得注意的是,在那里引入的扩展元组解包允许你做这样的事情:

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

也就是说,现在解包就像解开参数一样工作,所以你可以用*来表示“剩下的项目”,并把它们作为一个(可能是空的)列表。

分区可能是你正在做的最好的解决scheme。

这比你的解决scheme稍微好一点,但还不是很优雅; 如果有更好的方法去做,我也不会感到惊讶。

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

最好的方法是使用分区string方法 :

在sep的第一个出现处分割string,并返回包含分隔符之前的部分,分隔符本身和分隔符之后的部分的三元组。 如果找不到分隔符,则返回包含string本身的三元组,然后返回两个空string。

2.5版本中的新function

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

它也适用于不包含=string:

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

如果出于某种原因,您使用的是2.5之前的Python版本,则可以使用列表分片来完成相同的操作:

 >>> 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', '') 

你可以写一个辅助函数来做到这一点。

 >>> 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) 

但有时我不知道右边的列表大小,例如,如果我使用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。)

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

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

已经提出了许多其他的解决scheme,但是我不得不说,对我来说最直接的依然是

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

或者,也许使用正则expression式?

 >>> 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() 

我不build议使用这个,但只是为了好玩,这里有一些代码实际上是你想要的。 当你调用unpack(<sequence>)unpack函数使用inspect模块来查找调用该函数的实际源代码行,然后使用ast模块parsing该行并计算被解压缩的variables的数量。

注意事项:

  • 对于多重赋值(例如(a,b) = c = unpack([1,2,3]) ),它只使用赋值中的第一项
  • 如果找不到源代码,它将不起作用(例如,因为你是从repl中调用它的)
  • 如果赋值语句跨越多行,它将不起作用

码:

 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.") 

你尝试过吗?

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