How to use a variable inside a str.format() Method? [duplicate] - python

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?

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
======== ======= ==========

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
======== ======= ==========

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!

Related

I am able to parse the log file but not getting output in correct format in python [duplicate]

How do I concatenate a list of strings into a single string?
For example, given ['this', 'is', 'a', 'sentence'], how do I get "this-is-a-sentence"?
For handling a few strings in separate variables, see How do I append one string to another in Python?.
For the opposite process - creating a list from a string - see How do I split a string into a list of characters? or How do I split a string into a list of words? as appropriate.
Use str.join:
>>> words = ['this', 'is', 'a', 'sentence']
>>> '-'.join(words)
'this-is-a-sentence'
>>> ' '.join(words)
'this is a sentence'
A more generic way (covering also lists of numbers) to convert a list to a string would be:
>>> my_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> my_lst_str = ''.join(map(str, my_lst))
>>> print(my_lst_str)
12345678910
It's very useful for beginners to know
why join is a string method.
It's very strange at the beginning, but very useful after this.
The result of join is always a string, but the object to be joined can be of many types (generators, list, tuples, etc).
.join is faster because it allocates memory only once. Better than classical concatenation (see, extended explanation).
Once you learn it, it's very comfortable and you can do tricks like this to add parentheses.
>>> ",".join("12345").join(("(",")"))
Out:
'(1,2,3,4,5)'
>>> list = ["(",")"]
>>> ",".join("12345").join(list)
Out:
'(1,2,3,4,5)'
Edit from the future: Please don't use the answer below. This function was removed in Python 3 and Python 2 is dead. Even if you are still using Python 2 you should write Python 3 ready code to make the inevitable upgrade easier.
Although #Burhan Khalid's answer is good, I think it's more understandable like this:
from str import join
sentence = ['this','is','a','sentence']
join(sentence, "-")
The second argument to join() is optional and defaults to " ".
list_abc = ['aaa', 'bbb', 'ccc']
string = ''.join(list_abc)
print(string)
>>> aaabbbccc
string = ','.join(list_abc)
print(string)
>>> aaa,bbb,ccc
string = '-'.join(list_abc)
print(string)
>>> aaa-bbb-ccc
string = '\n'.join(list_abc)
print(string)
>>> aaa
>>> bbb
>>> ccc
We can also use Python's reduce function:
from functools import reduce
sentence = ['this','is','a','sentence']
out_str = str(reduce(lambda x,y: x+"-"+y, sentence))
print(out_str)
We can specify how we join the string. Instead of '-', we can use ' ':
sentence = ['this','is','a','sentence']
s=(" ".join(sentence))
print(s)
If you have a mixed content list and want to stringify it, here is one way:
Consider this list:
>>> aa
[None, 10, 'hello']
Convert it to string:
>>> st = ', '.join(map(str, map(lambda x: f'"{x}"' if isinstance(x, str) else x, aa)))
>>> st = '[' + st + ']'
>>> st
'[None, 10, "hello"]'
If required, convert back to the list:
>>> ast.literal_eval(st)
[None, 10, 'hello']
If you want to generate a string of strings separated by commas in final result, you can use something like this:
sentence = ['this','is','a','sentence']
sentences_strings = "'" + "','".join(sentence) + "'"
print (sentences_strings) # you will get "'this','is','a','sentence'"
def eggs(someParameter):
del spam[3]
someParameter.insert(3, ' and cats.')
spam = ['apples', 'bananas', 'tofu', 'cats']
eggs(spam)
spam =(','.join(spam))
print(spam)
Without .join() method you can use this method:
my_list=["this","is","a","sentence"]
concenated_string=""
for string in range(len(my_list)):
if string == len(my_list)-1:
concenated_string+=my_list[string]
else:
concenated_string+=f'{my_list[string]}-'
print([concenated_string])
>>> ['this-is-a-sentence']
So, range based for loop in this example , when the python reach the last word of your list, it should'nt add "-" to your concenated_string. If its not last word of your string always append "-" string to your concenated_string variable.

Python Copying part of string

I have this line
Server:x.x.x.x # U:100 # P:100 # Pre:0810 # Tel:xxxxxxxxxx
and I want to copy the value 0810 which is after Pre: value
How i can do that ?
You could use the re module ('re' stands for regular expressions)
This solution assumes that your Pre: field will always have four numbers.
If the length of the number varies, you could replace the {4}(expect exactly 4) with + (expect 'one or more')
>>> import re
>>> x = "Server:x.x.x.x # U:100 # P:100 # Pre:0810 # Tel:xxxxxxxxxx"
>>> num = re.findall(r'Pre:(\d{4})', x)[0] # re.findall returns a list
>>> print num
'0810'
You can read about it here:
https://docs.python.org/2/howto/regex.html
As usual in these cases, the best answer depends upon how your strings will vary, and we only have one example to generalize from.
Anyway, you could use string methods like str.split to get at it directly:
>>> s = "Server:x.x.x.x # U:100 # P:100 # Pre:0810 # Tel:xxxxxxxxxx"
>>> s.split()[6].split(":")[1]
'0810'
But I tend to prefer more general solutions. For example, depending on how the format changes, something like
>>> d = dict(x.split(":") for x in s.split(" # "))
>>> d
{'Pre': '0810', 'P': '100', 'U': '100', 'Tel': 'xxxxxxxxxx', 'Server': 'x.x.x.x'}
which makes a dictionary of all the values, after which you could access any element:
>>> d["Pre"]
'0810'
>>> d["Server"]
'x.x.x.x'

Pythonic way to set variable name as string

data1 = [1,2,3,4,5]
data2 = [7,8,9,10,11]
x = data1
y = data2
What I required are the strings of above variable as follows:
xlabel = 'data1'
ylabel = 'data2'
Rather than writing manually, how can I call using x like below:
xlabel = str(x)
But above is wrong.
So, how to do?
xlabel =???
Is there a pythonic way of doing it?
To see why this makes no sense, consider the following snippet
def magic_name_getter(object):
# here be dragons
return the_name
data1 = [1,2,3,4]
magic_name_getter(data1) # returns 'data1'
data1 = [1,2,3,4,5]
data2 = [1,2,3,4,5]
magic_name_getter(data2) # returns 'data2' (..or 'data1'?)
magic_name_getter([7,8,9,10,11]) # returns ... ummm ... ???
Objects in python can have many names, or no names at all.
So what you want, whilst not impossible, is very difficult. Since the variable names are important data for your use case, you should instead be using a dictionary of keys and values mapping the names to the lists so that you have easy access to the names (keys) aswell as the data (values).
You should really consider organizing your data, e.g., in a dictionary:
a = dict(
data1 = [1,2,3,4,5],
data2 = [6,7,8,9,10],
)
x = a.keys()[0]
y = a.keys()[1]
print x, y
Output:
data1 data2
Usually you won't need to store the keys in separate variables like x and y, but work directly on a.keys() or a.items().
collections.namedtuple might help you out:
import collections
Parameters = collections.namedtuple('Parameters', 'dataname, data', verbose = False)
x = Parameters('data1', [1,2,3,4,5])
y = Parameters('data2', [7,8,9,10,11])
>>> x.dataname
'data1'
>>> x.data
[1, 2, 3, 4, 5]
>>> y.dataname
'data2'
>>> y.data
[7, 8, 9, 10, 11]
You would use it for your plot like this:
plt.scatter(x.data, y.data)
plt.xlabel(x.dataname)

python .format() repeated fields?

I know in python3, you can do something like:
>>> '{0} {1} {0}'.format(13, 42)
'13 42 13'
So you're using the same argument multiple times. I'd like to do the opposite. I have a sequence of numbers. I'd like to somehow print them using a single format specifier, but I don't see a way to indicate to the format syntax that a given identifier should consume the next N arguments. I can use a reduce
>>> reduce(lambda out,x: out + '{:02X}'.format(x), b'\x60\x0d\x1a\xdd', '')
'600D1ADD'
But I was hoping for a single format invocation, as I had some other characters I wanted to place around my otherwise formatted sequence.
What about just something like this:
list = [13, 14, 42, 43]
out = ''.join([ '{0}'.format(x) for x in list ])
There isn't any way to do that exactly. What you could do is pre-format your list of numbers, then pass that formatted string as a single item to a second round of formatting:
>>> nums = (13, 42)
>>> '{0} is {1}'.format('my stuff', ''.join('{:02X}'.format(a) for a in nums))
'my stuff is 0D2A'
If you wanted, you could write a custom Formatter subclass that accepts a sequence of numbers as a single item to be formatted, and formats them all together. That could let do something like myFormatter.format('{0} is {1:list}', 'my stuff', (13, 42)). Here's how it could work:
class ListFormatter(string.Formatter):
def format_field(self, value, spec):
if spec == 'list':
return ''.join('{:02X}'.format(a) for a in value)
else:
return super(ListFormatter, self).format_field(value, spec)
>>> lf = ListFormatter()
>>> lf.format('{0} is {1:list}', 'my stuff', [1, 43])
'my stuff is 012B'
I've used something along these lines:
>>> list = [13, 14, 42, 43]
>>> ('0x' + len (list) * '{:02X}') .format (*list)
'0x0D0E2A2B'

Python - Make sure string is converted to correct Float

I have possible strings of prices like:
20.99, 20, 20.12
Sometimes the string could be sent to me wrongly by the user to something like this:
20.99.0, 20.0.0
These should be converted back to :
20.99, 20
So basically removing anything from the 2nd . if there is one.
Just to be clear, they would be alone, one at a time, so just one price in one string
Any nice one liner ideas?
For a one-liner, you can use .split() and .join():
>>> '.'.join('20.99.0'.split('.')[:2])
'20.99'
>>> '.'.join('20.99.1231.23'.split('.')[:2])
'20.99'
>>> '.'.join('20.99'.split('.')[:2])
'20.99'
>>> '.'.join('20'.split('.')[:2])
'20'
You could do something like this
>>> s = '20.99.0, 20.0.0'
>>> s.split(',')
['20.99.0', ' 20.0.0']
>>> map(lambda x: x[:x.find('.',x.find('.')+1)], s.split(','))
['20.99', ' 20.0']
Look at the inner expression of find. I am finding the first '.' and incrementing by 1 and then find the next '.' and leaving everything from that in the string slice operation.
Edit: Note that this solution will not discard everything from the second decimal point, but discard only the second point and keep additional digits. If you want to discard all digits, you could use e.g. #Blender's solution
It only qualifies as a one-liner if two instructions per line with a ; count, but here's what I came up with:
>>> x = "20.99.1234"
>>> s = x.split("."); x = s[0] + "." + "".join(s[1:])
>>> x
20.991234
It should be a little faster than scanning through the string multiple times, though. For a performance cost, you can do this:
>>> x = x.split(".")[0] + "." + "".join(x.split(".")[1:])
For a whole list:
>>> def numify(x):
>>> s = x.split(".")
>>> return float( s[0] + "." + "".join(s[1:]))
>>> x = ["123.4.56", "12.34", "12345.6.7.8.9"]
>>> [ numify(f) for f in x ]
[123.456, 12.34, 12345.6789]
>>> s = '20.99, 20, 20.99.23'
>>> ','.join(x if x.count('.') in [1,0] else x[:x.rfind('.')] for x in s.split(','))
'20.99, 20, 20.99'
If you are looking for a regex based solution and your intended behaviour is to discard everthing after the second .(decimal) than
>>> st = "20.99.123"
>>> string_decimal = re.findall(r'\d+\.\d+',st)
>>> float(''.join(string_decimal))
20.99

Categories

Resources