部分列表在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