I have a function that takes a list of tasks, create threads and solves a complex task for each task.
To simplify lets just say it is this function:
def efficient_function_that_only_takes_a_list(list):
return [-x for x in list]
Now, all I need to do is to have a list and give it to the function:
efficient_function_that_only_takes_a_list(my_list)
My problem now is that I have realized that not all of the items in the list needs to be processed by the function. But I want to keep the order and only process some of them. This is what I came up with.
Anyone have a more pythonic way?
my_list = [3,2,3,4,5,6,7,8,9]
def my_condition(x):
return x%2 == 0
def efficient_function_that_only_takes_a_list(list):
return [-x for x in list]
#What I want: [1,-2,3,-4,5,-6,7,-8,9]
# My attempt:
def apply_to_some(mylist, condition, function):
valid = [(i,x) for i,x in enumerate(mylist) if condition(x)]
index, values = zip(*valid)
results = function(values)
for new_value, ix in zip(results,index):
mylist[ix] = new_value
return my_list
print apply_to_some(my_list, my_condition, efficient_function_that_only_takes_a_list) #=> [3, -2, 3, -4, 5, -6, 7, -8, 9]
Edit: I really need my function to take a list and not a single item as I need to make it run efficiently i parallel like in this article: https://medium.com/p/40e9b2b36148
It would be neater to do something like:
def apply_to_some(mylist, condition, function):
return [function(x) if condition(x) else x for x in mylist]
Although note that this form doesn't mutate mylist.
However, if your efficient function relies on a list, aside from a few tweaks that just reduce the line count, there's not much to improve on what you have.
Or if list comprehensions make your head spin, use map:
def apply_to_some(mylist, condition, function):
return map(lambda x: function(x) if condition(x) else x, mylist)
Related
Hopefully an example could illustrate this better. I am creating a function that will take either a list of names, various lists of names, just a name or multiple names (not in a list) and make one list of them all (without importing... non-native libraries? I am ignorant here but I mean I want to use only what's already available in python).
def make_one_list(entry1, entry2, entry3 ...):
#regardless of what is entered (well.. it has to be list or element)
#extract elements from list and combine with single elements and...
return one_big_list_of_all_elements
>>>make_one_list(['John','Mark'],'Mike',['Kate','Ally'],'Shawn','Marina'...)
['John','Mark','Mike','Kate','Ally','Shawn','Marina',...]
My pseudo-code is not set in stone, obviously. I think, eventually, some input checking might be in order but not very pythonic in nature to do that. I am simply doing this as a personal communication function for my office.
THANKS!!
Like the chain answer but a bit more straightforward at a glance
def make_one_list(*args):
one_list = []
for arg in args:
if isinstance(arg, str):
one_list.append(arg)
else:
one_list.extend(arg)
return one_list
ret = make_one_list(['John','Mark'],'Mike',['Kate','Ally'],'Shawn','Marina')
print(ret)
You can make a good use of *args in python.
Just traverse through the list of arguements and check whether the arguement is a list or not(by checking its type). If it is, just append that arguement to the one_big_list_of_all_elements.
Pretty simple!
Use chain(iter1, iter2, ...), it is a native library. Though you need to handle the strings that are not in lists.
from itertools import chain
data = ['John','Mark'],'Mike',['Kate','Ally'],'Shawn','Marina'
r = chain(*[x if isinstance(x, list) else [x] for x in data])
print(list(r))
Output:
['John', 'Mark', 'Mike', 'Kate', 'Ally', 'Shawn', 'Marina']
final_list = []
def make_one_list(arg_list):
for arg in arg_list:
if isinstance(arg, list):
for item in arg:
final_list.append(item)
else:
final_list.append(arg)
return final_list
Try this
a = []
b = []
def myFun(*argv):
for arg in argv:
a.append(arg)
myList = str(a).replace('[','').replace(']','')
b = [myList]
print(b)
myFun('Hello', 'Welcome', 'to', 'Geeks',['ff','tt'])
OUTPUT >> ["'Hello', 'Welcome', 'to', 'Geeks', 'ff', 'tt'"]
hope it will help
def flatten(x):
if isinstance(x, list):
tmp = []
for i in x:
tmp.extend(flatten(i))
return tmp
else:
return [x]
Test:
print(repr(flatten([[1, 2, 3], 4, [5], [6, [7, 8], 9]])))
print(repr(flatten(1)))
print(repr(flatten([2])))
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1]
[2]
Fast, clean, works for any type of elements and any depth of nested lists.
Note usage of "extend" which avoids creating excessive intermediate results. This makes the function efficient even for large lists.
We can do what you asked for by connecting all lists with (+)
def make_one_list(*args):
result = []
for i in args:
if isinstance(i, list):
result.append(list(i))
else:
result.append(i)
return result
result = make_one_list(['John','Mark'], ['Kate','Ally'], ['Shawn','Marina'], 'Mike')
print(result)
Given some function that can return None or another value and a list of values:
def fn(x):
if x == 4:
return None
else return x
lst = [1, 2, 3, 4, 5, 6, 7]
I want to have a list of the outputs of fn() that don't return None
I have some code that works which looks like so:
output = []
for i in lst:
result = fn(i)
if result:
output.append(result)
I can express this as a list comprehension like so:
output = [fn(i) for i in lst if fn(i)]
but it runs fn(i) for anything that doesn't return None twice which is not desirable if fn is an expensive function.
Is there any way to have have the nice pythonic comprehension without running the function twice?
a possible solution:
output = [fn(i) for i in lst]
output = [o for o in f if o]
Your problem is that None is produced by the function, not the thing you are iterating over. Try
output = [fi for fi in map(fn, lst) if fi is not None]
Just combine your solutions:
[x for x in [fn(i) for i in lst] if x is not None]
The downside is that any Nones in the original list not produced by fn will be removed as well.
I need to remove duplicates in a list without set, functions or loops - only by using filter and a lambda function.
My attempt was:
list(filter(lambda x: x in l[:].remove(x), l))
But remove returns the removed item and not the whole list. Any suggestions?
You need to keep some state somehow. If you can use a new list, you could do something like this:
g = l[:]
filter(lambda x: g.remove(x) is None and g.count(x) == 0, l)
The above removes duplicates differently. If you had l = [1, 2, 2, 3, 2], you'd end up with [1, 3, 2] as the resultant list.
Or create an empty list and use it to keep track of what you've seen:
seen = []
return filter(lambda x: seen.append(x) is None if x not in seen else False, l)
Both the above is pretty akin to using sets, though far less efficient. :-) And both are using a goofy mechanism to allow mutate a list in place but return a True/False result (the is None portion in both of them is allowing us to chain expressions together).
If you can use map and enumerate, you could do something like:
map(lambda t: t[1],
filter(lambda t: l[:t[0]].count(t[1]) == 0, enumerate(l)))
(it uses the current index to look into the previous part of the list to find duplicates)
If you can use list comprehensions, you could remove the use of map:
[x for i, x in filter(lambda t: l[:t[0]].count(t[1]) == 0,
enumerate(l))]
If you could use reduce, then you could do something like:
reduce(lambda r, x: r + [x] if x not in r else r, l, [])
as you can keep state by passing the result from one iteration to the next.
But somehow you're going to need to have a record of what has been seen. None of this is what I'd call elegant Python code though, except maybe the reduce version--though it's not performant.
I made a solution using only a lambda function.
The following lambda function returns a list corresponding to the list passed as argument, without duplicates:
lambda l: (lambda u, a: u(u, a)) ((lambda f, x: x if len(x) <= 1 else (f(f, x[1:]) if x[0] in x[1:] else ([x[0]] + f(f, x[1:])))), l)
Since the target is a bit different, I posted a separate Q/A, where I explain this: Removing duplicates using only lambda functions.
I want to rotate k element in a list in python. For example, n = 7, k = 3, and the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
Here is the statement I wrote. It seems to work in the command line.
nums = nums[k%len(nums):] + nums[:k%len(nums)]
But when I encapsulate it in a function like:
def rotate(nums, k):
nums = nums[k%len(nums):] + nums[:k%len(nums)]
return
I want to modify nums directly, but this function doesn't work.
I know that I can use a for loop like:
for i in range(k):
nums.insert(0,nums.pop())
but I want to know why the previous method doesn't work?
What you want is a slice assignment:
nums[:] = nums[k%len(nums):] + nums[:k%len(nums)]
This mutates the list that was passed in, so the change is visible after the function returns. Assigning just to nums merely makes nums point to a different list inside the function; it doesn't affect the original list.
Are you sure you want to modify nums? You need not create a separate list even if you do not modify nums. One advantage of the following approach is that it will work with any sequence.
from itertools import islice
def rotate(lst, k):
n = len(lst)
start = n - (k % n) #handle all ints
for item in islice(lst, start, None):
yield item
for item in islice(lst, 0, start):
yield item
If you insist on modifying nums as you say, you can still do so. E.g.,
nums = [x + 1 for x in range(7)]
nums[:] = rotate(nums,-10)
The nums that is inside the function is only within that function. So you need to return it from that function. One way is like this (python 2.7 code, add parentheses to print if you use 3.x):
nums = [1, 2, 3, 4, 5, 6, 7]
k = 3
def rotate(nums, k):
return nums[k%len(nums):] + nums[:k%len(nums)]
print 'Original', nums
nums = rotate(nums, k)
print 'Rotated', nums
I need to use set(myList) but it's not possible because I have a list of lists. (It gives a not hashable error).
So I decided to convert each element in the list to a tuple. The list is more or less like this:
MyList[elem1, elem2, [nested1, nested2, [veryNested1, veryNested2]]]
How can I quickly convert everything to a tuple and then back to a list?
Use recursion
MyList = ['elem1', 'elem2', ['nested1', 'nested2', ['veryNested1', 'veryNested2']]]
print MyList
def tupconv(lst):
tuplst = []
for x in lst:
if isinstance(x, list):
tuplst.append(tupconv(x))
else:
tuplst.append(x)
return tuple(tuplst)
def listconv(tup):
lst = []
for x in tup:
if isinstance(x, tuple):
lst.append(listconv(x))
else:
lst.append(x)
return lst
mytup = tupconv(MyList)
print mytup
mylist = listconv(mytup)
print mylist
This should do it:
def lol_to_tuple(lol):
return tuple(el if type(el) is not list
else lol_to_tuple(el)
for el in lol)
To go back, just replace tuple with list:
def tuples_to_lol(tuples):
return list(el if type(el) is not tuple
else tuples_to_lol(el)
for el in tuples)
Hmm, I've seen this kind of problem before. I solved it by using recursion (on the assumption that the final element is the next level, and that I knew how deep the recursion needed to go to split it up.
Having said that, I think it might be better to ask if you were given the list in that format or if you made it like that through some process? That might help us avoid some unnecessary head-bashing.
You can flatten your list with:
In [1]: from compiler.ast import flatten
In [2]: flatten([1, 2, [11, 12, [21, 22]]])
Out[2]: [1, 2, 11, 12, 21, 22]
And then use set(myList)