This is my first post, so I apologize if this has been answered previously. I have tried to look through the Python 3 documentation on string formatting and lists, and reviewed similar formatting questions here on SO.
I want to take the string (data1), break it into a list (bigData), and print out a statement using the list items. Eventually, the idea would be to read in a csv file, break it up, and print out a response, but I've tried to simplify the process since there's an error.
"Hello, John Doe. Your current balance is $53.44."
However, I'm not sure why the following code is throwing an IndexError, much less a tuple index.
data1 = "John,Doe,53.44"
bigData = data1.split(",")
bigData[-1] = float(bigData[-1])
print(bigData) # test - []'s indicate a list, not tuple?
greeting = "Hello, {} {}. Your current balance is ${}."
print(greeting.format(bigData))
My guess is that bigData is heterogeneous, which implies a tuple. If I substitute a string value instead of 53.44 (so data1 and bigData are homogeneous), it throws the same error.
data1 = "John,Doe,random"
bigData = data1.split(",")
print(bigData) # test - []'s indicate a list, not tuple?
greeting = "Hello, {} {}. Your current balance is {}."
print(greeting.format(bigData))
However, if I convert the original to Python 2.x string formatting, it formats correctly without an error.
data1 = "John,Doe,53.44"
bigData = data1.split(",")
bigData[-1] = float(bigData[-1])
print(bigData) # test - []'s indicate a list, not tuple?
greeting = "Hello, %s %s. Your current balance is $%.2f."
print(greeting % tuple(bigData))
Why is it converting my string to a tuple?
How do I write this work in Python 3?
Thank you.
Use the splat (*) to unpack your arguments (your format string wants three arguments but you only give it one, a list containter).
print(greeting.format(*bigData))
Also, you may want:
bigData[-1] = str(round(float(bigData[-1]), 2))
The str.format method takes positional arguments, not a single list. You need to unpack your list bigData using the * operator:
data1 = "John,Doe,random"
bigData = data1.split(",")
print(bigData) # test - []'s indicate a list, not tuple?
greeting = "Hello, {} {}. Your current balance is {}."
print(greeting.format(*bigData)) # here's the change
You're correct that bigData is a list, not a tuple, str.split returns a list.
The str.split() method returns a list, by definition.
I think you've misunderstood something you've read - heterogeneous vs. homogeneous refer to typical use cases of tuples vs. lists. Having the types of all the elements match or not does not magically cause the container to change to the other type!
I can see how this is surprising, though what surprises me is that the traceback doesn't show that the exception occurs in the format call.
Python's lists can be heterogenous just like tuples; this is because the common type they store is object references, which all things in Python are. The tuple is actually the argument list to the format method, in this case (bigData,). It ran out of arguments when looking for things to format, since you had three {} placeholders but only one argument (the list bigData). You can use greeting.format(*bigData) to unpack the list and use its contents as arguments.
The % formatting doesn't encounter this error because it actually expects a tuple (or one item) in the right operand.
A more idiomatic and legible approach might actually be to go to the csv module already:
import csv, io
data1 = "John,Doe,random"
for row in csv.DictReader(io.StringIO(data1),
"givenname surname balance".split()):
greeting = "Hello, {givenname} {surname}. Your current balance is {balance}."
print(greeting.format(**row))
This lets us assign meaningful names to the columns, including reordering them in the format string if needed. I've left out the float conversion, and by the way, decimal.Decimal may be better for that use.
Related
I have a JSON file with n number of dictionaries as listed below in the snippet. I am trying to fetch the value against the key but it fails in my code when the value is defined as a list like in the below example for key affected_packages. I tried to check why my code fails, so it looks like it pulls no data out of it this fails. I just see two brackets [] as output instead of "thunderbird-0:78.9.1-1.el8_1","thunderbird-0:78.9.1-1.el8_2","thunderbird-0:78.9.1-1.el8_3","thunderbird-0:78.9.1-1.el7_9"
{"bugzilla_description":"CVE-2021-23992 Mozilla: A crafted OpenPGP key with an invalid user ID could be used to confuse the user","cvss_score":null,"cvss_scoring_vector":null,"CWE":"CWE-347","affected_packages":["thunderbird-0:78.9.1-1.el8_1","thunderbird-0:78.9.1-1.el8_2","thunderbird-0:78.9.1-1.el8_3","thunderbird-0:78.9.1-1.el7_9"],"resource_url":"https://access.redhat.com/hydra/rest/securitydata/cve/CVE-2021-23992.json","cvss3_scoring_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L","cvss3_score":"4.3"}
I am doing like below in my code as I need to prepare a worksheet. Sample snippet:
for i in range(offs):
ws.cell(row=r+1+i,column=2).value = v['current'][i]
if 'affected_packages' in list(tmp1.keys()):
ws.cell(row=r+1+index1,column=11).value = tmp1['affected_packages']
print("affected_packages done")
if 'advisories' in list(tmp1.keys()):
ws.cell(row=r+1+index2,column=13).value = tmp1['advisories']
print("advisories done")
Is there a way I can pull the value correctly for those defined as a list in the dictionary? I need a way so that it won't hamper my existing logic to pull value for normal key: value since while looking up into my JSON file.
So need something which can fulfil both whether my value in the dictionary is as a list or not as a list and I can get value against the keys in my json file.
As mentioned in the other answers, you can test the type of a variable using
if type(some_variable) == list:
# do what you need to do
You do mention that your code breaks, and I guess it's because inserting into a cell expects a String, not the list you pass in the line
ws.cell(row=r+1+index1,column=11).value = tmp1['affected_packages']
So how do we get a string out of a list of strings? It's pretty easy using the join method.
my_list = ["thunderbird-0:78.9.1-1.el8_1","thunderbird-0:78.9.1-1.el8_2","thunderbird-0:78.9.1-1.el8_3","thunderbird-0:78.9.1-1.el7_9"]
as_one_string = ", ".join(my_list)
print(as_one_string)
# Prints out 'thunderbird-0:78.9.1-1.el8_1, thunderbird-0:78.9.1-1.el8_2, thunderbird-0:78.9.1-1.el8_3, thunderbird-0:78.9.1-1.el7_9'
So combining the two ideas:
if 'affected_packages' in list(tmp1.keys()):
ws.cell(row=r+1+index1,column=11).value = tmp1['affected_packages'] if type(tmp1['affected_packages']) != list else ", ".join(tmp1['affected_packages'])
print("affected_packages done")
Quick feedback because I can't comment yet: Please always include an error message and/or the output you get when running your code when you ask a question
If I understand it correctly, you just need to determine if a value in dict is list. You can do that as below:
for i in d.items(): # gets key, value as a tuple.
if isinstance(i[1],list):
print('its a list, process it accordingly')
else:
print('Not a list')
Regarding your second problem, when you don't know if it is a list or something else, you can just check the type, maybe like this:
if type(tmp1['affected_packages']) == list:
# process the list
else:
# process other types
Since you don't know the data type, having this explicit type check seems necessary.
I have a really weird python problem. I have already asked several colleagues who all had no idea how it happened and how it could be solved.
I have a shallow dict of strings that I receive from an API call and I want to assign some of those values to a new dict.
This is what the first dict looks like. Just a bunch of strings:
I assign some values from dict1 to some keys in dict2. Really basic
dict2={}
dict2['access_key'] = dict1['access_key']
dict2['secret_access_key'] = dict1['secret_access_key'],
dict2['session_token'] =dict1['session_token'],
dict2['region'] = dict1['region']
Then this happens. The values for "secret access key" and "session_token" are turned into tuples. "access_key" and "region" remain strings
I have already tried initialising the values as strings, accessing the first entry of the tuple and casting the value to a string. All of that did not change anything. It seems like the value is assigned just fine and then something weird happens that turns it into a tuple
This is a screenshot of my Interpreter settings. I am using Pyython 3.6
I am really going crazy over this one :-/ Any help would be greatly appreciated
You have trailing commas at the end of two of your calls. This is why the strings are transformed to tuples.
My dear friend , the reason you have this problem is in Python tuple is idetified by commas.
You can try this code
a = 1,
print(a,type(a))
b = 1
print(b,type(b))
So I update your code to this:
dict2={}
dict2['access_key'] = dict1['access_key']
# please notes the end !
# dict2['secret_access_key'] = dict1['secret_access_key'],
# dict2['session_token'] =dict1['session_token'],
# above is the origin code
dict2['secret_access_key'] = dict1['secret_access_key']
dict2['session_token'] =dict1['session_token']
dict2['region'] = dict1['region']
I think this will help!
Try the following by removing the trailing commas:
dict2={}
dict2['access_key'] = dict1['access_key']
dict2['secret_access_key'] = dict1['secret_access_key']
dict2['session_token'] =dict1['session_token']
dict2['region'] = dict1['region']
I have a list of computer nodes called node_names, and I want to find the amount of free ram in each node, and store that in a second list. I then want to combine these lists into a dictionary.
I have:
for i in range(0, number_of_nodes):
sys_output = [commands.getoutput('ssh %s \'free -m\'' % node_names[i])]
free_memory = [x.split()[9] for x in sys_output]
print free_memory
For 4 nodes, this returns [mem1],[mem2],[mem3],[mem4].
How can I combine each memory value into a single list? I'm having difficulty assigning free_memory as a list instead of a string which is replaced after each loop iteration.
Once I have a memory list, I should be able to combine it with the node_names list to make a dictionary file and do any necessary sorting.
I would recommend just building the dictionary directly:
import commands
node_free_mem = {}
for n in node_names:
sys_output = commands.getoutput("ssh %s 'free -m'" % n)
free_memory = sys_output.split()[9]
node_free_mem[n] = int(free_memory)
Here's code that does exactly what you asked: it builds a list, then uses the list to make a dictionary. Discussion after the code.
import commands
def get_free_mem(node_name):
sys_output = commands.getoutput('ssh %s \'free -m\'' % node_name)
free_memory = sys_output.split()[9]
return int(free_memory)
free_list = [get_free_mem(n) for n in node_names]
node_free_mem = dict(zip(node_names, free_list))
Note that in both code samples I simply iterate over the list of node names, rather than using a range() to get index numbers and indexing the list. It's simplest and best in Python to just ask for what you want: you want the names, so ask for those.
I made a helper function for the code to get free memory. Then a simple list comprehension builds a parallel list of free memory values.
The only tricky bit is building the dict. This use of zip() is actually pretty common in Python and is discussed here:
Map two lists into a dictionary in Python
For large lists in Python 2.x you might want to use itertools.izip() instead of the built-in zip(), but in Python 3.x you just use the built-in zip().
EDIT: cleaned up the code; it should work now.
commands.getoutput() returns a string. There is no need to package up the string inside a list, so I removed the square braces. Then in turn there is no need for a list comprehension to get out the free_memory value; just split the string. Now we have a simple string that may be passed to int() to convert to integer.
I am newbie in python and mod_wsgi development. When I want to get data from POST i read envron['wsgi.input'] and as a result I get name=username&fullname=name&pass=password. Now my question is how to split it to get that input as array. For example to read like name = arraypost[0]?
You could split the data and pass it into dict() to create a dictionary of key, value pairs:
post_dict = dict([item.split('=') for item in envron['wsgi.input'].split('&')])
print post_dict['name']
print post_dict['fullname']
print post_dict['pass']
Here I'm demonstrating how to do it in Python 3. Using urllib.parse.parse_qs converts it to a dictionary of lists.
import urllib.parse
post = urllib.parse.parse_qs(environ['QUERY_STRING'])
# get value of first instance of POST var 'foo'
print(post.get(b'foo',[''])[0])
Note the use of get() above. The first argument is a byte string, the second argument is a default value. As I said, it's a dictionary of lists, and since we're immediately fetching from index 0, we want at least a zero-length string there so we don't go out-of-bounds.
Okay I concede that I didn't ask the question very well. I will update my question to be more precise.
I am writing a function that takes a list as an argument. I want to check the length of the list so I can loop through the list.
The problem that I have is when the list has only one entry, len(myList) returns the length of that entry (the length of the string) and not the length of the list which should be == 1.
I can fix this if I force the argument to be parsed as a single value list ['val']. But I would prefer my API to allow the user to parse either a value or a list of values.
example:
def myMethod(self,dataHandle, data,**kwargs):
comment = kwargs.get('comment','')
_dataHandle= list()
_data = list()
_dataHandle.append(dataHandle)
_data.append(data)
for i in range(_dataHandle):
# do stuff.
I would like to be able to call my method either by
myMethod('ed', ed.spectra,comment='down welling irradiance')
or by
myMethod(['ed','lu'] , [ed.spectra,lu.spectra] , comments = ['downwelling', upwelling radiance'])
Any help would be greatly appreciated. Might not seem like a big deal to parse ['ed'], but it breaks the consistency of my API so far.
The proper python syntax for a list consisting of a single item is [ 'ed' ].
What you're doing with list('ed') is asking python to convert 'ed' to a list. This is a consistent metaphor in python: when you want to convert something to a string, you say str(some_thing). Any hack you'd use to make list('ed') return a list with just the string 'ed' would break python's internal metaphors.
When python sees list(x), it will try to convert x to a list. If x is iterable, it does something more or less equivalent to this:
def make_list(x):
ret_val = []
for item in x:
ret_val.append(item)
return ret_val
Because your string 'ed' is iterable, python will convert it to a list of length two: [ 'e', 'd' ].
The cleanest idiomatic python in this case might be to have your function accept a variable number of arguments, so instead of this
def my_func(itemList):
...
you'd do this
def my_func(*items):
...
And instead of calling it like this
my_func(['ed','lu','lsky'])
You'd call it like this:
my_func('ed', 'lu', 'lsky')
In this way you can accept any number of arguments, and your API will be nice and clean.
You can ask if your variable is a list:
def my_method(my_var):
if isinstance(my_var, list):
for my_elem in my_var:
# do stuff with my_elem
else: # my_var is not iterable
# do stuff with my_var
EDIT: Another option is to try iterating over it, and if it fails (raises and exception) you assume is a single element:
def my_method(my_var):
try:
for my_elem in my_var:
# do stuff with my_elem
except TypeError: # my_var is not iterable
# do_stuff with my_var
The good thing about this second options is that it will work not only for lists, as the first one, but with anything that is iterable (strings, sets, dicts, etc.)
You do actually need to put your string in a list if you want it to be treated like a list
EDIT
I see that at some point there was a list in front of the string. list, contrary to what you may think, doesn't create a list of one item. It calls __iter__ on the string object and iterates over each item. Thus it makes a list of characters.
Hopefully this makes it clearer:
>>> print(list('abc'))
['a', 'b', 'c']
>>> print(list(('abc',)))
['abc']
list('ed') does not create a list containing a single element, 'ed'. list(x) in general does not create a list containing a single element, x. In fact, if you had been using numbers rather than strings (or anything else non-iterable), this would have been blindingly obvious to you:
>>> list('ed')
['e', 'd']
>>> list(3)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
list(3)
TypeError: 'int' object is not iterable
>>
So you are in fact passing a list with multiple elements to your method, which is why len is returning greater than 1. It's not returning the length of the first element of the list.
For your method to allow passing either a single item or a list, you'd have to do some checking to see if it's a single item first, and if it is create a list containing it with myVar = [myVar], then run your loop.
However this sort of API is tricky to implement and use, and I would not recommend it. The most natural way to check if you've been given a collection or an item is see if myVar is iterable. However this fails for strings, which are iterable. Strings unfortunately straddle the boundry between a collection and an individual data item; we very very often use them as data items containing a "chunk of text", but they are also collections of characters, and Python allows them to be used as such.
Such an API also is likely to cause you to one day accidentally pass a list that you're thinking of as a single thing and expecting the method to treat it as a single thing. But it's a list, so suddenly the code will behave differently.
It also raises questions about what you do with other data types. A dictionary is not a list, but it can be iterated. If you pass a dictionary as myVar, will it be treated as a list containing a single dictionary, or will it iterate over the keys of the dictionary? How about a tuple? What about a custom class implementing __iter__? What if the custom class implementing __iter__ is trying to be "string-like" rather than "list-like"?
All these questions lead to surprises if the caller guesses/remembers wrongly. Surprises when programming lead to bugs. IMHO, it's better to just live with the extra two characters of typing ([ and ]), and have your API be clean and simple.
I run into this same problem frequently. Building a list from an empty list, as you are doing with the "_dataHandle= list()" line, is common in Python because we don't reserve memory in advance. Therefore, it is often the case that the state of the list will transition from empty, to one element, to multiple elements. As you found, Python treats the indexing different for one element vs. multiple elements. If you can use list comprehension, then the solution can be simple. Instead of:
for i in range(_dataHandle):
use:
for myvar in _dataHandle:
In this case, if there is only one element, the loop only iterates once as you would expect.