Ответ 1
# 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 оператор присваивания может распаковать список или кортеж в переменные, например:
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
Можно ли как-то распаковать список в приведенном выше примере, чтобы получить a = "DEFAULT_LENGTH", а b равно "None" или не установлен? Простой способ выглядит долго:
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", [])
т.е. теперь распаковка распаковки теперь работает аналогично тому, как это происходит при распаковке аргументов, поэтому вы можете обозначить "остальную часть элементов" с помощью *
и получить их как (возможно, пустой) список.
Разделение, вероятно, является лучшим решением для того, что вы делаете.
Это немного лучше, чем ваше решение, но все еще не очень элегантно; это не удивило бы меня, если бы был лучший способ сделать это.
a, b = (string.split("=") + [None])[:2]
Самый приятный способ - метод строки разделов:
Разделите строку при первом вводе 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', '', '')
Если по какой-то причине вы используете версию Python до 2.5, вы можете использовать list-slicing, чтобы сделать то же самое, если немного менее аккуратно:
>>> x = "DEFAULT_LENGTH"
>>> a = x.split("=")[0]
>>> b = "=".join(x.split("=")[1:])
>>> print (a, b)
('DEFAULT_LENGTH', '')
.. и когда x = "length=25"
:
('length', '25')
Легко превращается в функцию или лямбда:
>>> 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
Было предложено много других решений, но я должен сказать, что самый простой для меня все еще
a, b = string.split("=") if "=" in string else (string, None)
В качестве альтернативы, возможно, используйте регулярное выражение?
>>> 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()
Я не рекомендую использовать это, но просто для удовольствия здесь какой-то код, который действительно делает то, что вы хотите. Когда вы вызываете 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 not part of an assignment
assert unpack('abc') == ('a', 'b', 'c')
# Or if it 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