List comprehension break down, deconstruction and/or disassemble - python

In web2py I have been trying to break down this list comprehension so I can do what I like with the categories it creates. Any ideas as to what this breaks down to?
def menu_rec(items):
return [(x.title,None,URL('shop', 'category',args=pretty_url(x.id, x.slug)),menu_rec(x.children)) for x in items or []]
In addition the following is what uses it:
response.menu = [(SPAN('Catalog', _class='highlighted'), False, '',
menu_rec(db(db.category).select().as_trees()) )]
So far I've come up with:
def menu_rec(items):
for x in items:
return x.title,None,URL('shop', 'category',args=pretty_url(x.id, x.slug)),menu_rec(x.children))
I've got other variations of this but, every variation only gives me back 1(one) category, when compared to the original that gives me all the categories.
Can anyone see where I'm messing this up at? Any and all help is appreciated, thank you.

A list comprehension builds a list by appending:
def menu_rec(items):
result = []
for x in items or []:
url = URL('shop', 'category', args=pretty_url(x.id, x.slug))
menu = menu_rec(x.children) # recursive call
result.append((x.title, None, url, menu))
return result
I've added two local variables to break up the long line somewhat, and to show how it recursively calls itself.
Your version returned directly out of the for loop, during the first iteration, and never built up a list.

You don't want to do return. Instead append to a list and then return the list:
def menu_rec(items):
result = []
for x in items:
result.append(x.title,None,URL('shop', 'category',args=pretty_url(x.id, x.slug)),menu_rec(x.children)))
return result
If you do return, it will return the value after only the first iteration. Instead, keep adding it to a list and then return that list at the end. This will ensure that your result list only gets returned when all the values have been added instead of just return one value.

Related

return function delete some data and print function add None in the output

when I use the print function it returns None.
when I replace it with return some of the data is deleted
def email_list(domains):
for domain in domains:
for user in domains[domain]:
return("{}#{}".format(user,domain))
print(email_list({"gmail.com": ["clark.kent", "diana.prince", "peter.parker"], "yahoo.com": ["barbara.gordon", "jean.grey"], "hotmail.com": ["bruce.wayne"]}))`
You are probably looking for this -
def email_list(domains):
lst = []
for domain in domains:
for user in domains[domain]:
lst.append("{}#{}".format(user,domain))
return lst
print(email_list({"gmail.com": ["clark.kent", "diana.prince", "peter.parker"], "yahoo.com": ["barbara.gordon", "jean.grey"], "hotmail.com": ["bruce.wayne"]}))
OUTPUT :
['clark.kent#gmail.com', 'diana.prince#gmail.com', 'peter.parker#gmail.com', 'barbara.gordon#yahoo.com', 'jean.grey#yahoo.com', 'bruce.wayne#hotmail.com']
In your code, the function would end on the first iteration itself. However, you are looking for first creating a list and at last after you are done with your looping, only then return the list.

Dataquest: I've just learned how to define a function in python. Now I want to run it in a loop.

I am a python beginner and I learn using dataquest.
I want to use a self-defined function in a loop to check every item in a list, whether it is a color movie or not and add the results (True, False) to a list. Right now the function returns False only, also way to many times. Any hints what I did wrong?
wonder_woman = ['Wonder Woman','Patty Jenkins','Color',141,'Gal Gadot','English','USA',2017]
def is_usa(input_lst):
if input_lst[6] == "USA":
return True
else:
return False
def index_equals_str(input_lst, index, input_str):
if input_lst[index] == input_str:
return True
else:
return False
wonder_woman_in_color = index_equals_str(input_str="Color", index=2, input_lst=wonder_woman)
# End of dataquest challenge
# My own try to use the function in a loop and add the results to a list
f = open("movie_metadata.csv", "r")
data = f.read()
rows = data.split("\n")
aufbereitet = []
for row in rows:
einmalig = row.split(",")
aufbereitet.append(einmalig)
# print(aufbereitet)
finale_liste = []
for item in aufbereitet:
test = index_equals_str(input_str="Color", index=2, input_lst=aufbereitet)
finale_liste.append(test)
print(finale_liste)
Also at pastebin: https://pastebin.com/AESjdirL
I appreciate your help!
The problem is in this line
test = index_equals_str(input_str="Color", index=2, input_lst=aufbereitet)
The input_lst argument should be input_lst=item. Right now you are passing the whole list of lists to your function everytime.
The .csv file is not provided but I assume the reading is correct and it returns a list like the one you provided in the first line of your code; in particular, that you are trying to pack the data in a list of lists (the einmalig variable is a list obtained by the row of the csv file, then you append each einmalig you find in another list, aufbereitet).
The problem is not in the function itself but in the parameters you give as inputs: when you do
test = index_equals_str(input_str="Color", index=2, input_lst=aufbereitet)
you should see that the third parameter is not a list corresponding to the single movie data but the whole list of movies. This means that the Python interpreter, in the function, does this iteration for every item in aufbereitet (that is, iterates for n times where n is aufbereitet's length):
if aufbereitet[2] == "Color":
return True
else:
return False
It is clear that even if the movie is in color, the comparison between a list (an element of aufbereitet) and a string returns False by default since they are different types.
To correct the issue just change the line
test = index_equals_str(input_str="Color", index=2, input_lst=aufbereitet)
with
test = index_equals_str(input_str="Color", index=2, input_lst=item)
since, when you use the for loop in that way, the variable item changes at each iteration with the elements in aufbereitet.
Notice that if you're learning that's still ok to use functions but you can use an inline version of the algorithm (that's what Python is famous for). Using
finale_liste = [item[2] == "Color" for item in aufbereitet]
you obtain the list without going to define a function and without using the for loop. That's called list comprehension.
Another thing you can do to make the code more Pythonic - if you want to use the functions anyway - is to do something like
def index_equals_str(input_lst, index, input_str):
return input_lst[index] == input_str
that has the same result with less lines.
Functional programming is sometimes more readable and adaptable for such tasks:
from functools import partial
def index_equals_str(input_lst, index=1, input_str='Null'):
return input_lst[index] == input_str
input_data = [['Name1', 'Category1', 'Color', 'Language1'],
['Name2', 'Category2', 'BW', 'Language2']]
result = list(map(partial(index_equals_str, input_str='Color', index=2), input_data))
# output
# [True, False]

Python Json loops

I am trying to parse a JSON page in Python, it is contained in a variable reddit_front.
I am trying to get the sum of all "ups" in this page. I do have the right answer which is the following:
def total_ups():
j=json.loads(reddit_front)
return sum(c["data"]["ups"] for c in j["data"]["children"])
However why does the following loop give me only 1 item and not iterate over?
def total_ups():
j=json.loads(reddit_front)
for c in j["data"]["children"]:
i = c["data"]["ups"]
a = +i
return a
PS: ok, all good points and thx for the negative reputations points, it's fair call I wasn't precise in my question.
return will stop the loop, try appending it to a list then you can join it or whatever you need to so you can get the data.
Example:
def total_ups():
a = list()
j=json.loads(reddit_front)
for c in j["data"]["children"]:
i = c["data"]["ups"]
a.append(+i)
return a
print(total_ups()) # returns list
print(", ".join(total_ups)) # returns a string separated by commas
Maybe...
def total_ups():
children = json.loads(reddit_front)["data"]["children"]
return sum(c["data"]["ups"] for c in children)

Returning more than one result

I'm using the following code:
def recentchanges(bot=False,rclimit=20):
"""
#description: Gets the last 20 pages edited on the recent changes and who the user who edited it
"""
recent_changes_data = {
'action':'query',
'list':'recentchanges',
'rcprop':'user|title',
'rclimit':rclimit,
'format':'json'
}
if bot is False:
recent_changes_data['rcshow'] = '!bot'
else:
pass
data = urllib.urlencode(recent_changes_data)
response = opener.open('http://runescape.wikia.com/api.php',data)
content = json.load(response)
pages = tuple(content['query']['recentchanges'])
for title in pages:
return title['title']
When I do recentchanges() I only get one result. If I print it though, all the pages are printed.
Am I just misunderstanding or is this something relating to python?
Also, opener is:
cj = CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
Once a return statment is reached in a function, that functions execution ends, so the second return does not get executed. In order to return both values you need to pack them in a list or tuple:
...
returnList = [title['title'] for title in pages]
return returnList
This uses list comprehension to make a list of all the object you want the function to return and then returns it.
Then you can unpackage individual results from the return list:
answerList = recentchanges()
for element in answerList:
print element
The problem you are having is that a function ends at the first return line it sees.
So. in the line
for title in pages:
return title['title']
It returns only the first value: pages[0]['title'].
One way around this is to use a list-comprehension i.e.
return [ title['title'] for title in pages ]
Another option is to make recentchanges a generator and use yield.
for title in pages:
yield title['title']
return ends the function. So the loop only executes once, because you're returning in the loop. Think about it: how would the caller get subsequent values once the first value has been returned? Would they have to call the function again? But that would start it over again. Should Python wait until the loop is complete to return all the values at once? But where would they go and how would Python know to do this?
You might provide a generator here by yielding rather than returning it. You could also just return a generator:
return (page['title'] for page in pages)
Either way, the caller can then convert it to a list if desired, or iterate over it directly:
titles = list(recentchanges())
# or
for title in recentchanges():
print title
Alternatively, you can just return the list of titles:
return [page['title'] for page in pages]
Since you use return, your function will end after returning first value.
There are two alternatives;
you can append the titles to a list and return that, or
you can use yield instead of return to turn your function into a generator.
The latter is probably more pythonic, because you could then us it like this:
for title in recentchanges():
# do something with the title
pass

How to add items to a dictionary

people. I'm python newbie. I have two def functions as below under a class.
def add_item(self, itemID, itemlist):
lines = []
self.itemID = itemID
self.itemlist = itemlist
for line in self.itemID, itemlist:
lines.append(line)
and
def get_keys(self):
i = []
i.append(self.itemID)
return i
If I do
example.add_item('abc', item list)
example.add_item('abcd', item list)
example.add_item('abce', item list)
then when I do
example.get_keys()
It should give:
['abc', 'abcd', 'abce']
but mine only gives the latest one that is ['abce'].
Can anyone please let me know how to fix?
If I understand correctly, you want to add several couple of key and item_list to your example, and be able to retrieve the keys you added so far ? The easiest is to store the keys and the itemlist in two lists
Assuming that you initialize your object as such
def __init__(self, *args, **kwargs):
self.itemID = []
self.itemlist = []
...
Now, your add_item can simplify in
def add_item(self, itemID, itemlist):
self.itemID.append(itemID)
self.itemlist.append(itemlist)
and your get_key is only:
def get_keys():
return self.itemID
Note that the get_key is exactly the one you have suggested, just simpler (no need to create a temporary list).
When you do
lines = []
for line in self.itemID, itemlist:
lines.append(line)
line first takes the value self.itemID, then itemlist. Eventually, your lines is just [self.itemID, itemlist]. Probably not what you had in mind.
To add a new key to a dictionary, just assign it.
dict['new_key'] = 'value'
Perhaps
i.extend(self.itemID)
Might be what you are looking for
It looks like you are overwriting the item each time you add it.
When you call add_item, you are creating this variable "lines" that is never used again, and item_id and item_list are over-written with the new inputs.
You could also use the built-in method update:
example.update({'Key':'value'})
def add_item(self, itemID, itemlist):
lines = []
You are initializing your lines variable with empty list...
So, each time you invoke this method, it create a new list, and add the item to it..
You can rather return your lines from this method and store it in some variable where you are invoking this method..
Or, just declare lines as instance variable.
example = Example();
example.lines = []
example.lines.extend(example.add_item(itemId1, item_list1));
example.lines.extend(example.add_item(itemId2, item_list2));
Or, you can rather add your itemId and list to dictionary __dict__ of your class..
dict[itemId] = value;
** NOTE: - Just saw that, you have not used your for-loop correctly.. You don't iterate over two iterable like this..
You need to go through a good Python Book.. Or rather, Python Documentation..
First thing I see: You are iterating over two elements at once which is usually done by using zip(), at least if both elements are lists. Otherwise just use the container you want to loop over.
for id,line in zip(self.itemID, itemlist):
lines.append(line)
But I don't see any dict...

Categories