简体   繁体   中英

Variable number of format arguments

I'm trying to make a format string variable based on number of items in a list

d = {1: ['Spices', 39], 2: ['Cannons', 43], 3: ['Tea', 31], 4: ['Contraband', 46], 5: ['Fruit', 38], 6: ['Textiles', 44]} 
d_max = [2, 11, 3]

for k,v in d.items():
    list_var = [k, v[0], v[1]]
    print(("{:<{}} " * 3).format(list_var[0], d_max[0], list_var[1], d_max[1], list_var[2], d_max[2]))

I'd like this to work if the keys had more or less values without hard coding the response. Can I create a string in a for loop then parse and eval it? I don't know the syntax for doing this. Or if there is a more pythonic way I'd love to know as well.

Thanks in advance.

I was under the impression that you also wanted to to be able to randomly add new items to the lists for each key. I was bored so I said why not and wrote the following code up. It will find the longest length of each entry of each key-value and put it in d_max, doesn't matter what type it is, as long as it can be converted to a string and also supports randomly adding things to the values (see last two lines of d). I tried to comment it well, but ask something if you need to.

d = {1: ['Spices', 39],
     2: ['Cannons', 43],
     3: ['Tea', 31],
     4: ['Contraband', 46],
     5: ['Fruit', 38],
     6: ['Textiles', 44],
     7: ['Odds and Ends', 100, 9999],
     8: ['Candies', 9999, 'It\'s CANDY!']} 
d_max = []

# Iterate over keys of d
for k in d:
    # Length of the key
    if len(d_max) <= 0:
        d_max.append(len(str(k)) + 1)
    elif len(str(k))+ 1 > d_max[0]:
        d_max[0] = len(str(k)) + 1 

    # Iterate over the length of the value
    for i in range(len(d[k])):
        # If the index isn't in d_max then this must be the longest
        # Add one to index because index 0 is the key's length
        if len(d_max) <= i+1:
            d_max.append(len(str(d[k][i])))
            continue
        # This is longer than the current one
        elif len(str(d[k][i])) + 1 > d_max[i+1]:
            d_max[i+1] = len(str(d[k][i])) + 1

for k,v in d.items():
    list_var = [k] + v

    # A list of values to unpack into the string
    vals = []
    # Add the value then the length of the space
    for i in range(len(list_var)):
        vals.append(list_var[i])
        vals.append(d_max[i])

    print(("{:<{}} " * len(list_var)).format(*vals))

Output:

1  Spices         39    
2  Cannons        43    
3  Tea            31    
4  Contraband     46    
5  Fruit          38    
6  Textiles       44    
7  Odds and Ends  100   9999         
8  Candies        9999  It's CANDY! 

If you wanted it all in one line then I'm afraid I can't help you :( There's also probably a cleaner way to do the second loop but that's all I could think up on a few hours of sleep.

do you mean you want to do something like:

list_var = [k] + v[:2]

This will work if the values list has too many items (It'll just remove the excess).

If I understand your question correctly, this is the format string you want:

"{:<{}} " * len(list_var)

This of course requires, in your particular case, that your lists ( d_max and list_var ) are both the correct length. But it'll work for any length.

To make the actual use case a little cleaner, I'd probably put the list of arguments together in a separate statement. The general case for your loop could look something like this:

args = []
for i in range(len(list_var)): # Build list of value/width arguments
    args += [list_var[i], d_max[i]]
print(("{:<{}} " * len(list_var)).format(*args))

Or if you're a stickler for one-liners, you could use this monstrosity (does the same thing as the explicit loop above, except that it's much harder to read.):

# Ew
print(("{:<{}} " * len(list_var)).format(*functools.reduce(
                                            lambda a, i: a + [list_var[i], d_max[i]],
                                            range(len(list_var)),
                                            [])))

Finally, as others have noted, you shouldn't rely on a dictionary to preserve order. If it's important that the items are presented in order by their dictionary keys, you should make sure to sort them before you iterate:

for k,v in sorted(d.items()):

If I've understood your question correctly, the following will handle both more and fewer values being associated with each key in the dictionary. In fact, each entry does not have to have the same number of values in the list.

To illustrate this I've both added and removed values from several of the entries shown in the dictionary your code to the one below. Value entries beyond the first two are simply ignored, but you could easily accommodate more by extending the d_max field width list as necessary for them.

d = {1: ['Spices', 39, 'a'],
     2: ['Cannons', 43, 'b', 'c'],
     3: [],
     4: ['Contraband', 46, 'd'],
     5: ['Fruit'],
     6: ['Textiles', 44, 'f']}

d_max = [2, 11, 3]

for k,v in d.items():
    list_var = [k] + v[:2]
    pairs = (item for pair in zip(list_var, d_max) for item in pair)
    print(("{:<{}} " * len(list_var)).format(*pairs))

Output:

1  Spices      39
2  Cannons     43
3
4  Contraband  46
5  Fruit
6  Textiles    44

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM