tuple index when invoke with function with dynamic arguments - python

>>> class Test(object):
>>> def test(self,*arg):
>>> print(arg[0],arg[1])
>>> p = Test()
>>> t = 2,3
>>> p.test(t)
gives me IndexError: tuple index out of range
why is that? and how do i get the value for that tuple?

You passed in just one argument (the whole tuple (2, 3)), so only arg[0] exists; if you meant the tuple values to be separate arguments, apply them with the *args call syntax:
p.test(*t)
The alternative is to not use the *arg catchall argument in your function definition:
def test(self, arg):
Now your function has two normal positional arguments, self and arg. You can only pass in one argument, and if that is your tuple, arg[0] and arg[1] will work as expected.

Using your demo class:
>>> class Test(object):
>>> def test(self,*arg):
>>> print(arg[0],arg[1])
When doing this:
>>> p = Test()
>>> t = 2,3
>>> p.test(t)
arg will have a value of [(1,2),]
When doing this:
>>> p = Test()
>>> t = 2,3
>>> p.test(*t)
arg will have a value of [1,2]
The * in the function means that all remaining arguments (non-keyword) are put into a list for you.
In the first case you send (1,2) has a single argument. In the second case the tuple is made into individual arguments using the * thus you send in 1 and 2.
For complete documentation on this refer to this Python article:
http://docs.python.org/2/reference/expressions.html#calls

Related

What does single(not double) asterisk * means when unpacking dictionary in Python?

Can anyone explain the difference when unpacking the dictionary using single or double asterisk? You can mention their difference when used in function parameters, only if it is relevant here, which I don't think so.
However, there may be some relevance, because they share the same asterisk syntax.
def foo(a,b)
return a+b
tmp = {1:2,3:4}
foo(*tmp) #you get 4
foo(**tmp) #typeError: keyword should be string. Why it bothers to check the type of keyword?
Besides, why the key of dictionary is not allowed to be non-string when passed as function arguments in THIS situation? Are there any exceptions? Why they design Python in this way, is it because the compiler can't deduce the types in here or something?
When dictionaries are iterated as lists the iteration takes the keys of it, for example
for key in tmp:
print(key)
is the same as
for key in tmp.keys():
print(key)
in this case, unpacking as *tmp is equivalent to *tmp.keys(), ignoring the values. If you want to use the values you can use *tmp.values().
Double asterisk is used for when you define a function with keyword parameters such as
def foo(a, b):
or
def foo(**kwargs):
here you can store the parameters in a dictionary and pass it as **tmp. In the first case keys must be strings with the names of the parameter defined in the function firm. And in the second case you can work with kwargs as a dictionary inside the function.
def foo(a,b)
return a+b
tmp = {1:2,3:4}
foo(*tmp) #you get 4
foo(**tmp)
In this case:
foo(*tmp) mean foo(1, 3)
foo(**tmp) mean foo(1=2, 3=4), which will raise an error since 1 can't be an argument. Arg must be strings and (thanks # Alexander Reynolds for pointing this out) must start with underscore or alphabetical character. An argument must be a valid Python identifier. This mean you can't even do something like this:
def foo(1=2, 3=4):
<your code>
or
def foo('1'=2, '3'=4):
<your code>
See python_basic_syntax for more details.
It is a Extended Iterable Unpacking.
>>> def add(a=0, b=0):
... return a + b
...
>>> d = {'a': 2, 'b': 3}
>>> add(**d)#corresponding to add(a=2,b=3)
5
For single *,
def add(a=0, b=0):
... return a + b
...
>>> d = {'a': 2, 'b': 3}
>>> add(*d)#corresponding to add(a='a',b='b')
ab
Learn more here.
I think the ** double asterisk in function parameter and unpacking dictionary means intuitively in this way:
#suppose you have this function
def foo(a,**b):
print(a)
for x in b:
print(x,"...",b[x])
#suppose you call this function in the following form
foo(whatever,m=1,n=2)
#the m=1 syntax actually means assign parameter by name, like foo(a = whatever, m = 1, n = 2)
#so you can also do foo(whatever,**{"m":1,"n":2})
#the reason for this syntax is you actually do
**b is m=1,n=2 #something like pattern matching mechanism
so b is {"m":1,"n":2}, note "m" and "n" are now in string form
#the function is actually this:
def foo(a,**b): # b = {"m":1,"n":2}
print(a)
for x in b: #for x in b.keys(), thanks to #vlizana answer
print(x,"...",b[x])
All the syntax make sense now. And it is the same for single asterisk. It is only worth noting that if you use single asterisk to unpack dictionary, you are actually trying to unpack it in a list way, and only key of dictionary are unpacked.
[https://docs.python.org/3/reference/expressions.html#calls]
A consequence of this is that although the *expression syntax may appear after explicit keyword arguments, it is processed before the keyword arguments (and any **expression arguments – see below). So:
def f(a, b):
print(a, b)
f(b=1, *(2,))
f(a=1, *(2,))
#Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
#TypeError: f() got multiple values for keyword argument 'a'
f(1, *(2,))

I would call this advanced tuple manipulation?

I'm trying to take a list of tuples that contain the name of individual functions and its arguments and instantiate them..this will be used for a multi-threading application.
import pandas as pd
df = pd.DataFrame()
def func1(df,arg2,arg3):
print 'func1'
print type(df)
print arg2,arg4
def func2(arg1,df,arg3,arg4):
print 'func2'
print arg1
print type(df)
print arg3,arg4
list_of_func_tups = [(func1,df,'two','three'),(func2,'one',df,'three','four')]
for tup in list_of_func_tups:
f = tup[0]
args = tup[1:]
results = f(args)
But the problem is that args is still seen as a tuple, hence, the error stating that only one argument was given.
TypeError: func1() takes exactly 3 arguments (1 given)
Is there a way that I can somehow get args seen as its respective individual arguments? Maybe a type of for loop or something? Anythin?
Let's define a function:
def f(arg1, arg2, arg3):
return arg1 + arg2 + arg3
As Willem mentions, you can flatten a tuple or list with an asterix.
f(*(1, 2, 3))
You can use the double asterix for dictionaries
f(**{'arg1': 1, 'arg2': 2, 'arg3': 3})
What you are looking for is a way to feed the tuple as positional arguments. You can do this by using a leading asterisk (*):
result = f(*args)
This is documented in the documentation of function calls:
If the syntax *expression appears in the function call,
expression must evaluate to an iterable. Elements from this iterable are treated as if they were additional positional
arguments; if there are positional arguments x1,..., xN, and
expression evaluates to a sequence y1, ..., yM, this is equivalent to
a call with M+N positional arguments x1, ..., xN, y1, ..., yM.

What does ** indicate in Python? [duplicate]

I want to build a query for sunburnt(solr interface) using class inheritance and therefore adding key - value pairs together. The sunburnt interface takes keyword arguments. How can I transform a dict ({'type':'Event'}) into keyword arguments (type='Event')?
Use the double-star (aka double-splat?) operator:
func(**{'type':'Event'})
is equivalent to
func(type='Event')
** operator would be helpful here.
** operator will unpack the dict elements and thus **{'type':'Event'} would be treated as type='Event'
func(**{'type':'Event'}) is same as func(type='Event') i.e the dict elements would be converted to the keyword arguments.
FYI
* will unpack the list elements and they would be treated as positional arguments.
func(*['one', 'two']) is same as func('one', 'two')
Here is a complete example showing how to use the ** operator to pass values from a dictionary as keyword arguments.
>>> def f(x=2):
... print(x)
...
>>> new_x = {'x': 4}
>>> f() # default value x=2
2
>>> f(x=3) # explicit value x=3
3
>>> f(**new_x) # dictionary value x=4
4

Map with no arguments and class method?

It seems pretty simple:
# builtins work fine:
>>> map (str, [(), (), ()])
['()', '()', '()']
# but no luck for class methods:
>>> class C (object):
... def m(self):
... return 42
...
>>> c = C()
>>> map(c.m, [(), (), ()])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: m() takes exactly 1 argument (2 given)
You need to add a parameter to your m method, where the argument of the map will be passed.
class C (object):
def m(self, x):
return 42
>>> c = C()
>>> map(c.m, [(), (), ()])
[42, 42, 42]
See, c.m is a bound method, already like calling m(c), you need a placeholder for the additional parameter passed by map
c and the argument passed by map are the 2 arguments to m your stack trace is complaining about:
TypeError: m() takes exactly 1 argument (2 given)
map(f, L) always calls f with a single argument whose values are taken from L. It's always a single argument, never zero. The ()s in the list are not argument lists, they are empty tuples. Outside of a function call, things in parentheses aren't arguments to a function, they are objects called "tuples" (think of them as immutable lists). Check the difference between str() and str(()) - str with no arguments gives '' and not '()'.
If you have tuples of arguments and want to call a callable (function or method) with these arguments, you can use itertools.starmap. In particular, if you pass empty tuples the functions will be called with no arguments. It returns an iterator, so if you need a list you need to explicitly use list() over the result
>>> import itertools
>>> f = lambda: 42
>>> L = [(), (), ()]
>>> values = itertools.starmap(f, L)
>>> print list(values)
[42, 42, 42]
In the general case, it works with any tuple of arguments:
>>> f = lambda *x: sum(x)
>>> L = [(1,2), (4, ), (5,6)]
>>> values = itertools.starmap(f, L)
>>> print list(values)
[3, 4, 11]
If you want to simply call a function multiple times and get the result, you might consider using a list comprehension or a generator expression instead.
>>> f = lambda: 42
>>> [f() for _ in xrange(3)]
[42, 42, 42]
>>> values = (f() for _ in xrange(3))
>>> print list(values)
[42, 42, 42]
If you have a list of empty tuples like in your example, you might use xrange(len(L)) in the place of xrange(3).
That's not a class method, it lacks the classmethod decorator and self should be cls. But you don't want a class method here anyway, as class methods are methods which operate on classes (you can pass other objects, of course, but that's not the intended use case - the #classmethod would be grossly misleading).
You're looking for the term "unbound method", which you get by refering to a member of the class, not to an instance thereof. Use C.m. Note of course that the method will be called with self as (in your example) a tuple and not an instance of C. Normally, such trickery should be restricted to avoid this (e.g. str.lower and a bunch of strings is O.K.).
First, that's not a class method. A class method takes the class as it's first argument, and is called on the class, not an instance of it:
class C(object):
#classmethod
def m(cls):
return 42
map(C.m, range(10))
However, that will still break because map passes in each item from the iterable to the function, and your method only accepts one argument, the class.
If you change your method to accept the extra argument (def m(cls, arg)), it will work. You could also use an instance method instead of a class method:
class C(object):
def m(self, *args): # or def m(self, n)
return 42
c = C()
map(c.m, range(10))
You forgot your decorator to make it a class method, but you probably want a static method:
static:
class C(object):
#staticmethod
def m(arg):
return 42
class:
class C(object):
#classmethod
def m(cls, arg):
#cls is a reference to the actual "C" type, not an instance of the "C" class.
return 42
m is a 1-argument method that takes an object of type C. The syntax "c.m" is actually equivalent to "m(c)", which is just 42. But 42 is not a function you can map over a list like [(),(),()].
The following should work:
class C (object):
def f(self): return lambda x: x+1
two,three,four = map(C().f(), [1,2,3])
Now C().* returns a function, instead of a constant.

Passing functions which have multiple return values as arguments in Python

So, Python functions can return multiple values. It struck me that it would be convenient (though a bit less readable) if the following were possible.
a = [[1,2],[3,4]]
def cord():
return 1, 1
def printa(y,x):
print a[y][x]
printa(cord())
...but it's not. I'm aware that you can do the same thing by dumping both return values into temporary variables, but it doesn't seem as elegant. I could also rewrite the last line as "printa(cord()[0], cord()[1])", but that would execute cord() twice.
Is there an elegant, efficient way to do this? Or should I just see that quote about premature optimization and forget about this?
printa(*cord())
The * here is an argument expansion operator... well I forget what it's technically called, but in this context it takes a list or tuple and expands it out so the function sees each list/tuple element as a separate argument.
It's basically the reverse of the * you might use to capture all non-keyword arguments in a function definition:
def fn(*args):
# args is now a tuple of the non-keyworded arguments
print args
fn(1, 2, 3, 4, 5)
prints (1, 2, 3, 4, 5)
fn(*[1, 2, 3, 4, 5])
does the same.
Try this:
>>> def cord():
... return (1, 1)
...
>>> def printa(y, x):
... print a[y][x]
...
>>> a=[[1,2],[3,4]]
>>> printa(*cord())
4
The star basically says "use the elements of this collection as positional arguments." You can do the same with a dict for keyword arguments using two stars:
>>> a = {'a' : 2, 'b' : 3}
>>> def foo(a, b):
... print a, b
...
>>> foo(**a)
2 3
Actually, Python doesn't really return multiple values, it returns one value which can be multiple values packed into a tuple. Which means that you need to "unpack" the returned value in order to have multiples.
A statement like
x,y = cord()
does that, but directly using the return value as you did in
printa(cord())
doesn't, that's why you need to use the asterisk. Perhaps a nice term for it might be "implicit tuple unpacking" or "tuple unpacking without assignment".

Categories