Why does `yield from` in a generator expression yield `None`s? - python

I have the following code:
import itertools
for c in ((yield from bin(n)[2:]) for n in range(10)):
print(c)
The output is:
0
None
1
None
1
0
None
1
1
None
... etc. Why do the Nones appear? If I instead have:
def hmm():
for n in range(10):
yield from bin(n)[2:]
for c in hmm():
print(c)
Then I get what I would expect:
0
1
1
0
1
1
... etc. Further, is there a way to write it as the generator expression to get the same result as the latter?

yield is an expression, and its value is whatever is sent into the generator with send. If nothing is sent in, the value of yield is None. In your example yield from yields the values from the list, but the value of the yield from expression itself is None, which is yielded at each iteration of the enclosing generator expression (i.e., every value of range(10)).
Your example is equivalent to:
def hmm():
for n in range(10):
yield (yield from bin(n)[2:])
for item in hmm():
print(item)
Note the extra yield.
You will always have this issue if you try to use yield in a generator expression, because the generator expression already yields its target values, so if you add an explicit yield, you are adding an extra expression (the yield expression) whose value will also be output in the generator. In other words, something like (x for x in range(5)) already yields the numbers in range(5); if you do something like ((yield x) for x in range(5)), you're going to get the values of the yield expression in addition to the numbers.
As far as I know, there is no way to get the simple yield from behavior (without extra Nones) using a generator comprehension. For your case, I think you can achieve what you want by using itertools.chain.from_iterable:
for c in itertools.chain.from_iterable(bin(n)[2:] for n in range(10)):
print(c)
(Edit: I realized you can get the yield from behavior in a generator comprehension by using nested for clauses: x for n in range(10) for x in bin(n)[2:]. However, I don't think this is any more readable than using itertools.chain.)

Related

To return elements of a list from a function

I want to define a function that takes a list as its argument and then returns the elements in order.
For example:
def iterator(lis):
for e in range(len(lis)):
return lis[e]
l=input("Enter list elements:").split()
x=iterator(l)
print(x)
But this just returns the first value of the list as:
Enter list elements:12 23 34
12
How can I print all the values in successive lines?
You can use yield in order to build a generator, here's the official documentation about Generators
What is a generator in Python?
A Python generator is a function which returns a generator iterator
(just an object we can iterate over) by calling yield. yield may be
called with a value, in which case that value is treated as the
"generated" value.
I also want to share an example, be sure to read the comments:
def iterator(lis):
for e in range(len(lis)):
yield lis[e]
l=input("Enter list elements:").split()
# A generator returns an Iterable so you should
# loop to print
for number in iterator(l):
print(number)
# Or use list
result = list(iterator(l))
print(result)
Output
1
2
3
['1', '2', '3']
You probably want yield, as return causes the function call to end immediately. But yield just produces another iterator; you still need to iterate over the result to print them all, rather than simply printing the iterator itself.
def iterator(lis):
for e in range(len(lis)):
yield lis[e]
...
for element in x:
print(element)
Of course, you are pretty much just reimplementing the existing list iterator here; an equivalent definition would be
def iterator(lis):
yield from lis
What you might want instead is to do something like
x = '\n'.join(l)
print(x)
which creates a string by iterating over l and joining the elements using \n. The resulting multiline string can then be printed.
It will print only one element if you do return
def iterator(lis):
for e in range(len(lis)):
return lis[e]
l=input("Enter list elements:").split()
x=iterator(l)
for y in x: print(y)
Use:
def iterator(list):
for e in range(len(list)):
a= list[e]
print(a)
l=a,b,c=input().split()
a=iterator(l)
Use:
[print(i) for i in input("Enter list elements:").split(" ")]
return causes the function to stop after it hits the statement. So your for loop only ever runs once.
You could use yield as mentioned in the other answers, but I really don't think you need a function in this situation. Because the function is just going to return the list that it took as an argument. What you should do is something like this:
i = input("Enter list elements: ").split()
for x in i:
print(x)
It's that simple.
def iterator(lis):
for e in range(len(lis)):
print( lis[e])
l=input("Enter list elements:").split()
iterator(l)
if you want to do some operations for each item in the list, then you should accomodate those within the function.
Use of print instead of return will give you the expected output..try it once

Python: general iterator or pure function for testing any condition across list

I would like to have a function AllTrue that takes three arguments:
List: a list of values
Function: a function to apply to all values
Condition: something to test against the function's output
and return a boolean of whether or not all values in the list match the criteria.
I can get this to work for basic conditions as follows:
def AllTrue(List, Function = "Boolean", Condition = True):
flag = True
condition = Condition
if Function == "Boolean"
for element in List:
if element != condition:
flag = False
break
else:
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
Since python doesn't have function meant for explicitly returning if something is True, I just make the default "Boolean". One could clean this up by defining TrueQ to return True if an element is True and then just mapping TrueQ on the List.
The else handles queries like:
l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
AllTrue(l, len, 2)
#False
testing if all elements in the list are of length 2. However, it can't handle more complex conditions like >/< or compound conditions like len > 2 and element[0] == 15
How can one do this?
Cleaned up version
def TrueQ(item):
return item == True
def AllTrue(List, Function = TrueQ, Condition = True):
flag = True
condition = Condition
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
and then just call AllTrue(List,TrueQ)
Python already has built-in the machinery you are trying to build. For example to check if all numbers in a list are even the code could be:
if all(x%2==0 for x in L):
...
if you want to check that all values are "truthy" the code is even simpler:
if all(L):
...
Note that in the first version the code is also "short-circuited", in other words the evaluation stops as soon as the result is known. In:
if all(price(x) > 100 for x in stocks):
...
the function price will be called until the first stock is found with a lower or equal price value. At that point the search will stop because the result is known to be False.
To check that all lengths are 2 in the list L the code is simply:
if all(len(x) == 2 for x in L):
...
i.e. more or less a literal translation of the request. No need to write a function for that.
If this kind of test is a "filter" that you want to pass as a parameter to another function then a lambda may turn out useful:
def search_DB(test):
for record in database:
if test(record):
result.append(record)
...
search_DB(lambda rec: all(len(x) == 2 for x in rec.strings))
I want a function that takes a list, a function, and a condition, and tells me if every element in the list matches the condition. i.e. foo(List, Len, >2)
In Python >2 is written lambda x : x>2.
There is (unfortunately) no metaprogramming facility in Python that would allow to write just >2 or things like ยท>2 except using a string literal evaluation with eval and you don't want to do that. Even the standard Python library tried going down that path (see namedtuple implementation in collections) but it's really ugly.
I'm not saying that writing >2 would be a good idea, but that it would be nice to have a way to do that in case it was a good idea. Unfortunately to have decent metaprogramming abilities you need a homoiconic language representing code as data and therefore you would be programming in Lisp or another meta-language, not Python (programming in Lisp would indeed be a good idea, but for reasons unknown to me that approach is still unpopular).
Given that, the function foo to be called like
foo(L, len, lambda x : x > 2)
is just
def foo(L, f=lambda x : x, condition=lambda x: x):
return all(condition(f(x)) for x in L)
but no Python programmer would write such a function, because the original call to foo is actually more code and less clear than inlining it with:
all(len(x) > 2 for x in L)
and requires you to also learn about this thing foo (that does what all and a generator expression would do, just slower, with more code and more obfuscated).
You are reinventing the wheel. Just use something like this:
>>> l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
>>> def all_true(iterable, f, condition):
... return all(condition(f(e)) for e in iterable)
...
>>> def cond(x): return x == 2
...
>>> all_true(l, len, cond)
False
You can define a different function to check a different condition:
>>> def cond(x): return x >= 1
...
>>> all_true(l, len, b)
True
>>>
And really, having your own function that does this seems like overkill. For example, to deal with your "complex condition" you could simply do something like:
>>> l = [[0,2],[0,1,2],[0,1,3,4]]
>>> all(len(sub) > 2 and sub[0] == 5 for sub in l)
False
>>> all(len(sub) > 1 and sub[0] == 0 for sub in l)
True
>>>
I think the ideal solution in this case may be:
def AllTrue(List, Test = lambda x:x):
all(Test(x) for x in List)
This thereby allows complex queries like:
l = [[0, 1], [1, 2, 3], [2, 5]]
AllTrue(l, lambda x: len(x) > 2 and x[0] == 1)
To adhere to Juanpa's suggestion, here it is in python naming conventions and an extension of what I posted in the question now with the ability to handle simple conditions like x > value.
from operator import *
all_true(a_list, a_function, an_operator, a_value):
a_map = map(a_function, a_list)
return all( an_operator(m, a_value) for m in a_map)
l = [[0,2],[0,1,2],[0,1,3,4]]
all_true(l, len, gt, 2)
#True
Note: this works for single conditions, but not for complex conditions like
len > 2 and element[0] == 5

Writing filter() function but getting typeError

my code consists of me recreating the function 'filter()' and using it with a function to filter words longer than 5 characters. It worked with the actual function filter when I tried it btw...I'm using python 3+
def filter1(fn, a):
i = 0
while i != len(a):
u = i - 1
a[i] = fn(a[i], a[u])
i += 1
return a
def filter_long_words(l):
if len[l] > 5:
return [l]
listered = ['blue', 'hdfdhsf', 'dsfjbdsf', 'jole']
print(list(filter1(filter_long_words, listered)))
getting error
TypeError: filter_long_words() takes 1 positional argument but 2 were given
You are passing two parameters to fn (which refers to filter_long_words) here:
a[i] = fn(a[i], a[u])
But filter_long_words only accepts one parameter.
Notes:
You can loop through lists using for item in my_list, or if you want index as well for index, item in enumerate(my_list).
I think you might get an IndexError since u will be -1 in the first round of your loop.
The filter function can also be expressed as a list comprehension: (item for item in listered if filter_long_words(item))
My version of filter would look like this, if I have to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
for item in sequence:
if fn(item):
yield item
Since you have stated that you are using Python 3, this returns a generator instead of a list. If you want it to return a list:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
acc = []
for item in sequence:
if fn(item):
acc.append(item)
return acc
If you don't need to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
return (item for item in sequence if fn(item))
Your're calling fn with 2 parameters in filter1(fn, a), and since you've passed filter_long_words() to filter1 as fn, that triggers the error.
But there's more weird stuff:
I don't understand the magick of filter1 or what you were trying to
accomplish, but it seems to me that you don't have a clear idea what to do.
But if you want to mimic (somehow) how filter works, you have to return a
list which contains only items for which the fn function returns true. When
you know this, you can rewrite it - here are a few suggestions for rewrite
# explicit, inefficient and long, but straightforward version:
def filter1(fn, a):
new_list = []
for item in a:
if fn(item):
new_list.append(item):
return new_list
# shorter version using list comprehensions:
def filter1(fn, a):
return [item for item in a if fn(item)]
The filter_long_words function is wrong too - it should return True or
False. The only reason why it could work is because any non-empty list is
treated as True by python and default return value of a function is None,
which translates to False. But it's confusing and syntactically wrong to use
len[l] - the proper usage is len(l).
There are a few suggestions for rewrite, which all returns explicit boolean
values:
# unnecessary long, but self-explanatory:
def filter_long_words(l):
if len(l) > 5:
return True
else
return False
# short variant
def filter_long_words(l):
return len(l) > 5
You are calling "filter_long_words" with 2 parameter => fn(a[i], a[u]) also there is an error
def filter_long_words(l):
if **len[l]** > 5:
return [l]
len is builtin method it should be len(l)

(Python 2.7) Use a list as an argument in a function?

So I'm trying to learn Python using codecademy but I'm stuck. It's asking me to define a function that takes a list as an argument. This is the code I have:
# Write your function below!
def fizz_count(*x):
count = 0
for x in fizz_count:
if x == "fizz":
count += 1
return count
It's probably something stupid I've done wrong, but it keeps telling me to make sure the function only takes one parameter, "x". def fizz_count(x): doesn't work either though. What am I supposed to do here?
Edit: Thanks for the help everyone, I see what I was doing wrong now.
There are a handful of problems here:
You're trying to iterate over fizz_count. But fizz_count is your function. x is your passed-in argument. So it should be for x in x: (but see #3).
You're accepting one argument with *x. The * causes x to be a tuple of all arguments. If you only pass one, a list, then the list is x[0] and items of the list are x[0][0], x[0][1] and so on. Easier to just accept x.
You're using your argument, x, as the placeholder for items in your list when you iterate over it, which means after the loop, x no longer refers to the passed-in list, but to the last item of it. This would actually work in this case because you don't use x afterward, but for clarity it's better to use a different variable name.
Some of your variable names could be more descriptive.
Putting these together we get something like this:
def fizz_count(sequence):
count = 0
for item in sequence:
if item == "fizz":
count += 1
return count
I assume you're taking the long way 'round for learning porpoises, which don't swim so fast. A better way to write this might be:
def fizz_count(sequence):
return sum(item == "fizz" for item in sequence)
But in fact list has a count() method, as does tuple, so if you know for sure that your argument is a list or tuple (and not some other kind of sequence), you can just do:
def fizz_count(sequence):
return sequence.count("fizz")
In fact, that's so simple, you hardly need to write a function for it!
when you pass *x to a function, then x is a list. Do either
def function(x):
# x is a variable
...
function('foo') # pass a single variable
funciton(['foo', 'bar']) # pass a list, explicitly
or
def function(*args):
# args is a list of unspecified size
...
function('foo') # x is list of 1 element
function('foo', 'bar') # x is list with two elements
Your function isn't taking a list as an argument. *x expands to consume your passed arguments, so your function is expecting to be called like this:
f(1, 2, 3)
Not like this:
f([1, 2, 3])
Notice the lack of a list object in your first example. Get rid of the *, as you don't need it:
# Write your function below!
def fizz_count(lst):
count = 0
for elem in lst:
if elem == "fizz":
count += 1
return count
You can also just use list.count:
# Write your function below!
def fizz_count(lst):
return lst.count('fizz')
It must be a typo. You're trying to iterate over the function name.
try this:
def fizz_count(x):
counter = 0
for element in x:
if element == "fizz":
counter += 1
return counter
Try this:
# Write your function below!
def fizz_count(x):
count = 0
for i in x:
if i == "fizz":
count += 1
return count
Sample :
>>> fizz_count(['test','fizz','buzz'])
1
for i in x: will iterate through every elements of list x. Suggest you to read more here.

Is there any way to shorten this Python generator expression?

I need to build a generator and I was looking for a way to shorten this for loop into a single line. I tried enumerate but that did not work.
counter=0
for element in string:
if function(element):
counter+=1
yield counter
else:
yield counter
counter=0
for element in string:
counter+=bool(function(element))
yield counter
(Yes, adding Booleans to ints works exactly as if True was 1 and False was 0).
The bool() call is only necessary if function() can have return values other than True, False, 1, and 0.
First, you can transform the string into an iterator over the function return values:
truths = (function(x) for x in string)
Then you can map those to 0s and 1s:
onesandzeroes = (1 if function(x) else 0 for x in string)
And then accumulate them:
running = itertools.accumulate(1 if function(x) else 0 for x in string)
As the docs note, accumulate was added in Python 3.2. If you're using 2.x, you can copy and paste the "Equivalent to" recipe from the docs. (If you're using 3.0-3.1, you can do the same, but really, in that case, just upgrade.)
If you're using Python 3, you can do:
from itertools import accumulate
yield from accumulate(1 if function(x) else 0 for x in string)
Although I'd use Simeon Visser's answer. While this one may be short, it isn't immediately clear what the code does.
You could shorten it to:
counter=0
for element in string:
if function(element):
counter+=1
yield counter

Categories