Python generator on dictionary view does not seem to be lazily evaluated - generator-expression

It's my understanding that python generator expressions return lazily evaluated comprehensions, and view objects on dictionaries update when their underlying dictionary is changed.
Given the following code (Python 2.7.6 Windows):
d = {}
gt_three = (x for x in d.viewkeys() if x > 3)
print list(gt_three)
d[1] = 'one'
d[4] = 'four'
print list(gt_three)
I would expect output like:
[]
[4]
Instead, I'm receiving:
[]
[]
If I recreate the generator after adding to the dictionary, or print the view itself, I do see the expected output.
What am I not understanding about generators? I'm quite new to python.

A generator expression returns an iterator, which you can only iterate over once. Attempts to iterate over an iterator after the first time will always see it as empty.

Related

Math-like way to define a set in Python: technical name [duplicate]

Can someone explain the last line of this Python code snippet to me?
Cell is just another class. I don't understand how the for loop is being used to store Cell objects into the Column object.
class Column(object):
def __init__(self, region, srcPos, pos):
self.region = region
self.cells = [Cell(self, i) for i in xrange(region.cellsPerCol)] #Please explain this line.
The line of code you are asking about is using list comprehension to create a list and assign the data collected in this list to self.cells. It is equivalent to
self.cells = []
for i in xrange(region.cellsPerCol):
self.cells.append(Cell(self, i))
Explanation:
To best explain how this works, a few simple examples might be instructive in helping you understand the code you have. If you are going to continue working with Python code, you will come across list comprehension again, and you may want to use it yourself.
Note, in the example below, both code segments are equivalent in that they create a list of values stored in list myList.
For instance:
myList = []
for i in range(10):
myList.append(i)
is equivalent to
myList = [i for i in range(10)]
List comprehensions can be more complex too, so for instance if you had some condition that determined if values should go into a list you could also express this with list comprehension.
This example only collects even numbered values in the list:
myList = []
for i in range(10):
if i%2 == 0: # could be written as "if not i%2" more tersely
myList.append(i)
and the equivalent list comprehension:
myList = [i for i in range(10) if i%2 == 0]
Two final notes:
You can have "nested" list comrehensions, but they quickly become hard to comprehend :)
List comprehension will run faster than the equivalent for-loop, and therefore is often a favorite with regular Python programmers who are concerned about efficiency.
Ok, one last example showing that you can also apply functions to the items you are iterating over in the list. This uses float() to convert a list of strings to float values:
data = ['3', '7.4', '8.2']
new_data = [float(n) for n in data]
gives:
new_data
[3.0, 7.4, 8.2]
It is the same as if you did this:
def __init__(self, region, srcPos, pos):
self.region = region
self.cells = []
for i in xrange(region.cellsPerCol):
self.cells.append(Cell(self, i))
This is called a list comprehension.

python3 list creation from class makes a global list rather than a series iterated ones

So here is the problem I am having. I am trying to iterate the makeAThing class, and then create a list for the iteration using the makeAList class. Instead of making seperate lists for each iteration of makeAThing, it is making one big global list and adding the different values to it. Is there something I am missing/don't know yet, or is this just how python behaves?
class ListMaker(object):
def __init__(self,bigList = []):
self.bigList = bigList
class makeAThing(object):
def __init__(self,name = 0, aList = []):
self.name = name
self.aList = aList
def makeAList(self):
self.aList = ListMaker()
k = []
x = 0
while x < 3:
k.append(makeAThing())
k[x].name = x
k[x].makeAList()
k[x].aList.bigList.append(x)
x += 1
for e in k:
print(e.name, e.aList.bigList)
output:
0 [0, 1, 2]
1 [0, 1, 2]
2 [0, 1, 2]
the output I am trying to achieve:
0 [0]
1 [1]
2 [2]
After which I want to be able to edit the individual lists and keep them assigned to their iterations
Your init functions are using mutable default arguments.
From the Python documentation:
Default parameter values are evaluated from left to right when the
function definition is executed. This means that the expression is
evaluated once, when the function is defined, and that the same
“pre-computed” value is used for each call. This is especially
important to understand when a default parameter is a mutable object,
such as a list or a dictionary: if the function modifies the object
(e.g. by appending an item to a list), the default value is in effect
modified. This is generally not what was intended. A way around this
is to use None as the default, and explicitly test for it in the body
of the function, e.g.:
def whats_on_the_telly(penguin=None):
if penguin is None:
penguin = []
penguin.append("property of the zoo")
return penguin
In your code, the default argument bigList = [] is evaluated once - when the function is defined - the empty list is created once. Every time the function is called, the same list is used - even though it is no longer empty.
The default argument aList = [] has the same problem, but you immediately overwrite self.aList with a call to makeAList, so it doesn't cause any problems.
To verify this with your code, try the following after your code executes:
print(k[0].aList.bigList is k[1].aList.bigList)
The objects are the same.
There are instances where this behavior can be useful (Memoization comes to mind - although there are other/better ways of doing that). Generally, avoid mutable default arguments. The empty string is fine (and frequently used) because strings are immutable. For lists, dictionaries and the sort, you'll have to add a bit of logic inside the function.

Getting a list item with the max evaluation in a list of tuples in Python

Given this list of tuples:
my_tuples = [(1,2), (3,4)]
and the following evaluation function:
def evaluate(item_tuple):
return item_tuple[0] * 2
Question: how can I get the list item (tuple) that has the highest evaluation value? (I'm guessing I can use a list comprehension for this)
def max_item(tuples_list, evaluation_fn):
'''Should return the tuple that scores max using evaluation_fn'''
# TODO Implement
# This should pass
assertEqual((3,4), max_item(my_tuples, evaluate))
Correct me if I'm wrong, you want the list of tuples sorted by the result of multiplying one of the values inside the tuple with x (in your example above it would be the first value of the tuple multiplied by 2).
If so, you can do it this way:
from operator import itemgetter
sorted(l, key=itemgetter(0 * 2), reverse=True)
I managed to do it this way:
def max_item(tuples_list, evaluation_fn):
zipped = zip(map(evaluation_fn, tuples_list), tuples_list)
return max(zipped, key=lambda i:i[0])[1]
I don't know if there's a simpler (more pythonic?) way to solve it though.
Edit
I figured how I could use a list comprehension to make it more succinct/readable:
def max_item(tuples_list, evaluation_fn):
return max([(evaluation_fn(i), i) for i in tuples_list])[1]

Iteration to Recurssion

so myListToPyList(lst): takes lst, a MyList object and returns a Python list containing the same data
def myListToPyList(lst):
return myListToPyListRec(lst.head)
here's my helper function:
def myListToPyListRec(node):
if node == None:
return
else:
st1 = []
st1.append(node.data)
myListToPyListRec(node.next)
return st1
it's not working correctly.
Now here is my iterative solution that works correctly:
def myListToPyList(lst):
"""
Takes a list and returns a python list containing
the same data
param; lst
return; list
"""
st1 = []
curr = lst.head
while curr != None:
st1.append(curr.data)
curr = curr.next
return st1
Your current recursive code doesn't work because each time it gets called, it creates a new empty list, adds a single value to the list, then recurses (without passing the list along). This means that when the last item in the link list is being processed, the call stack will have N one-element Python lists (where N is the number of list nodes).
Instead, you should create the list just once, in your non-recursive wrapper function. Then pass it along through all of the recursion:
def myListToPyList(lst):
result_list = [] # create just one Python list object
myListToPyListRec(lst.head, result_list) # pass it to the recursive function
return result_list # return it after it has been filled
def myListToPyListRec(node, lst):
if node is not None # the base case is to do nothing (tested in reverse)
lst.append(node.data) # if node exists, append its data to lst
myListToPyListRec(node.next, lst) # then recurse on the next node
Because Python lists are mutable, we don't need to return anything in our recursive calls (None will be returned by default, but we ignore that). The list referred to by result_list in myListToPyList is the same object referred to by lst in each of the recursive calls to myListToPyListRec. As long as the recursive function mutates the object in place (e.g. with append) rather than rebinding it, they'll all see the same thing.
Note that recursion is going to be less eficient in Python than iteration, since function calls have more overhead than just updating a couple variables.
A while loop is equivalent to tail recursion, and vice versa. (One reason Python does not have automatic tail-call elimination is that the 'vice versa' part is rather easy.) The tail recursion requires that you add an accumulator parameter to be returned in the base case. Although I do not have a linked list for testing, I believe the following should work. (If not, this is close.) Python's default arguments make the helper either easier or unnecessary.
def myListToPyListRec(node, py_list=[]):
if node
py_list.append(node.data)
return myListToPyListRec(node.next, py_list)
else:
return py_list

Print doesn't print when it's in map, Python

primes = [2,3,5,7..] (prime numbers)
map(lambda x:print(x),primes)
It does not print anything.
Why is that?
I've tried
sys.stdout.write(x)
too, but doesn't work either.
Since lambda x: print(x) is a syntax error in Python < 3, I'm assuming Python 3. That means map returns a generator, meaning to get map to actually call the function on every element of a list, you need to iterate through the resultant generator.
Fortunately, this can be done easily:
list(map(lambda x:print(x),primes))
Oh, and you can get rid of the lambda too, if you like:
list(map(print,primes))
But, at that point you are better off with letting print handle it:
print(*primes, sep='\n')
NOTE: I said earlier that '\n'.join would be a good idea. That is only true for a list of str's.
This works for me:
>>> from __future__ import print_function
>>> map(lambda x: print(x), primes)
2
3
5
7
17: [None, None, None, None]
Are you using Python 2.x where print is a statement, not a function?
Alternatively, you can unpack it by putting * before map(...) like the following
[*map(...)]
or
{*map(...)}
Choose the output you desire, a list or a dictionary.
Another reason why you could be seeing this is that you're not evaluating the results of the map function. It returns a generator (an iterable) that evaluates your function lazily and not an actual list.
primes = [2,3,5,7]
map(print, primes) # no output, because it returns a generator
primes = [2,3,5,7]
for i in map(print, primes):
pass # prints 2,3,5,7
Alternately, you can do list(map(print, primes)) which will also force the generator to be evaluated and call the print function on each member of your list.

Resources