Collapse Generator in Python - python

So I have a function like this:
def demo_range(limit):
value = 0
while value < limit:
yield value
value = value + 1
and this
def demo_generator_to_list(generator):
return [x for x in range(generator)]
Now in the demo_generator_to_list(generator) I need to fill some code to collapse a generator to:
[0,1,2,3]
from
demo_generator_to_list(demo_range(4))

Just pass the generator to a list() call:
def demo_generator_to_list(generator):
return list(generator)
The list() function will iterate over the generator and add all results to a new list object.
You could still use a list comprehension, but then you don't use range():
return [x for x in generator]
This has no advantage over using list(); it is just slower.

Related

How to return object when using `append` in Python

i have a function to append a list, something like this:
def append_func(element):
if xxxx:
new_list.append(element)
else:
[]
I have another function that uses append_func():
def second_func(item):
for i in item:
append_func(i)
if i run :
new_list = []
second _func(item)
new_list
This will return the list i want, but i can't do new_list = second _func(item) because in this case new_list will be a None.
I understand that append() will return a None type, but i'd like to return the appended list so I can use in other places result = second _func(xxx), what i have missed? Thanks.
According to the clarification you did in the comments you might want something like this. (I changed some of your placeholders so we have running code and a reproducible example)
The list is created by second_func so we get rid of the global list.
def append_func(data, element):
if 2 < element < 7:
data.append(element ** 2)
def second_func(items):
new_list = []
for i in items:
append_func(new_list, i)
return new_list
items = list(range(10))
result = second_func(items)
print(result)
The result is [9, 16, 25, 36].
simply tell python what to return:
def append_func(element):
if xxxx:
new_list.append(element)
else:
[]
return new_list # here, return whatever you want to return
if there is no "return" statement in the function, then the function returns None
The new_list you define before calling second_func is a global variable. Every time you call second_func() it will append the argument to the global variable. But the new_list is not restricted to the namespace of either function, so setting it as a return value doesn't make sense.

how do I iterate only once per function call

I have a list of things I want to iterate over and return each of them only once per function call.
What I've tried:
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# convert each string into list
result = [x.strip() for x in tl.split(",")]
index = 0
def func():
return result[index]
index += 1
It's saying code unreachable at the index += 1 part.
The output I want is zza the first time I call func(), then zzb, then zzc, etc.
Appreciate the help.
EDIT:
I've found this answer to work well and easily readable:
# list of elements seperated by a comma
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# split each string by comma to get a list
result = [x.strip() for x in tl.split(",")]
# initialize the object
iterator_obj = iter(result)
print(next(iterator_obj))
print(next(iterator_obj))
print(next(iterator_obj))
output:
zza
zzb
zzc
In c++ there is an operator that will increment a variable with ++i incrementing before evaluation and i++ after evaluation
(i:=i+1) #same as ++i (increment, then return new value)
(i:=i+1)-1 #same as i++ (return the incremented value -1)
so the function you want is
def func():
global index
return result[(index := index+1)-1]
the := operator is new in python 3.8
so
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# convert each string into list
result = [x.strip() for x in tl.split(",")]
index = 0
def func():
global index
return result[(index := index + 1) - 1]
print(func())
print(func())
print(func())
print(func())
prints
zza
zzb
zzc
zzd
Because return statement exit the function, any statement after that is not reachable. A quick fix to your code:
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# convert each string into list
result = [x.strip() for x in tl.split(",")]
next_index = 0
def func():
global next_index
next_index += 1
return result[next_index-1]
BTW, your func behaves like built-in next. If you don't want to reinvent the wheel:
# convert each string into iterator
result = (x.strip() for x in tl.split(","))
# next(result) will get to the next item on the list
If you want to be able to only return one item of your string at a time per function call, you need to use an generator:
def func(items):
string_list = items.split(",")
for i in range(len(string_list)):
yield string_list[i]
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
item = func(tl)
To pull a value out, in order, use
next(item) # zza
next(item) # zzb
...
Every time you call next, a new value will be returned.
As an aside, anything after return statement will not run, which is why your index += 1 did not work. Return stops the function from running.

Apply map on *args in Python where *args are lists

I want to get a list of lists consisting of only 0 and 1 and map the first element of the first list with the first element of the second list and so on.
My mapping function is this:
def intersect(*values):
result = values[0]
for idx in range(1, len(values)):
result = result << 1
result = result | values[idx]
return result
I'm trying to do this but it does not work:
def intersect_vectors(*vectors):
return list(map(intersect, zip(vectors)))
It would work if I would knew the number of vectors and would have a function like this:
def intersect_vectors(v1, v2, v3):
return list(map(intersect, v1,v2,v3))
Example:
intersect_vectors([1,1],[1,0],[0,1]) would return [6,5] which is [b110, b101]
You can explode your vectors with * and it will work the same:
def intersect_vectors(*vectors):
return list(map(intersect, *vectors))
The simplest solution is probably to delegate the functionality of transforming between a list and 'arguments' to a lambda:
return [list(map((lambda v: intersect(*v)), zip(vectors)))]

Simple list function in Python

I am trying to append values (x) to a list if the numbers are divisible by 2 and then print out that list. Seems pretty simple. The following runs but returns None:
x = []
def test():
while x in range(0,100):
if x % 2 == 0:
x.append()
print(test())
Use for to iterate a range - not while.
You have ambiguous meaning to x - both as iteration variable and as a list.
You need to pass the value to append.
You need to return a value so it would be printed through the print statement - otherwise None is the default.
Fixed:
x = []
def test():
for i in range(0,100):
if i % 2 == 0:
x.append(i)
return x
print(test())
Other notes:
You can only use this once. The next call for test would return a list twice the size, since x is a global variable. I believe this is unintended and can be solved by putting the x = [] inside the function.
A list comprehension like [x for x in range(100) if x % 2 == 0] would be much better.
Problems and Fixes
Your have several problems with your code:
You named your list x and your iterate variable x.
You never append a value to your list.
You never return a list from test. Rather than appending to a global list, make a local list in test and return that list.
You're using a while loop when you should be using a for loop.
After the above changes, you code look likes:
def test():
even_numbers = []
for number in range(0, 100):
if number % 2 == 0:
even_numbers.append(number)
return even_numbers
print(test())
Improvements
Note there are better ways to do this. In this case, a list comprehension is a better choice. List comprehensions can be used to avoid the common pattern of building a list of values - such as your case:
def test():
return [n for n in range(0, 100) if n % 2 == 0]
print(test())
Generally you should pass the variable to the function and return it from the function instead of relying on global variables:
def test(x):
...
return x
However while x in range(0, 100) won't work because it will check if x (an empty list) is contained in the range object. But the range only contains numbers so the while loop body will never execute. So you could use a for-loop instead:
def test(x):
for i in range(0, 100):
if i % 2 == 0:
x.append(i)
return x
But you could also use the fact that range supports a step and remove the if by just using step=2:
def test(x):
for i in range(0, 100, 2):
x.append(i)
return x
At this point you could even just extend (append an iterable to) the list:
def test(x):
x.extend(range(0, 100, 2))
return x
You could also use a step of 2 to avoid if tests :
x = []
def test():
for i in range(0,100,2):
x.append(i)
return x
Or use a list comprehension :
x = []
def test():
x = [i for i in range(0,100,2)]
return x
Or use the range it's self as a list:
x = []
def test()
x = list(range(0,100,2))
return x
Rename your inner x variable for simplicity. Use x.append(n) instead of x.append(). Finally, print the variable not the method.
You should either change the list variable or the iteration variable(x in while loop) . and append function works like this list.append(value).

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)

Categories