Search code examples
pythonlist-comprehension

Understanding string variable substitution in a python list comprehension


I am not sure on the syntax being used in a list comprehension example in the Python Cookbook.

Code (with my print statements in) is below:

import html

# to accept any number of KEYWORD arguments, use **
# treat the argument as a dictionary
def make_element(name, value, **attrs):
    print(f"attrs has type {type(attrs)}")
    print(f"attrs -> {attrs}")
    print(f"attrs.items() -> {attrs.items()}")
    
    keyvals = ['%s = "%s"' % item for item in attrs.items()]
    
    print(f"keyvals -> {keyvals}")
    
    attr_str = ' '.join(keyvals)
    
    print(f"attr_str -> {attr_str}")
    
    element=f"<{name}{attr_str}>{html.escape(value)}</{name}>"
    
    print(f"element -> {element}")
    
    return element

The confusing line for me is:

keyvals = ['%s = "%s"' % item for item in attrs.items()]

I don't really understand what this is doing (I get the output), but the %s = '%s' and the % before 'item' keyword, is something I haven't seen before?

The function returns this:

# method call
make_element('item', 'Albatross', size="Large", color="Blue")

# output
attrs has type <class 'dict'>
attrs -> {'size': 'Large', 'color': 'Blue'}
attrs.items() -> dict_items([('size', 'Large'), ('color', 'Blue')])
keyvals -> ['size = "Large"', 'color = "Blue"']
attr_str -> size = "Large" color = "Blue"
element -> <itemsize = "Large" color = "Blue">Albatross</item>
'<itemsize = "Large" color = "Blue">Albatross</item>'

Solution

  • When applied to a string (as the left argument), the % operator performs C-style formatting substitutions, producing a string as the result. For example, %s does string formatting, %d does integer formatting, etc.

    Here are a couple simple examples:

    >>> '%s' % 'foo'
    'foo'
    >>> 
    
    >>> '%d' % 123
    '123'
    >>> 
    

    You can format more than one value by packing the values into a tuple:

    >>> '%s %s' % ("foo", "bar")
    'foo bar'
    >>> 
    
    >>> '%s %d' % ("foo", 123)
    'foo 123'
    >>> 
    

    The last example is basically the case you have in your code. Try the following:

    >>> items = ('foo', 'xyz')
    >>> '%s = "%s"' % items
    'foo = "xyz"'
    >>>