Having a little trouble parsing JSON with Python and I'm not really sure what the syntax is I'd need.
The structure looks like this
name = (json_data['JsonResultTitle']['Loc']['List'][0]['Events'][0]['Name'])
The numbers after List and Events can both change.
I was able to iterate through the List ones using something ugly like:
namecounter = 0
try:
name = (json_data['JsonResultTitle']['Loc']['List'][namecounter]['Events'][0]['Name'])
namecounter +=1
except:
print "stop"
And I could technically embed another loop in there which runs until another exception for the Events counter but there has to be a neater way of doing this.
Something like this might be neater but it still doesn't strike me as the right way of doing things either:
counter = 0
secondcounter = 0
for i in json_data['JsonResultTitle']:
try:
print i['Loc']['List'][counter]['Events'][0]['Name']
except:
print "no first"
counter +=1
try:
print i['Loc']['List'][counter]['Events'][secondcounter]['Name']
except:
print "no second"
secondcounter +=1
This might be blindingly obvious but I can't see the proper way of doing this.
I'd really appreciate a nudge in the right direction.
Seems like you just want a nested loop.
for item in json_data['JsonResultTitle']['Loc']['List']:
for event in item['Events']:
print event['Name']
Related
I'm using the Google Docs API to retrieve the contents of a document and process it using Python. However, the document is of complex structure and I have to loop through multiple nodes of the returned JSON, so I have to use multiple for loops to get the desired content and do the filter necessary. Is there a way that I can eliminate some of the indentations to make the format look much more organized?
Here is a snippet of my loops:
for key, docContent in docs_api_result.json().items():
if key == "body":
content = docContent['content']
for i, body_content in enumerate(content):
if "table" in body_content:
for sKey, tableContent in content[i]['table'].items():
if sKey == "tableRows":
for tableRowContent in tableContent:
for tableCellMain in tableRowContent['tableCells']:
for tableCellContent in tableCellMain['content']:
hasBullet = False
for tableCellElement in tableCellContent['paragraph']['elements']:
if "bullet" in tableCellContent['paragraph']:
...
I know that instead of having
if True:
# some code here
I can replace it with
if False:
continue
# some code here
to remove some of the indents, but that only solves part of the problem. I still have 7 for-loops left and I hope that I could remove some of the indentations as well.
Any help is appreciated! :)
The general method for reducing indentation levels would be to identify blocks of code to go in their own functions.
E.g. looking at your loop, I guess I would try something like:
class ApiResultProcessor(object):
def process_api_result(self, api_result):
doc_dict = api_result.json()
if "body" in doc_dict:
self.process_body(doc_dict["body"])
def process_body(self, body_dict):
content = body_dict["content"]
for i, content_element_dict in enumerate(content):
if "table" in content_element_dict:
self.process_table(content_element_dict["table"])
...
def process_table(self, table_dict):
for tableRowContent in table_dict["tableRows"]:
for tableCellMain in tableRowContent["tableCells"]:
for tableCellContent in tableCellMain['content']:
self.process_cell_content(tableCellContent)
def process_cell_content(self, table_cell_dict):
hasBullet = False
for tableCellElement in table_cell_dict["paragraph"]["elements"]:
if "bullet" in table_cell_dict["paragraph"]:
...
The only refactoring that I have done is trying to avoid the dreadful "for if" antipattern.
I am not that experienced with python, but I am pretty sure you can use only one space and not multiple of four every indentation and it won't be an indentation error. Although it is not according to the PEP 8 protocol...
So just remove every 4 spaces/tab you have in this bunch of code, to 1 space.
I have a long function to do a NLTK task which also calculates the ratio of several things like sum(adj)/len(words).
Sometimes it´s possible that the words are zero which then occures an error. I tried it with something like this
def nltk_process(dict_bp):
try:
#do all the stuff and then calculate several ratios
verhaeltnis_adj_verb = "Verhältnis ADJ/VERB: " + str(dict.get('ADJ')/dict.get('VERB')) # just an example where sometimes it devides by zero
except:
pass
I use the function in a big for loop to do this on about 800 MySQL tables. It works for the most if I f.e. break the loop after 10 iterations. But then in some of the data it devides by zero which makes everything not work.
So is there any simple solution to this? Why does the try and except not work on my function?
error:
global filtered_sentence_verhaeltnis
ZeroDivisionError: division by zero
filtered_sentence_verhaeltnis = ("Sentence Sentiment gefiltert: " + str(sum(filtered_sentence_sent)/len(filtered_sentence_sent)))
EDIT//
My code looks something like this:
for i in dict_bp.values():
nltk_process(i)
in this nltk_process sometimes it gets the error because of the empty values of the dict. So I need something to make my nltk_process function to not error everything, just keep continueing if an error occures.
Thanks.
thanks for your help.
So I fixed it. I used try and except but not in the function itself. I had to use it in the loop where I call the function.
Now it looks like this:
def export_all_to_excel(dict_bp):
workbook = xlsxwriter.Workbook("Export_ALL.xlsx")
z = 0
worksheet = workbook.add_worksheet('Analyse')
for key,values in dict_bp.items():
z += 1
try:
nltk_einzelauswahl(list(values))
#some more stuff
worksheet.write('G{}'.format(z), sentence_sent_verhaeltnis)
except:
worksheet.write('A{}'.format(z), key)
worksheet.write('B{}'.format(z), "Some error occured")
workbook.close()
So I have been trying to find out a more beautiful way to actually do some fault and errors in a script I am working on.
Basically I have a json_resp = resp.json() who either gives me values or [] meaning either there is something or not.
Now the question I am having trouble with is that I don't know which way is the best to repeat a function if it is empty, shall I repeat the whole function or what else would be most "best reason" to solve it in a good way?
What I have done is that I changed the objects from the json resp to a len. If its 0 then repeat else do other stuff:
#json_resp['objects'] either has empty [] or not always.
json_resp = resp.json()
if len(json_resp['objects']) == 0:
print('Sleeping in 2 sec')
time.sleep(2)
run_method() #Shall I call the function to start over?
else:
print(len(json_resp['objects']))
continue do rest of the code
As you can see right now I am compare with len of the json_resp but what makes me unsure is that if it is a good way to actually call the function again? Wouldn't it have a limit or maybe delay the whole process... Im not sure but what is your thoughts of making this function "better, smarter, faster"?
My thought was maybe to either put a try except or while loop that? Let me know what you guys think
Python lists are faulty so you can just use if json_resp:
You can use recursion. Just make sure you have somewhere to break
I'd like to revise your code into:
max_iteration = 5
current_iteration = 0
def run_method():
current_iteration += 1
# Do other stuff. Requests I guess?
response = resp.json
if response:
# do something with the response
else:
if current_iteration == max_iteration:
return 'Maximum iterations reached: {}'.format(max_iteration)
timer = 2
print('Sleeping in {} seconds'.format(timer))
time.sleep(timer)
run_method()
Current assignment is building a basic text adventure. I'm having trouble with the following code. The current assignment uses only functions, and that is the way the rules of the assignment state it must be done.
def make_selections(response):
repeat = True
while repeat == True:
selection = raw_input('-> ')
for i, v in enumerate(response):
i +=1 # adds 1 to the index to make list indices correlate to a regular 1,2,3 style list
if selection == i:
print v[1]
else:
print "There's an error man, what are you doing?!?!?"
firstResponse = 'You chose option one.'
secondResponse = 'You chose option two.'
thirdResponse = 'You chose option three.'
responses = [(0, firstResponse), (1, secondResponse),( 0, thirdResponse)]
make_selections(responses)
My intention in that code is to make it so if the user selects a 1, it will return firstResponse, if the user selects 2 it will return secondResponse, etc.
I am basically just bug testing the code to make sure it produces the appropriate response, hence the "Error man..." string, but for some reason it just loops through the error message without printing the appropriate response string. Why is this?
I know that this code is enumerating the list of tuples and I can call them properly, as I can change the code to the following and get the expected output:
for i, v in enumerate(response):
i += 1 # adds 1 to the index to make list indices correlate to a regular 1,2,3 style list
print i, v
Also, two quick asides before anyone asks:
I know there is currently no way to get out of this while loop. I'm just making sure each part of my code works before I move on to the next part. Which brings me to the point of the tuples.
When I get the code working, a 0 will produce the response message and loop again, asking the user to make a different selection, whereas a 1 will produce the appropriate response, break out of the loop, and move on to the next 'room' in the story... this way I can have as many 'rooms' for as long of a story as I want, the player does not have to 'die' each time they make an incorrect selection, and each 'room' can have any arbitrary amount of options and possible responses to choose from and I don't need to keep writing separate loops for each room.
There are a few problems here.
First, there's no good reason to iterate through all the numbers just to see if one of them matches selection; you already know that will be true if 1 <= selection <= len(response), and you can then just do response[selection-1] to get the v. (If you know anything about dicts, you might be able to see an even more convenient way to write this whole thing… but if not, don't worry about it.)
But if you really want to do this exhaustive search, you shouldn't print out There is an error man after any mismatch, because then you're always going to print it at least twice. Instead, you want to only print it if all of them failed to match. You can do this by keeping track of a "matched" flag, or by using a break and an else: clause on your for loop, whichever seems simpler, but you have to do something. See break and continue Statements, and else Clauses on Loops in the tutorial for more details.
But the biggest problem is that raw_input returns a string, and there's no way a string is ever going to be equal to a number. For example, try '1' == 1 in your interactive interpreter, and it'll say False. So, what you need to do is convert the user's input into a number so you can compare it. You can do that like this:
try:
selection = int(selection)
except ValueError:
print "That's not a number!"
continue
Seems like this is a job for dictionaries in python. Not sure if your assignment allows this, but here's my code:
def make_selections(response):
selection = raw_input('-> ')
print response.get(selection, err_msg)
resp_dict = {
'1':'You chose option one.',
'2':'You chose option two.',
'3':'You chose option three.'
}
err_msg = 'Sorry, you must pick one of these choices: %s'%sorted(resp_dict.keys())
make_selections(resp_dict)
The problem is that you are comparing a string to an integer. Selection is raw input, so it comes in as a str. Convert it to an int and it will evaluate as you expect.
You can check the type of a variable by using type(var). For example, print type(selection) after you take the input will return type 'str'.
def make_selections(response):
repeat = True
while repeat == True:
selection = raw_input('-> ')
for i, v in enumerate(response):
i +=1 # adds 1 to the index to make list indices correlate to a regular 1,2,3 style list
if int(selection) == i:
print v[1]
else:
print "There's an error man, what are you doing?!?!?"
I have an IRC bot that I made for automating stuff.
Here's a snippet of it:
def analyseIRCText(connection, event):
global adminList, userList, commandPat, flood
userName = extractUserName(event.source())
userCommand = event.arguments()[0]
escapedChannel = cleanUserCommand(config.channel).replace('\\.', '\\\\.')
escapedUserCommand = cleanUserCommand(event.arguments()[0])
#print userName, userCommand, escapedChannel, escapedUserCommand
if flood.has_key(userName):
flood[userName] += 1
else:
flood[userName] = 1
... (if flood[userName] > certain number do...)
So the idea is that flood thing is a dictionary where a list of users who have entered in a command to the bot in the recent... some time is kept, and how many times they've said so and so within that time period.
Here's where I run into trouble. There has to be SOMETHING that resets this dictionary so that the users can say stuff every once in awhile, no? I think that a little thing like this would do the trick.
def floodClear():
global flood
while 1:
flood = {} # Clear the list
time.sleep(4)
But what would be the best way to do this?
At the end of the program, I have a little line called:
thread.start_new_thread(floodClear,())
so that this thing doesn't get called at gets stuck in an infinite loop that halts everything else. Would this be a good solution or is there something better that I could do?
Your logic should be enough. If you have say:
if flood.has_key(userName):
flood[userName] += 1
else:
flood[userName] = 1
if flood[userName] > say 8:
return 0
That should make your bot ignore the user if he has spammed too many times within your given time period. What you have there should also work to clear up your flood dictionary.