2009-04-14 51 views
38

Trong Python, toán tử gán có thể giải nén danh sách hoặc tuple vào biến, như thế này:danh sách phần giải nén bằng Python

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

Nhưng tôi cần phải xác định chính xác cùng một lượng tên sang trái như là một mục đếm trong danh sách ở bên phải. Nhưng đôi khi tôi không biết kích thước của danh sách ở bên phải, ví dụ nếu tôi sử dụng split(). Ví dụ:

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

Nhưng đoạn mã sau sẽ dẫn đến một lỗi:

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

Có thể bằng cách nào đó giải nén danh sách trong ví dụ trên vì vậy tôi có được một = "DEFAULT_LENGTH" và b tương đương với ' Không có 'hoặc không được đặt? Một cách đơn giản trông khá dài:

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

https://stackoverflow.com/questions/5333680/extended-tuple-unpacking-in-python-2 chung – n611x007

Trả lời

46
# 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

Bạn đã thử cái này chưa?

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

Phải là a = values ​​[0] –

+0

hoặc "a, = values" –

7

Điều này tốt hơn một chút so với giải pháp của bạn nhưng vẫn không rất thanh lịch; nó sẽ không làm tôi ngạc nhiên nếu có một cách tốt hơn để làm điều đó.

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

Nice. Về cơ bản là phiên bản phân vùng được tạo tại nhà. –

4

Bạn có thể viết một hàm helper để làm điều đó.

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

Không sử dụng mã này, nó có nghĩa là như một trò đùa, nhưng nó làm những gì bạn muốn:

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

Chỉ cần đợi cho đến khi ai đó cố gắng mở rộng nó để làm việc cho 3 biến mặc dù (hoặc sử dụng python3)! Đặt rằng trong mã của bạn một người nào đó có thể đọc sẽ là khá ác :-) Một cách tiếp cận lành mạnh hơn là có thể chỉ cần đặt a theString trong khối ngoại trừ. – Brian

6

Cách đẹp nhất đang sử dụng partition string method:

Split the string at the first occurrence of sep, and return a 3-tuple containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return a 3-tuple containing the string itself, followed by two empty strings.

New in version 2.5.

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

Nó cũng hoạt động cho các chuỗi không chứa =:

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

Nếu vì một lý do nào bạn đang sử dụng một phiên bản của Python trước 2.5, bạn có thể sử dụng danh sách-cắt làm nhiều như vậy, nếu một chút ít gọn gàng:

>>> x = "DEFAULT_LENGTH" 

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

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

..và khi x = "length=25":

('length', '25') 

một cách dễ dàng biến thành một chức năng hay lambda:

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

Tôi thích câu trả lời này tốt hơn câu trả lời đã chọn vì nó thực sự giải thích phân vùng nào có. Ký hiệu không trực quan 100% ngay từ cái nhìn đầu tiên. –

50

Điều này có thể sử dụng không cho bạn trừ khi bạn đang sử dụng Python 3. Tuy nhiên, cho đầy đủ, nó có giá trị lưu ý rằng extended tuple unpacking giới thiệu có cho phép bạn làm những việc như:

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

Tức là tuple giải nén bây giờ hoạt động tương tự như làm thế nào nó trong giải nén đối số, vì vậy bạn có thể biểu thị "phần còn lại của các mục" với *, và nhận được chúng như là một (có thể có sản phẩm nào) danh sách.

Phân vùng có lẽ là giải pháp tốt nhất cho những gì bạn đang làm.

+2

py2 thay thế? – n611x007

+0

Đây là câu trả lời tôi muốn, cảm ơn –

1

But sometimes I don't know a size of the list to the right, for example if I use split().

Yeah, khi tôi đã có trường hợp với giới hạn> 1 (vì vậy tôi không thể sử dụng phân vùng) Tôi thường đầy đặn cho:

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] là có để có được một str rỗng' 'hoặc 'unicode', phù hợp với bất cứ những sự chia rẽ được sản xuất. bạn có thể sử dụng None nếu bạn thích.)

0

Nhiều giải pháp khác đã được đề xuất, nhưng tôi phải nói rằng đơn giản nhất đối với tôi vẫn là

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

Thay thế, có thể sử dụng cụm từ thông dụng?

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

Nếu bạn chắc chắn rằng re.match() luôn luôn thành công, .groups() sẽ luôn luôn trả đúng số yếu tố để giải nén vào tuple của bạn, vì vậy bạn có thể thực hiện một cách an toàn

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

Tôi không khuyên bạn nên sử dụng này, nhưng chỉ cho vui đây là một số mã mà thực sự làm những gì bạn muốn. Khi bạn gọi unpack(<sequence>), chức năng unpack sử dụng mô-đun inspect để tìm dòng thực tế của nguồn mà hàm được gọi, sau đó sử dụng mô-đun ast để phân tích dòng đó và đếm số biến được giải nén.

Hãy cẩn thận:

  • Đối với nhiều nhiệm vụ (ví dụ (a,b) = c = unpack([1,2,3])), nó chỉ sử dụng thuật ngữ đầu tiên trong việc giao
  • Nó sẽ không hoạt động nếu nó không thể tìm mã nguồn (ví dụ bởi vì bạn đang gọi nó từ repl)
  • nó sẽ không hoạt động nếu lệnh gán kéo dài nhiều dòng

Code:

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.") 
Các vấn đề liên quan