Подтвердить что ты не робот

Распаковать частичный список в 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

Можно ли как-то распаковать список в приведенном выше примере, чтобы получить a = "DEFAULT_LENGTH", а b равно "None" или не установлен? Простой способ выглядит долго:

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

Ответ 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]

Ответ 2

Это может быть бесполезно для вас, если вы не используете Python 3. Однако для полноты, стоит отметить, что расширенный набор кортежей, который позволяет вам делать такие вещи, как:

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

т.е. теперь распаковка распаковки теперь работает аналогично тому, как это происходит при распаковке аргументов, поэтому вы можете обозначить "остальную часть элементов" с помощью * и получить их как (возможно, пустой) список.

Разделение, вероятно, является лучшим решением для того, что вы делаете.

Ответ 3

Это немного лучше, чем ваше решение, но все еще не очень элегантно; это не удивило бы меня, если бы был лучший способ сделать это.

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

Ответ 4

Самый приятный способ - метод строки разделов:

Разделите строку при первом вводе 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', '')

Ответ 5

Вы можете написать вспомогательную функцию, чтобы сделать это.

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

Ответ 6

Но иногда я не знаю размер списка справа, например, если я использую 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, если хотите.

Ответ 7

Не используйте этот код, это подразумевается как шутка, но он делает то, что вы хотите:

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

Ответ 8

Было предложено много других решений, но я должен сказать, что самый простой для меня все еще

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

Ответ 9

В качестве альтернативы, возможно, используйте регулярное выражение?

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

Ответ 10

Я не рекомендую использовать это, но просто для удовольствия здесь какой-то код, который действительно делает то, что вы хотите. Когда вы вызываете unpack(<sequence>), функция unpack использует модуль inspect, чтобы найти фактическую строку источника, где была вызвана функция, затем использует модуль ast для разбора этой строки и подсчета количества распакованных переменных.

Предостережения:

  • Для множественного присвоения (например, (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 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.")

Ответ 11

Вы пробовали это?

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