arguments in functions python - python

This is a subtle question about notation.
I want to call a function with specific arguments, but without having to redefine it.
For example, min() with a key function on the second argument key = itemgetter(1) would look like:
min_arg2 = lambda p,q = min(p,q, key = itemgetter(1))
I'm hoping to just call it as something like min( *itemgetter(1) )...
Does anyone know how to do this? Thank you.

You want to use functools.partial():
min_arg2 = functools.partial(min, key=itemgetter(1))
See http://docs.python.org/library/functools.html for the docs.
Example:
>>> import functools
>>> from operator import itemgetter
>>> min_arg2 = functools.partial(min, key=itemgetter(1))
>>> min_arg2(vals)
('b', 0)

Using functools (as in Duncan's answer) is a better approach, however you can use a lambda expression, you just didn't get the syntax correct:
min_arg2 = lambda p,q: min(p,q, key=itemgetter(1))

Related

How to pass arguments to a function in "map" function call?

I know a map function gets a function as its first argument and the next arguments are iterators on which the passed function needs to be applied. My question here is say if I have a 2d list like so
l=[[1,2,3],[4,5,6],[7,8,9]]
how can I sort the individual lists in reverse order so my output is
l=[[3,2,1],[6,5,4],[9,8,7]]
I know a potential solution is using a lambda function such as
list(map(lambda x:x[::-1],l))
I want something like this
list(map(sorted, l,'reversed=True'))
where 'reversed=True' is an argument that sorted takes
eg:
>>> newList=[1,2,3]
>>> sorted(newList,reversed='True')
>>> [3,2,1]
I have seen how to pass arguments to a the pow function using the itertools.repeat module
map(pow,list,itertools.repeat(x))
x=power to which the list must be raised
I want to know if there is any way the arguments can be passed in a map function. In my case the 'reverse=True' for the sorted function.
You can use functools.partial for this:
import functools
new_list = list(map(functools.partial(sorted, reverse=True), l))
You can use a lambda to wrap the funtion:
map(lambda x: sorted(x, reversed=True), l)
or:
map(lambda i, j: pow(i, j), list,itertools.repeat(x))
There are many ways to do it.
You could use functools.partial. It creates a partial, for the lack of a better word, of the function you pass to it. It sort of creates a new function with some parameters already passed into it.
For your example, it would be:
from functools import partial
rev_sort = partial(sorted, reverse=True)
map(rev_sort, l)
The other way is using a simple lambda:
map(lambda arr: sorted(arr, reverse=True), l)
The other other way (my personal choice), is using generators:
(sorted(arr, reverse=True) for arr in l)
For this specific case, you can also use a list comprehension -
l=[[1,2,3],[4,5,6],[7,8,9]]
l = [list(reversed(sublist)) for sublist in l]
//[[3,2,1],[6,5,4],[9,8,7]]

Can we pass any parameters for the key function which we are using along with sorted function

I have a an element k and list of lists which I named as A . I have to sort elements in A using sorted function based on k'th element in A .
Ex :
k=1
A=[[10,20,30],[100,5,300]]
Output should be
[[100,5,300],[10,20,30]]
I can do this easily using below code .
def mysort(x):
return (x[k])
k=1
A=[[10,20,30],[100,5,300]]
print(sorted(A,key=mysort))
But what I am asking here is I want to pass the variable k in to function mysort .
operator.itemgetter is convenient for your specific use case:
from operator import itemgetter
res = sorted(A, key=itemgetter(k))
functools.partial offers a more general solution:
from functools import partial
def mysort(x, index):
return x[index]
res = sorted(A, key=partial(mysort, index=k))
You can create a lambda for that - lambdas are (kindof) anonymous functions:
k=1
A=[[10,20,30],[100,5,300]]
print(sorted(A,key=lambda x:x[k]))
Output:
[[100, 5, 300], [10, 20, 30]]
This post why-are-python-lambdas-useful explains it in some more detail and the official documentation is here: lambda expressions
The lambda lambda x:x[k] uses every element of A and from it the k-th value to sort your list.
I think what you're looking for is this:
def mysort(k):
return lambda x: x[k]
# ...
sorted(A, key=mysort(k))
Fortunately for you, this is alrealy implemented in operator from the standard library:
from operator import itemgetter
k = 1
sorted(A, key=itemgetter(k))
You can build the function for the key paramerer dynamically using closures:
def mysort(k):
def f(x):
return x[k]
return f
k=1
A=[[10,20,30],[100,5,300]]
print(sorted(A,key=mysort(k)))

custom sort function in python 3 [duplicate]

In Python 2.x, I could pass custom function to sorted and .sort functions
>>> x=['kar','htar','har','ar']
>>>
>>> sorted(x)
['ar', 'har', 'htar', 'kar']
>>>
>>> sorted(x,cmp=customsort)
['kar', 'htar', 'har', 'ar']
Because, in My language, consonents are comes with this order
"k","kh",....,"ht",..."h",...,"a"
But In Python 3.x, looks like I could not pass cmp keyword
>>> sorted(x,cmp=customsort)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'cmp' is an invalid keyword argument for this function
Is there any alternatives or should I write my own sorted function too?
Note: I simplified by using "k", "kh", etc. Actual characters are Unicodes and even more complicated, sometimes there is vowels comes before and after consonents, I've done custom comparison function, So that part is ok. Only the problem is I could not pass my custom comparison function to sorted or .sort
Use the key keyword and functools.cmp_to_key to transform your comparison function:
sorted(x, key=functools.cmp_to_key(customsort))
Use the key argument (and follow the recipe on how to convert your old cmp function to a key function).
functools has a function cmp_to_key mentioned at docs.python.org/3.6/library/functools.html#functools.cmp_to_key
A complete python3 cmp_to_key lambda example:
from functools import cmp_to_key
nums = [28, 50, 17, 12, 121]
nums.sort(key=cmp_to_key(lambda x, y: 1 if str(x)+str(y) < str(y)+str(x) else -1))
compare to common object sorting:
class NumStr:
def __init__(self, v):
self.v = v
def __lt__(self, other):
return self.v + other.v < other.v + self.v
A = [NumStr("12"), NumStr("121")]
A.sort()
print(A[0].v, A[1].v)
A = [obj.v for obj in A]
print(A)
Instead of a customsort(), you need a function that translates each word into something that Python already knows how to sort. For example, you could translate each word into a list of numbers where each number represents where each letter occurs in your alphabet. Something like this:
my_alphabet = ['a', 'b', 'c']
def custom_key(word):
numbers = []
for letter in word:
numbers.append(my_alphabet.index(letter))
return numbers
x=['cbaba', 'ababa', 'bbaa']
x.sort(key=custom_key)
Since your language includes multi-character letters, your custom_key function will obviously need to be more complicated. That should give you the general idea though.
I don't know if this will help, but you may check out the locale module. It looks like you can set the locale to your language and use locale.strcoll to compare strings using your language's sorting rules.
Use the key argument instead. It takes a function that takes the value being processed and returns a single value giving the key to use to sort by.
sorted(x, key=somekeyfunc)

TypeError: 'cmp' is an invalid keyword argument for this function

I'm using Python3, but the script is not compatible with this version and I hit some errors. Now I have problem with cmp parameter. Here is the code
def my_cmp(x,y):
counter = lambda x, items: reduce(lambda a,b:a+b, [list(x).count(xx) for xx in items])
tmp = cmp(counter(x, [2,3,4,5]), counter(y, [2,3,4,5]))
return tmp if tmp!=0 else cmp(len(x),len(y))
for i, t in enumerate([tmp[0] for tmp in sorted(zip(tracks, self.mapping[idx][track_selection[-1]].iloc[0]), cmp=my_cmp, key=lambda x:x[1])]):
img[i,:len(t)] = t
I would really appreciate any help how to deal with this error in Python3.
from python documentation
In Python 2.7, the functools.cmp_to_key() function was added to the
functools module.
The function available in python 3 too.
Just wrap your cmp function with cmp_to_key
from functools import cmp_to_key
...
...key=cmp_to_key(my_cmp)...
You should try to rewrite your cmp function to a key function instead. In this case it looks like you can simply return the counter() function output for just one element:
def my_key(elem):
counter = lambda x, items: sum(list(x).count(xx) for xx in items)
return counter(elem, [2, 3, 4, 5]), len(elem)
I took the liberty of replacing the reduce(...) code with the sum() function, a far more compact and readable method to sum a series of integers.
The above too will first sort by the output of counter(), and by the length of each sorted element in case of a tie.
The counter function is hugely inefficient however; I'd use a Counter() class here instead:
from collections import Counter
def my_key(elem):
counter = lambda x, items: sum(Counter(i for i in x if i in items).values())
return counter(elem, {2, 3, 4, 5}), len(elem)
This function will work in both Python 2 and 3:
sorted(zip(tracks, self.mapping[idx][track_selection[-1]].iloc[0]),
key=lambda x: my_key(x[1]))
If you cannot, you can use the cmp_to_key() utility function to adapt your cmp argument, but take into account this is not an ideal solution (it affects performance).

Why should I use operator.itemgetter(x) instead of [x]?

There is a more general question here: In what situation should the built-in operator module be used in python?
The top answer claims that operator.itemgetter(x) is "neater" than, presumably, than lambda a: a[x]. I feel the opposite is true.
Are there any other benefits, like performance?
You shouldn't worry about performance unless your code is in a tight inner loop, and is actually a performance problem. Instead, use code that best expresses your intent. Some people like lambdas, some like itemgetter. Sometimes it's just a matter of taste.
itemgetter is more powerful, for example, if you need to get a number of elements at once. For example:
operator.itemgetter(1,3,5)
is the same as:
lambda s: (s[1], s[3], s[5])
There are benefits in some situations, here is a good example.
>>> data = [('a',3),('b',2),('c',1)]
>>> from operator import itemgetter
>>> sorted(data, key=itemgetter(1))
[('c', 1), ('b', 2), ('a', 3)]
This use of itemgetter is great because it makes everything clear while also being faster as all operations are kept on the C side.
>>> sorted(data, key=lambda x:x[1])
[('c', 1), ('b', 2), ('a', 3)]
Using a lambda is not as clear, it is also slower and it is preferred not to use lambda unless you have to. Eg. list comprehensions are preferred over using map with a lambda.
Performance. It can make a big difference. In the right circumstances, you can get a bunch of stuff done at the C level by using itemgetter.
I think the claim of what is clearer really depends on which you use most often and would be very subjective
When using this in the key parameter of sorted() or min(), given the choice between say operator.itemgetter(1) and lambda x: x[1], the former is typically significantly faster in both cases:
Using sorted()
The compared functions are defined as follows:
import operator
def sort_key_itemgetter(items, key=1):
return sorted(items, key=operator.itemgetter(key))
def sort_key_lambda(items, key=1):
return sorted(items, key=lambda x: x[key])
Result: sort_key_itemgetter() is faster by ~10% to ~15%.
(Full analysis here)
Using min()
The compared functions are defined as follows:
import operator
def min_key_itemgetter(items, key=1):
return min(items, key=operator.itemgetter(key))
def min_key_lambda(items, key=1):
return min(items, key=lambda x: x[key])
Result: min_key_itemgetter() is faster by ~20% to ~60%.
(Full analysis here)
As performance was mentioned, I've compared both methods operator.itemgetter and lambda and for a small list it turns out that operator.itemgetter outperforms lambda by 10%. I personally like the itemgetter method as I mostly use it during sort and it became like a keyword for me.
import operator
import timeit
x = [[12, 'tall', 'blue', 1],
[2, 'short', 'red', 9],
[4, 'tall', 'blue', 13]]
def sortOperator():
x.sort(key=operator.itemgetter(1, 2))
def sortLambda():
x.sort(key=lambda x:(x[1], x[2]))
if __name__ == "__main__":
print(timeit.timeit(stmt="sortOperator()", setup="from __main__ import sortOperator", number=10**7))
print(timeit.timeit(stmt="sortLambda()", setup="from __main__ import sortLambda", number=10**7))
>>Tuple: 9.79s, Single: 8.835s
>>Tuple: 11.12s, Single: 9.26s
Run on Python 3.6
Leaving aside performance and code style, itemgetter is picklable, while lambda is not. This is important if the function needs to be saved, or passed between processes (typically as part of a larger object). In the following example, replacing itemgetter with lambda will result in a PicklingError.
from operator import itemgetter
def sort_by_key(sequence, key):
return sorted(sequence, key=key)
if __name__ == "__main__":
from multiprocessing import Pool
items = [([(1,2),(4,1)], itemgetter(1)),
([(5,3),(2,7)], itemgetter(0))]
with Pool(5) as p:
result = p.starmap(sort_by_key, items)
print(result)
Some programmers understand and use lambdas, but there is a population of programmers who perhaps didn't take computer science and aren't clear on the concept. For those programmers itemgetter() can make your intention clearer. (I don't write lambdas and any time I see one in code it takes me a little extra time to process what's going on and understand the code).
If you're coding for other computer science professionals go ahead and use lambdas if they are more comfortable. However, if you're coding for a wider audience. I suggest using itemgetter().

Categories