How does this lambda/yield/generator comprehension work?

后端 未结 2 441
时光说笑
时光说笑 2020-12-23 02:19

I was looking through my codebase today and found this:

def optionsToArgs(options, separator=\'=\'):
    kvs = [
        (
            \"%(option)s%(separato         


        
2条回答
  •  有刺的猬
    2020-12-23 02:35

    Since Python 2.5, yield is an expression, not a statement. See PEP 342.

    The code is hideously and unnecessarily ugly, but it's legal. Its central trick is using f((yield x)) inside the generator expression. Here's a simpler example of how this works:

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

    Basically, using yield in the generator expression causes it to produce two values for every value in the source iterable. As the generator expression iterates over the list of strings, on each iteration, the yield x first yields a string from the list. The target expression of the genexp is f((yield x)), so for every value in the list, the "result" of the generator expression is the value of f((yield x)). But f just ignores its argument and always returns the option string "-o". So on every step through the generator, it yields first the key-value string (e.g., "x=1"), then "-o". The outer list(reversed(list(...))) just makes a list out of this generator and then reverses it so that the "-o"s will come before each option instead of after.

    However, there is no reason to do it this way. There are a number of much more readable alternatives. Perhaps the most explicit is simply:

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

    Even if you like terse, "clever" code, you could still just do

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

    The kvs list comprehension itself is a bizarre mix of attempted readability and unreadability. It is more simply written:

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

    You should consider arranging an "intervention" for whoever put this in your codebase.

提交回复
热议问题