variable length of %s with the % operator in python - python

I'm trying to do this:
max_title_width = max([len(text) for text in columns])
for column in columns:
print "%10s, blah" % column
But I want to replace the 10 with the value of max_title_width. How do I do this in the most pythonic way possible?

This is a carryover from the C formatting markup:
print "%*s, blah" % (max_title_width,column)
If you want left-justified text (for entries shorter than max_title_width), put a '-' before the '*'.
>>> text = "abcdef"
>>> print "<%*s>" % (len(text)+2,text)
< abcdef>
>>> print "<%-*s>" % (len(text)+2,text)
<abcdef >
>>>
If the len field is shorter than the text string, the string just overflows:
>>> print "<%*s>" % (len(text)-2,text)
<abcdef>
If you want to clip at a maximum length, use the '.' precision field of the format placeholder:
>>> print "<%.*s>" % (len(text)-2,text)
<abcd>
Put them all together this way:
%
- if left justified
* or integer - min width (if '*', insert variable length in data tuple)
.* or .integer - max width (if '*', insert variable length in data tuple)

You have the new strings formatting methods from Python 3 and Python 2.6.
Starting in Python 2.6, the built-in str and unicode classes provide the ability to do complex variable substitutions and value formatting via the str.format() method described in PEP 3101. The Formatter class in the string module allows you to create and customize your own string formatting behaviors using the same implementation as the built-in format() method.
(...)
For example, suppose you wanted to have a replacement field whose field width is determined by another variable:
>>> "A man with two {0:{1}}.".format("noses", 10)
"A man with two noses ."
>>> print("A man with two {0:{1}}.".format("noses", 10))
A man with two noses .
So for your example it would be
max_title_width = max(len(text) for text in columns)
for column in columns:
print "A man with two {0:{1}}".format(column, max_title_width)
I personally love the new formatting methods, as they are far more powerful and readable in my humble opinion.

Python 2.6+ alternate version examples:
>>> '{:{n}s}, blah'.format('column', n=10)
'column , blah'
>>> '{:*>{l}s}'.format(password[-3:], l=len(password)) # password = 'stackoverflow'
'**********low'
>>> '{:,.{n}f} {}'.format(1234.567, 'USD', n=2)
'1,234.57 USD'
Hint: first non-keyword args, then keyword args.

you could create your template outside of the loop:
tmpl = '%%%ds, blah' % max_title_width
for column in columns:
print tmpl % column
You could also learn about the new formatting in python.
and btw, max doesn't require a list, you can pass it an iterable:
max_title_width = max(len(i) for i in columns)

Related

Can I include variable in string formatting mini-language (Python)? [duplicate]

Is it possible to use variables in the format specifier in the format()-function in Python? I have the following code, and I need VAR to equal field_size:
def pretty_printer(*numbers):
str_list = [str(num).lstrip('0') for num in numbers]
field_size = max([len(string) for string in str_list])
i = 1
for num in numbers:
print("Number", i, ":", format(num, 'VAR.2f')) # VAR needs to equal field_size
You can use the str.format() method, which lets you interpolate other variables for things like the width:
'Number {i}: {num:{field_size}.2f}'.format(i=i, num=num, field_size=field_size)
Each {} is a placeholder, filling in named values from the keyword arguments (you can use numbered positional arguments too). The part after the optional : gives the format (the second argument to the format() function, basically), and you can use more {} placeholders there to fill in parameters.
Using numbered positions would look like this:
'Number {0}: {1:{2}.2f}'.format(i, num, field_size)
but you could also mix the two or pick different names:
'Number {0}: {1:{width}.2f}'.format(i, num, width=field_size)
If you omit the numbers and names, the fields are automatically numbered, so the following is equivalent to the preceding format:
'Number {}: {:{width}.2f}'.format(i, num, width=field_size)
Note that the whole string is a template, so things like the Number string and the colon are part of the template here.
You need to take into account that the field size includes the decimal point, however; you may need to adjust your size to add those 3 extra characters.
Demo:
>>> i = 3
>>> num = 25
>>> field_size = 7
>>> 'Number {i}: {num:{field_size}.2f}'.format(i=i, num=num, field_size=field_size)
'Number 3: 25.00'
Last but not least, of Python 3.6 and up, you can put the variables directly into the string literal by using a formatted string literal:
f'Number {i}: {num:{field_size}.2f}'
The advantage of using a regular string template and str.format() is that you can swap out the template, the advantage of f-strings is that makes for very readable and compact string formatting inline in the string value syntax itself.
I prefer this (new 3.6) style:
name = 'Eugene'
f'Hello, {name}!'
or a multi-line string:
f'''
Hello,
{name}!!!
{a_number_to_format:.1f}
'''
which is really handy.
I find the old style formatting sometimes hard to read. Even concatenation could be more readable. See an example:
'{} {} {} {} which one is which??? {} {} {}'.format('1', '2', '3', '4', '5', '6', '7')
I used just assigned the VAR value to field_size and change the print statement. It works.
def pretty_printer(*numbers):
str_list = [str(num).lstrip('0') for num in numbers]
field_size = max([len(string) for string in str_list])
VAR=field_size
i = 1
for num in numbers:
print("Number", i, ":", format(num, f'{VAR}.2f'))

How to use Python format to pre-pend a specified number of spaces to a string

How do I use the string format function to pre-pend a specified number of spaces to a string? Everything I search (e.g. this post and this post) tells me to use something like
"{:>15}".format("Hello")
But that will give me 10 spaces in front. What if I always want to put 4 spaces in front, keeping things left-aligned, when the input strings are of variable length? For example:
Hello
Goodbye
I thought of doing
"{:4}{}".format("", "Hello")
Which does work, but then I have to pass in this bogus empty string. Is there a cleaner way to achieve this?
If you have n as your number of spaces required
newString = f'{" "*n}oldstring'
should add n spaces
You can use a helper function and define the number of spaces or type of indent you want:
def indent(word, n = 1, style = ' '):
print(f"{style * n}->{word}")
indent('hello', n = 10)
>> ->hello
indent('hello', n = 10, style = '*')
>>**********->hello
You can change the default value of the n keyword or style according to your needs so that you won't have to always have to use f-strings or format on every output.
This doesn't use format, but textwrap.indent() does what you want.
>>> import textwrap
>>> s = 'hello\n\n \nworld'
>>> textwrap.indent(s, ' ' * 4)
' hello\n\n \n world'
Python also allows you to define your own formatting options. See this question an example of how to override it. In this case, it might look like:
import string
import re
class Template(string.Formatter):
def format_field(self, value, spec):
if re.match('\d+t', spec):
value = ' ' * int(spec[:-1]) + value
spec = ''
return super(Template, self).format_field(value, spec)
Usage:
>>> fmt = Template().format
>>> fmt('{:4t} {}', 'hello', 'world')
hello world
Studying the format string language, I do not see a way to do exactly what you want.

multi line string formatting in python

To begin with, I am working to a get a desired output like this:
*********************************************************************
hello
*********************************************************************
To achieve this I have assigned the desired output to a variable with multiline string and printing the same with format.
$ cat varibale.py
decorator = """ **********************************************************************
{}
********************************************************************** """
print(decorator.format("hello"))
Output:
**********************************************************************
hello
**********************************************************************
The issue with above approach is the extra spaces in the third line of output which is looking odd.
I am able to achieve this in the following way:
$ cat varibale.py
decorator = """ **********************************************************************
{}
*********************************************************************
"""
print(decorator.format("hello"))
Output:
**********************************************************************
hello
*********************************************************************
But this way my code doesn't look good, as it is not following the indentation.
Please suggest the right way to achieve the desired output.
One way to make multi-line literal strings look good is to use a backslash to escape the newline, like this:
s = '''\
*********************************************************************
hello
*********************************************************************
'''
print(s)
output
*********************************************************************
hello
*********************************************************************
However, PEP-008 discourages backslash usage like that. It's too fragile: if there's a space between the backslash and the newline then the newline won't get escaped, and the backslash will get printed.
A more versatile approach is to use a function which calculates the amount of padding required to centre the text, and applies it via a nested formatting specifier. For example:
def banner(s, width=69):
stars = '*' * width
pad = (width + len(s)) // 2
return '{0}\n{1:>{2}}\n{0}'.format(stars, s, pad)
print(banner('hello'))
print(banner('Hello, world', width=16))
output
*********************************************************************
hello
*********************************************************************
****************
Hello, world
****************
How it works
That format string is a little dense, so I guess I should try to explain it. ;) For full information on this topic please see Format String Syntax in the docs. The explanation below borrows from & paraphrases those docs.
'{0}\n{1:>{2}}\n{0}'.format(stars, s, pad)
The stuff enclosed in {} in a format string is called a "replacement field". The first item in a replacement field is the optional field name. This lets us identify which arg of .format goes with this replacement field. There are a couple of possible variations for field names, this format string uses numeric names, so it identifies the .format args by their position. That is, 0 corresponds to stars, 1 corresponds to s and 2 corresponds to pad.
If no field names are given they get automatically filled by the numbers 0, 1, 2, ... etc (unless you're using Python 2.6, where field names are mandatory). That's quite useful most of the time, so most format strings don't bother using field names.
After the field name we can give a "format specifier" or "format spec" which describes how the value is to be presented. A colon : separates the field name from the format spec. If you don't supply a format spec then you get a default one, and most of the time that's adequate. But here we do want a little more control, so we need to supply a format spec.
In a form spec the > sign forces the field to be right-aligned within the available space. After the alignment sign we can provide a number to specify the minimum field width; the field will automatically be made larger if necessary.
For example, '{0:>6}'.format('test') says to put argument 0 ('test') in a space that's at least 6 chars wide, aligned to the right. Which results in the string ' test'.
But a format spec can actually contain a whole new replacement field! This allows us to supply a variable to control the field width. So in my format string {1:>{2}} says to put arg 1 here (s), right aligned in a field with a width given by arg 2 (pad). Only one level of replacement field nesting is permitted, but it's hard to think of a situation where you'd actually want deeper nesting.
So putting it all together: '{0}\n{1:>{2}}\n{0}' tells .format to build a string that starts with arg 0 (stars) using the default format spec, followed by a newline, followed by arg 1 (s) right aligned in a field of width pad, followed by another newline, finally followed by arg 0 (stars) again.
I hope that made enough sense. :)
In Python 3.6+, we could use an f-string:
def banner(s, width=69):
stars = '*' * width
pad = (width + len(s)) // 2
return f'{stars}\n{s:>{pad}}\n{stars}'
you could proceed for example as:
print('*'*80)
print('{msg:^80s}'.format(msg = 'HELLO')) #^ centers the message
print('*'*80)
or if you want to have the text-width dynamic:
def fn(msg, w = 80):
delim = '*'*w
fmt = '{msg:^%ds}'%w
print(delim)
print(fmt.format(msg=msg))
print(delim)
fn('hello')
or slightly generalized version should you need to write to a file:
import sys
def fn(msg, w = 80, F = sys.stdout):
delim = '*'*w
fmt = '{delim:s}\n{msg:^%ds}\n{delim:s}\n'%w
F.write(fmt.format(delim = delim, msg = msg))
fn('hello')
Maybe :
print '*' * 80 + '\n' + ' ' * 38 + 'hello' + '\n' + '*' *80
OR
If it is python3
a = lambda x,c,mess: print(c*x + ('\n' if not mess else mess))
a(80, '*', None)
a(38, ' ', 'Hello')
a(80, '*', None)

Using variables in the format() function in Python

Is it possible to use variables in the format specifier in the format()-function in Python? I have the following code, and I need VAR to equal field_size:
def pretty_printer(*numbers):
str_list = [str(num).lstrip('0') for num in numbers]
field_size = max([len(string) for string in str_list])
i = 1
for num in numbers:
print("Number", i, ":", format(num, 'VAR.2f')) # VAR needs to equal field_size
You can use the str.format() method, which lets you interpolate other variables for things like the width:
'Number {i}: {num:{field_size}.2f}'.format(i=i, num=num, field_size=field_size)
Each {} is a placeholder, filling in named values from the keyword arguments (you can use numbered positional arguments too). The part after the optional : gives the format (the second argument to the format() function, basically), and you can use more {} placeholders there to fill in parameters.
Using numbered positions would look like this:
'Number {0}: {1:{2}.2f}'.format(i, num, field_size)
but you could also mix the two or pick different names:
'Number {0}: {1:{width}.2f}'.format(i, num, width=field_size)
If you omit the numbers and names, the fields are automatically numbered, so the following is equivalent to the preceding format:
'Number {}: {:{width}.2f}'.format(i, num, width=field_size)
Note that the whole string is a template, so things like the Number string and the colon are part of the template here.
You need to take into account that the field size includes the decimal point, however; you may need to adjust your size to add those 3 extra characters.
Demo:
>>> i = 3
>>> num = 25
>>> field_size = 7
>>> 'Number {i}: {num:{field_size}.2f}'.format(i=i, num=num, field_size=field_size)
'Number 3: 25.00'
Last but not least, of Python 3.6 and up, you can put the variables directly into the string literal by using a formatted string literal:
f'Number {i}: {num:{field_size}.2f}'
The advantage of using a regular string template and str.format() is that you can swap out the template, the advantage of f-strings is that makes for very readable and compact string formatting inline in the string value syntax itself.
I prefer this (new 3.6) style:
name = 'Eugene'
f'Hello, {name}!'
or a multi-line string:
f'''
Hello,
{name}!!!
{a_number_to_format:.1f}
'''
which is really handy.
I find the old style formatting sometimes hard to read. Even concatenation could be more readable. See an example:
'{} {} {} {} which one is which??? {} {} {}'.format('1', '2', '3', '4', '5', '6', '7')
I used just assigned the VAR value to field_size and change the print statement. It works.
def pretty_printer(*numbers):
str_list = [str(num).lstrip('0') for num in numbers]
field_size = max([len(string) for string in str_list])
VAR=field_size
i = 1
for num in numbers:
print("Number", i, ":", format(num, f'{VAR}.2f'))

How to do a string replace in a urlencoded string

I have a string like x = "http://query.yahooapis.com/v1/public/yql?q=select%20owner%2Curls%20from%20flickr.photos.info%20where%20photo_id%3D'%s'&format=json"
If I do x % 10 that fails as there are %20f etc which are being treated as format strings, so I have to do a string conactination. How can I use normal string replacements here.?
urldecode the string, do the formatting, and then urlencode it again:
import urllib
x = "http://query.yahooapis.com/v1/public/yql?q=select%20owner%2Curls%20from%20flickr.photos.info%20where%20photo_id%3D'%s'&format=json"
tmp = urllib.unquote(x)
tmp2 = tmp % (foo, bar)
x = urllib.quote(tmp2)
As one commenter noted, doing formatting using arbitrary user-inputted strings as the format specification is historically dangerous, and is certainly a bad idea.
In python string formatting, use %% to output a single % character (docs).
>>> "%d%%" % 50
'50%'
So you could replace all the % with %%, except where you want to substitute during formatting. But #Conrad Meyer's solution is obviously better in this case.
Otherwise you can use the new-style output formatting (available since v 2.6), which doesn't rely on %:
x = 'http://query.yahooapis.com/v1/public/yql?q=select%20owner%2Curls%20from%20flickr.photos.info%20where%20photo_id%3D{0}&format=json'
x.format(10)
It also has the advance of not being typedependent.

Categories