# Print list items in a rectangular frame

The aim is to have that:

list ["Hello", "World", "in", "a", "frame"]

And make it printed like this:

*********
* Hello *
* World *
* in    *
* a     *
* frame *
*********

I did this and it works fine:

a = 0
for nb in listA:
if a == 0:
print("*"*10).center(20)
a += 1
if len(nb) == 2:
print("*   " + nb + "   *").center(20)
elif len(nb) == 1:
print("*   " + nb + "    *").center(20)
else:
print("* " + nb + "  *").center(20)
if a == len(listA):
listA[-1]
print("*"*10).center(20)

Still, I feel it is very cumbersome. Do you have any ideas how I can improve it, please?

You can let .format() do most of the work for you:

def print_in_a_frame(*words):
size = max(len(word) for word in words)
print('*' * (size + 4))
for word in words:
print('* {:<{}} *'.format(word, size))
print('*' * (size + 4))

>>> print_in_a_frame("Hello", "World", "in", "a", "frame")
*********
* Hello *
* World *
* in    *
* a     *
* frame *
*********

Most of the magic happens in this line:

print('* {:<{}} *'.format(word, size))

It may be easier to understand what's going on if we rewrite it as:

print('* {a:<{b}} *'.format(a=word, b=size))

This tells format to print the keyword argument a, left-aligned, on a field of total width the keyword argument b. Note that if b was fixed, say to 10, you would write that as:

print('* {a:<10}} *'.format(a=word))

or:

print('* {:<10}} *'.format(word))
• Hi @jaime, thanks for your answer.Can you explain a bit more this part, please {:<{}} Commented May 19, 2016 at 14:40
• It probably looks like your face trying to figure it out... ;-) Have added a more detailed explanation, see if it makes sense... Commented May 19, 2016 at 14:50
• Close enough @jamie my face would look like this -_-' Thanks for the detailed explanation Commented May 19, 2016 at 15:14

@Jaime's answer was quite good. I can only suggest a couple very minor changes, mostly just to show some alternative methods.

## Get length of longest item in a list

The max() function takes an optional 2nd argument (if the first argument is an iterable) to specify a one-argument ordering function. So instead of computing size as:

size = max(len(word) for word in words)

you could compute it as:

size = len(max(words, key=len))

I'd make the argument that the first method (Jaime's) is more readable: it's a very simple and clear use of a list comprehension that almost reads like straight English — which is about as literate programming as you can hope for!

However, if for some reason (not in the OP's problem) you needed the actual longest string rather than its length, longstr = max(words, key=len) is the canonical Pythonic way to get it. If I had been used to using that, my first thought would probably be to just wrap that with a len(), et voila.

## Let print_in_a_frame() accept custom bounding character

First, I suggest letting print_in_a_frame() take a list of strings, rather than an arbitrary number of arguments (each being one of the strings to print). The reason I suggest it is to enable the following...

Second, if for some reason you decide to change the bounding character from * to something else, why not do the following:

def print_in_a_frame(words, borderchar = '*'):
size = max(len(word) for word in words)
print(borderchar * (size + 4))
for word in words:
print('{bc} {:<{}} {bc}'.format(word, size, bc = borderchar))
print(borderchar * (size + 4))

(Note the words argument is not preceded with the argument-packing operator *; thus words can be a list). Now you can do the following:

>>> print_in_a_frame("Box this message".split())
***********
* Box     *
* this    *
* message *
***********

>>> print_in_a_frame("Box this message".split(), '+')
+++++++++++
+ Box     +
+ this    +
+ message +
+++++++++++

Now, as it is, there is no checking if the 2nd argument in my suggestion for print_in_a_frame() is a single character:

>>> print_in_a_frame("Box this message".split(), '*:')
*:*:*:*:*:*:*:*:*:*:*:
*: Box     *:
*: this    *:
*: message *:
*:*:*:*:*:*:*:*:*:*:*:

so perhaps, if you only ever wanted to box text with * characters, this suggestion can lead to scope creep, and before you know it you're shaving a yak.