2013-04-11 50 views
49

tôi đang tìm kiếm thông qua codebase của tôi ngày hôm nay và thấy điều này:Tính năng hiểu lambda/năng suất/máy phát điện này hoạt động như thế nào?

def optionsToArgs(options, separator='='): 
    kvs = [ 
     (
      "%(option)s%(separator)s%(value)s" % 
      {'option' : str(k), 'separator' : separator, 'value' : str(v)} 
     ) for k, v in options.items() 
    ] 
    return list(
     reversed(
      list(
        (lambda l, t: 
         (lambda f: 
          (f((yield x)) for x in l) 
         )(lambda _: t) 
        )(kvs, '-o') 
       ) 
      ) 
     ) 

Có vẻ như để có một dict các thông số và biến chúng thành một danh sách các tham số cho một lệnh shell. Có vẻ như nó đang sử dụng năng suất bên trong một máy phát hiểu, mà tôi nghĩ là không thể ...?

>>> optionsToArgs({"x":1,"y":2,"z":3}) 
['-o', 'z=3', '-o', 'x=1', '-o', 'y=2'] 

Cách hoạt động?

+66

Dang. Nói về mã không đọc được. – BenDundee

+2

phần hài hước nhất là danh sách (đảo ngược (danh sách ('một phần để có được công tắc' -o', mặc dù – ch3ka

+5

Ngoài ra tất cả các lambdas có thể chỉ là '((lambda _: '-o') ((yield x)) cho x trong kvs) ' –

Trả lời

47

Kể từ Python 2.5, yield <value> là một biểu thức, không phải là câu lệnh. Xem PEP 342.

Mã rất ghê gớm và không cần thiết xấu xí, nhưng nó là hợp pháp. Thủ thuật trung tâm của nó là sử dụng f((yield x)) bên trong biểu thức trình tạo. Dưới đây là một ví dụ đơn giản về cách làm việc này:

>>> def f(val): 
...  return "Hi" 
>>> x = [1, 2, 3] 
>>> list(f((yield a)) for a in x) 
[1, 'Hi', 2, 'Hi', 3, 'Hi'] 

Về cơ bản, sử dụng yield trong biểu thức máy phát điện gây ra nó để tạo ra hai giá trị cho mọi giá trị trong nguồn iterable. Khi biểu thức máy phát lặp lại qua danh sách các chuỗi, trên mỗi lần lặp lại, trước tiên, yield x mang lại một chuỗi từ danh sách. Biểu thức đích của genexp là f((yield x)), vì vậy đối với mọi giá trị trong danh sách, "kết quả" của biểu thức trình tạo là giá trị của f((yield x)). Nhưng f chỉ bỏ qua đối số của nó và luôn trả về chuỗi tùy chọn "-o". Vì vậy, trên mỗi bước thông qua trình tạo, trước tiên nó tạo ra chuỗi khóa-giá trị (ví dụ: "x=1"), sau đó "-o". Các bên ngoài list(reversed(list(...))) chỉ cần làm cho một danh sách ra khỏi máy phát điện này và sau đó đảo ngược nó để "-o" s sẽ đến trước mỗi tùy chọn thay vì sau.

Tuy nhiên, không có lý do nào để thực hiện theo cách này. Có một số lựa chọn thay thế dễ đọc hơn nhiều. Có lẽ rõ ràng nhất chỉ đơn giản là:

kvs = [...] # same list comprehension can be used for this part 
result = [] 
for keyval in kvs: 
    result.append("-o") 
    result.append(keyval) 
return result 

Thậm chí nếu bạn thích ngắn gọn, "thông minh" mã, bạn có thể vẫn chỉ là làm

return sum([["-o", keyval] for keyval in kvs], []) 

Danh sách kvs hiểu chính nó là một sự pha trộn kỳ lạ của khả năng đọc cố gắng và không thể đọc được. Nó được viết đơn giản hơn:

kvs = [str(optName) + separator + str(optValue) for optName, optValue in options.items()] 

Bạn nên cân nhắc sắp xếp "can thiệp" cho bất kỳ ai đặt mã này vào trong codebase của bạn.

+3

trong lịch sử, nó từng là: 'danh sách trả về (itertools.chain (* [['- o', v] cho v trong kvs]))'. Không rõ tại sao nó lại bị thay đổi từ điều này. – Dog

+1

@Dog thay đổi tôi muốn làm với mã trong nhận xét của bạn là sử dụng 'itertools.chain.from_iterable' để tránh sử dụng '*' (có thể trở nên đắt nếu danh sách lớn) ... – Bakuriu

19

Chúa ơi. Về cơ bản, nó nắm này ,:

def f(_):    # I'm the lambda _: t 
    return '-o' 

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     yield f((yield x)) 

Vì vậy, khi lặp kết thúc, thegenerator sản lượng x (thành viên của kvs) và sau đó giá trị trả về của f, mà luôn luôn là -o, tất cả trong một lần lặp trên kvs. Bất cứ điều gì yield x trả về và những gì được chuyển đến f bị bỏ qua.

Tương đương:

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     whatever = (yield x) 
     yield f(whatever) 

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     yield x 
     yield f(None) 

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     yield x 
     yield '-o' 

Có rất nhiều cách để làm điều này đơn giản hơn nhiều, tất nhiên.Ngay cả với mẹo gấp đôi lợi nhuận ban đầu, toàn bộ điều có thể là

return list(((lambda _: '-o')((yield x)) for x in kvs))[::-1] 
Các vấn đề liên quan