4

In order to print a header for tabular data, I'd like to use only one format string line and one spec for column widths w1, w2, w3 (or even w = x, y, z if possible.)

I've looked at this but tabulate etc. don't let me justify things in the column like format does.

This approach works:

head = 'eggs', 'bacon', 'spam'  
w1, w2, w3 = 8, 7, 10  # column widths  
line = '  {:{ul}>{w1}}  {:{ul}>{w2}}  {:{ul}>{w3}}'  
under = 3 * '='  
print line.format(*head, ul='', w1=w1, w2=w2, w3=w3)  
print line.format(*under, ul='=', w1=w1, w2=w2, w3=w3)  

Must I have individual names as widths {w1}, {w2}, ... in the format string? Attempts like {w[1]}, {w[2]}, give either KeyError or keyword can't be an expression.

Also I think the w1=w1, w2=w2, w3=w3 is not very succinct. Is there a better way?

2
  • 2
    Why not use a dictionary - w = {'w1': 8, 'w2': 7, 'w3': 10} then call line.format(..., **w). You could even build the dictionary dynamically from [8, 7, 10] - w = {'w{}'.format(index): value for index, value in enumerate([8, 7, 10], 1)}. Commented Jul 2, 2016 at 10:48
  • @jonrsharpe Thank you! I'll admit I had to study your automatic dict filler for a bit before I understood. See below also.. Commented Jul 2, 2016 at 21:21

3 Answers 3

9

Using the f-string format becomes very easy nowadays.

If you were using

print(f'{token:10}')

And you want the 10 to be another variable (for example the max length of all the tokens), you would write

print(f'{token:{maxTokenLength}}')

In other words, enclose the variable within {}


In your particular case, all you need is this.

head = 'eggs', 'bacon', 'spam'  
w1, w2, w3 = 8, 7, 10  # column widths  

print(f'  {head[0]:>{w1}}  {head[1]:>{w2}}  {head[2]:>{w3}}')
print(f'  {"="*w1:>{w1}}  {"="*w2:>{w2}}  {"="*w3:>{w3}}')

Which produces

      eggs    bacon        spam
  ========  =======  ==========
Sign up to request clarification or add additional context in comments.

Comments

1

Specifying w[0], w[1], w[2] should work if you defined w = 8, 7, 10 and passed w as keyword argument like below:

>>> head = 'eggs', 'bacon', 'spam'
>>> w = 8, 7, 10  # <--- list is also okay
>>> line = '  {:{ul}>{w[0]}}  {:{ul}>{w[1]}}  {:{ul}>{w[2]}}'
>>> under = 3 * '='
>>> print line.format(*head, ul='', w=w)  # <-- pass as a keyword argument
      eggs    bacon        spam
>>> print line.format(*under, ul='=', w=w)  # <-- pass as a keyword argument
  ========  =======  ==========

Comments

1

This is jonrsharpe's comment to my OP, worked out so as to visualise what's going on.

line = '  {:{ul}>{w1}}  {:{ul}>{w2}}  {:{ul}>{w3}}'
under = 3 * '_'

head = 'sausage', 'rat', 'strawberry tart'

# manual dict 
v = {'w1': 8, 'w2':5, 'w3': 17}
print line.format(*under, ul='_', **v)

# auto dict
widthl = [8, 7, 9]
x = {'w{}'.format(index): value for index, value in enumerate(widthl, 1)}
print line.format(*under, ul='_', **x)     

The point is that I want to be able to quickly rearrange the header without having to tweak the format string. The auto dict meets that requirement very nicely.

As for filling a dict in this way: WOW!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.