Creating a generator in python - python

I am trying to create a generator that prints the next number on the list. This is a racket version:
(require racket/generator)
(define my-gen
(generator
(_)
(for ([x (list 1 2 3))])
(yield x)))
)
How it should work:
(my-gen) -> 1
(my-gen) -> 2
I also want to be able to use the function directly without having to initialize it at a specific point, so having the generator be the function that actually returns the result, as opposed to something like this:
l = [1, 2, 3]
def myGen():
for element in l:
yield element
g = myGen()
print(next(g))
What is the best way to go about this in python?

# Generator expression
(exp for x in iter)
# List comprehension
[exp for x in iter]
Iterating over the generator expression or the list comprehension will do the same thing. However, the list comprehension will create the entire list in memory first while the generator expression will create the items on the fly, so you are able to use it for very large.
In above generator will create generator object, to get data you have iterate or filter over it. It may be help you

Using functools.partial:
>>> import functools
>>> l = [1, 2, 3]
>>> my_gen = functools.partial(next, iter(l))
>>> my_gen()
1
>>> my_gen()
2
>>> my_gen()
3
>>> my_gen()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Is this what you are looking for?
number = 1
while(number<500 +1):
print('(my-gen) ->', number)
number +=1

Related

Unexpected results when comparing list comprehension with generator expression [duplicate]

This question already has answers here:
Generator Comprehension different output from list comprehension?
(4 answers)
Generator expressions vs. list comprehensions
(13 answers)
Closed 3 years ago.
I think I'm overlooking something simple, but I can't seem to figure out what exactly. Please consider the following code:
a = [2, 3, 4, 5]
lc = [ x for x in a if x >= 4 ] # List comprehension
lg = ( x for x in a if x >= 4 ) # Generator expression
a.extend([6,7,8,9])
for i in lc:
print("{} ".format(i), end="")
for i in lg:
print("{} ".format(i), end="")
I expected that both for-loops would produce the same result, so 4 5. However, the for-loop that prints the generator exp prints 4 5 6 7 8 9. I think it has something to do with the declaration of the list comprehension (Which is declared before the extend). But why is the result of the generator different, as it is also declared before extending the list? E.g. what is going on internally?
Generators aren't evaluated until you call next() on them which is what makes them useful, while list comprehensions are evaluated immediately.
So lc = [4,5] before extend and is therefore done.
lg is still the same value at the start so the extend still applies to the a which hasn't finished being evaluated within the generator, meaning that a gets extended before you start printing it which is why it will print out longer with the rest of the numbers as well.
Check it out like this:
>>> a = [2, 3, 4, 5]
>>> lg = ( x for x in a if x >= 4 )
>>> next(lg)
4
>>> next(lg)
5
>>> a.extend([6,7,8,9])
>>> next(lg)
6
However, if you were to try calling an extra next() before extend you'll get StopIteration because the generator is exhausted at that point and then you won't be able to call it any longer.
>>> a = [2, 3, 4, 5]
>>> lg = ( x for x in a if x >= 4 )
>>> next(lg)
4
>>> next(lg)
5
>>> next(lg)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> a.extend([6,7,8,9])
>>> next(lg)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
what is going on internally?
Generators are inherently lazy.
[ x for x in a if x >= 4 ] is evaluated as soon as it is executed.
( x for x in a if x >= 4 ) when this executes it just creates the generator. The loops itself is only evaluated/executed when the generator is consumed in one of the many ways possible ('manually' calling next, converting to another iterable type [list, tuple, set etc] or with a for loop).
The main advantage of generators being lazy is memory consumption. They do not need to store all the elements in memory, but only the current (or next, I should say) element.
The generator expression is lazily evaluated, so when you get back the generator object the code x for x in a if x >= 4 is not yet executed.
The for-in loop internally calls the built-in next() function for each iteration of the loop for that generator object. The next() call actually evaluates the code and that code points to the updated list which has the new set of values you added after the generator object was created.
>>> lg = ( x for x in a if x >= 4)
#evaluates the code and returns the first value
>>> next(lg)
4
>>> next(lg)
5
# if new values are added here to the list
# the generator will return them
But in the case of the list comprehension the generator object's next() method is immediately invoked and all the values are added in a list container using the values which was there in the beginning.
The built-in list() and the [] takes an iterable object as a parameter and constructs a list with the values returned from the iterable object. This happens immediately when you pass the iterable (in your case the generator object which is an iterable) to the list constructor.
But on the other hand if you simply execute the generator expression, you just get back the generator object which is just an iterable and also an iterator. So either you need to call next() on it to execute the code and get the value or use it in a for in iterable: loop which does it implicitly.
But remember once you exhaust the generator object by getting a StopIteration exception, and you add a new value in the list that value won't be returned from the next() call as the generator object can be consumed only once.
>>> a = [2, 3, 4, 5]
>>> lg = ( x for x in a if x >= 4)
>>> next(lg)
4
>>> next(lg)
5
>>> a.append(9)
>>> next(lg)
9
>>> next(lg)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
# lg is consumed
>>> a.append(10)
>>> next(lg)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Is there a generic way to add items to any iterable in python2?

I am trying to add items into any iterable in a type-agnostic way, but not all python iterables seem to have a standard interface/method for doing so.
lists use append(), sets use add(), etc.
Basically, something like this -
def add(anylist, item):
# adds 'item' into 'anylist'
# possibly return 'newlist' if 'anylist' is immutable (such as tuple, str)
return newlist
x = add([1,2,3], 4) # x is now [1,2,3,4]
y = add('abcd', 'e') # y is now "abcde"
z = add((1,2,3), 4) # z is now (1,2,3,4)
Currently, I am making do by handling individual types on a case-by-case basis like so-
>>> def add(anylist, item):
... if isinstance(anylist, list):
... anylist.append(item)
... elif isinstance(anylist, set):
... anylist.add(item)
... elif isinstance(anylist, str):
... anylist += item
... elif isinstance(anylist, tuple):
... anylist += (item,)
... return anylist
...
>>> print add([1,2,3], 4)
[1, 2, 3, 4]
>>> print add((1,2,3), 4)
(1, 2, 3, 4)
>>> print add("123", '4')
1234
>>> print add({1,2,3}, 4)
set([1, 2, 3, 4])
This is obviously sub-optimal because it doesn't work with every iterable.
Am I missing something?
I tried to make use of python's concatenate(+) operator to achieve this, but that didn't help either.
>>> x = [1,2,3]
>>> x += [4]
>>> x
[1, 2, 3, 4]
>>> y = "123"
>>> y += "4"
>>> y
'1234'
as there's no easy way to convert 'item' to an iterable type either.
For eg, the following -
item = '4'
x = '123'
x += str(item) # x = '1234'
works, but lists do not behave the same way -
>>> item = 4
>>> x = [1,2,3]
>>> x += list(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
There's no generic way to take one sequence an automatically create another sequence of the same type with one extra item. That's because not all sequences allow arbitrary values. For instance, a Python 3 range object (or an xrange in Python 2) computes its values on the fly, so there's no way to add a new value of your choice.
However, if you're only going to iterate over your sequence after adding the value to the end, you may be able to get what you want using itertools.chain, which returns an iterator.
import itertools
def add(iterable, value):
return itertools.chain(iterable, [value])
You won't be able to pass the iterator directly to a print statement, but you can perhaps use some other kind of container, like a list to hold the values so you can print them.
print list(add((1,2,3), 4)) # prints [1, 2, 3, 4], doesn't try to produce a tuple
You can do this by overloading operators in iterable classes, but the effort probably outweighs the benefit.
In any case, if you want to use + as a way of extending the list, you can create your own container class inheriting from list, and overload the .__add__ method:
class my_list(list):
def __add__(self, o):
self.__getitem__(self).extend(o)
a = my_list()
a += [1] #a=[1]
a += [2] #a=[1,2]
Similarly, you extend all iterables.
Your proposed method of if-else is probably easier to maintain with language updates, so worth keeping this in mind if you decide to start extending the inbuilt classes.

what is the best way to calculate the power of several ints in a list using python3?

So what I have is a list of integers such as [2, 2, 2, 3, ..., n] the length of the list can be anywhere from 1 to 100. What I need to do is calculate the powers of all the numbers. This should be super easy but there is also the stipulation that you must raise each number to the power of the next number to the power of the next number and so on. FOR EXAMPLE: if the list contained [2, 3, 4] first I would need to calculate the power of 3^4 and then 2^(answer of 3^4). If the list is longer it would need to calculate the value for all of them. The example above [2, 3, 4] should return 2^81 which should be something like 2417851639229258349412352 according to wolfram. Any help would be awesome even if its just an algorithm (I can figure out the code from there) I've just been struggling to come up with a sufficient algorithm for some time now.
here is some code I have now...
temp = []
length = 0
for num in powernumbers:
for index in num:
if index.isdigit():
temp.append(index)
length = len(temp)
if length > 0:
for j in reversed(range(len(temp))):
_temp = math.pow(int(temp[j-1]), int(temp[j]))
#THE ABOVE CODE WILL ONLY WORK FOR A LIST OF LEN 2
print(_temp)
#needs math.pow(0,(math.pow(1,(math.pow(2,...)))))
print("TEMP:", temp)
Once again any help is super appreciated!
You could use functools.reduce with reversed list:
>>> from functools import reduce
>>> l = [2, 3, 4]
>>> reduce(lambda x, y: y**x, reversed(l))
2417851639229258349412352
reduce takes two arguments: function and iterable. Then it will cumulatively apply the function to reduce the iterable to single value. First parameter of the function is reduced value and second parameter is item from iterable. Since we want to process the list in reverse order we're using reversed so that 3**4 will be executed first.
Note that on Python 2 reduce is a builtin so there's no need to import anything.
>>> numbers = [2,3,4] # your list
>>> result = 1
>>> for n in reversed(numbers):
result = n**result
>>> result
2417851639229258349412352
>>>
first initialize the result on 1, then go through the list in reverse order raising the number to the previous result, which the first time is 1 resulting for this example in
result = 4**1 -> 4
result = 3**4 -> 81
result = 2**81 -> 2417851639229258349412352
but be aware, this Nested exponentials will grow very very fast, and you more likely would get a memory error for the insanely big numbers
>>> result = 1
>>> powers = [2,2,2,2,2,2]
>>> for n in reversed(powers):
result = n**result
Traceback (most recent call last):
File "<pyshell#60>", line 2, in <module>
result = n**result
MemoryError
>>>
Pop the last element off of the list, then go through the list backwards and keep exponentiating.
powernumbers = [2, 3, 4]
result = powernumbers.pop()
for num in powernumbers[::-1]:
result = num**result
The result:
>>> result
2417851639229258349412352

What is an expression such as [d[k] for k in d] called?

Python newbie here.
In learning about Python I have come across some very nice, succinct bits of code such as:
[d[k] for k in d]
I can see immediately that there are lots of possibilities for these kinds of expressions ("these kinds" meaning contained inside a []).
I'm unsure of what this kind of expression is called, and so I am having trouble searching for information on how to use it. Would be great for some knowledgeable folks to direct me toward the part of the Python docs, or other resources, that discusses these, and perhaps provide some suggestions of how to effectively use them.
The code you posted is an expression, not a statement.
It is commonly called a list comprehension and its basic structure is:
[item for item in iterable if condition]
where the if condition clause is optional. The result is a new list object created from the items in iterable (possibly filtered by if condition):
>>> [x for x in (1, 2, 3)] # Get all items in the tuple (1, 2, 3).
[1, 2, 3]
>>> [x for x in (1, 2, 3) if x % 2] # Only get the items where x % 2 is True.
[1, 3]
>>>
In addition, there are dictionary comprehensions:
{key:value for key, value in iterable if condition}
and set comprehensions:
{item for item in iterable if condition}
which each do the same thing as the list comprehension, but produce dictionaries or sets respectively.
Note however that you need Python 2.6 or greater to use these constructs.
A final tool that you should be aware of is a generator expression:
(item for item in iterable if condition)
Similar to the list comprehension, it creates a generator object which produces its items lazily (one at a time as they are needed):
>>> (x for x in (1, 2, 3))
<generator object <genexpr> at 0x02811A80>
>>> gen = (x for x in (1, 2, 3))
>>> next(gen) # Advance the generator 1 position.
1
>>> next(gen) # Advance the generator 1 position.
2
>>> next(gen) # Advance the generator 1 position.
3
>>> next(gen) # StopIteration is raised when there are no more items.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>

python reduce error?

The following is my python code:
>>> item = 1
>>> a = []
>>> a.append((1,2,3))
>>> a.append((7,2,4))
>>> sums=reduce(lambda x:abs(item-x[1]),a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 1 argument (2 given)
>>>
How can I fix it?
Thanks!
Your lambda takes only one argument, but reduce requires a function that takes two arguments. Make your lambda take two arguments.
Since you didn't say what you want this code to do, I'll just guess:
the_sum=reduce(lambda x,y:abs(y[1]-x[1]),a)
Your problem itself is a bit unclear. Anyway, i have taken just assumption--
>>> a = []
>>> a.append((1,2,3))
>>> a.append((7,2,4))
>>> a
[(1, 2, 3), (7, 2, 4)] # list of tuples
I am assuming that you might be interested in getting the sum of all the elements in the list. If that is the problem then that could be solved in 2 steps
1) The first step should be to flatten the list.
2) And then add all the elements of the list.
>>> new_list = [y for x in a for y in x] # List comprehension used to flatten the list
[1, 2, 3, 7, 2, 4]
>>> sum(new_list)
19
One liner
>>> sum([y for x in a for y in x])
19
Another assumption, if your problem is to minus every element of tuple by item in the list then use this:
>>> [tuple(map(lambda y: abs(item - y), x)) for x in a]
[(0, 1, 2), (6, 1, 3)] # map function always returns a list so i have used tuple function to convert it into tuple.
If the problem is something else then please elaborate.
PS: Python List comprehension is far better and efficient than anything else.
reduce expects the function it is given to accept 2 arguments. For every item in the iterable it will pass the function the current item, and the previous return value from the function. So, getting the sum of a list is reduce(lambda: x,y: x+y, l, 0)
If I understand correctly, to get the behavior you were trying to get, change the code to:
a_sum = reduce(lambda x,y: x + abs(item-y[1]), a, 0)
But I might be mistaken as to what you were trying to get.
Further information is in the reduce function's docstring.

Categories

Resources