Hi everyone this is probably something extremely simple that i'm overlooking but can someone point me in the right direction for how to handle this problem.
def nodeFunction(self,*args):
return self[1] + self[2]
Basically what I am trying to do is grab the data passed in through the arguments. I am just stuck on the syntax for referencing the arguments inside the function when using *args.
args is simply a tuple:
def nodeMethod(self, *args):
return args[0], args[1]
Is that what you mean?
Note that there's nothing special about "args". You could use any variable name. It's the * operator that counts.
>>> class Node(object):
... def nodeMethod(self, *cornucopia):
... return cornucopia[0], cornucopia[1]
...
>>> n = Node()
>>> n.nodeMethod(1, 2, 3)
(1, 2)
Still, "args" is the most idiomatic variable name; I wouldn't use anything else without a good reason that would be obvious to others.
def nodeFunction(self, arg1, arg2, *args)
*arg in argument list means: pass the remaning arguments as a list in variable arg. So check how to handle lists. Note: list index starts from 0.
Related
I want to write a wrapper function which call one function and pass the results to another function. The arguments and return types of the functions are the same, but I have problem with returning lists and multiple values.
def foo():
return 1,2
def bar():
return (1,2)
def foo2(a,b):
print(a,b)
def bar2(p):
a,b=p
print(a,b)
def wrapper(func,func2):
a=func()
func2(a)
wrapper(bar,bar2)
wrapper(foo,foo2)
I am searching for a syntax which works with both function pairs to use it in my wrapper code.
EDIT: The definitions of at least foo2 and bar2 should stay this way. Assume that they are from an external library.
There is no distinction. return 1,2 returns a tuple. Parentheses do not define a tuple; the comma does. foo and bar are identical.
As I overlooked until JacobIRR's comment, your problem is that you need to pass an actual tuple, not the unpacked values from a tuple, to bar2:
a = foo()
foo2(*a)
a = bar()
bar2(a)
I don't necessarily agree with the design, but following your requirements in the comments (the function definitions can't change), you can write a wrapper that tries to execute each version (packed vs. unpacked) since it sounds like you might not know what the function expects. The wrapper written below, argfixer, does exactly that.
def argfixer(func):
def wrapper(arg):
try:
return func(arg)
except TypeError:
return func(*arg)
return wrapper
def foo():
return 1,2
def bar():
return (1,2)
#argfixer
def foo2(a,b):
print(a,b)
#argfixer
def bar2(p):
a,b=p
print(a,b)
a = foo()
b = bar()
foo2(a)
foo2(b)
bar2(a)
bar2(b)
However, if you aren't able to put the #argfixer on the line before the function definitions, you could alternatively wrap them like this in your own script before calling them:
foo2 = argfixer(foo2)
bar2 = argfixer(bar2)
And as mentioned in previous comments/answers, return 1,2 and return (1,2) are equivalent and both return a single tuple.
This code does not run because of arg differences. It runs if you use def foo2(*args): and def bar2(*p):.
The return 1, 2 and return (1, 2) are equivalent. The comma operator just creates a tuple, whether it is enclosed in parentheses or not.
All programming languages that I know of return a single value, so, since you want to return multiple, those values must be wrapped into a collection type, in this case, a tuple.
The problem is in the way you call the second function. Make it bar2(a) instead of bar2(*a), which breaks the tuple into separate arguments.
I found out that using
(int)('123')
Works, the same as using int('123')
I explored it a bit and noticed that it work with other functions too.
def add_10(num):
return num + 10
print (add_10)(10) # prints 20
And it also works with classes
class MyClass(object):
def __init__(self, x):
self.x = x
print (MyClass)(10).x # returns 10
I never seem this behaviour before, is it used by anyone? Does this have a name? Where in the docs is this stated? Why do we have this?
It works in both Python 2.7 and Python 3.
Edit:
Further testing and I noticed that the parenthesis don't have any effect. Using ((((int))))('2') is the same as int('2')
Let's try to put this in other words: a function in Python is just an ordinary object.
Appending a pair of parentheses to an object's name, whatever it is, causes the previous object to be called - i.e., it's __call__ method is invocated with the passed parameters.
So, a name in Python, whether from a function or not, can be surrounded by parentheses. The parentheses will be resolved first, as an expression - so -
in (int)(5), (int) is processed as an expression which yields int. Which happens to be a callable object.
One wayto make it easier to understand is to make the expression in parentheses to be less trivial. For example - one can come up with "addable functions" that when added create a new callable object that chains the return value across all functions. It is more or less straightforward to do that:
def compose(func1, func2):
def composed(*args, **kw):
return func2(func1(*args, **kw))
return composed
class addable(object):
def __init__(self, func):
self.func = func
def __add__(self, other):
if callable(other):
return addable(compose(self.func, other))
raise TypeError
def __call__(self, *args, **kw):
return self.func(*args, **kw)
#addable
def mysum(a, b):
return a + b
#addable
def dup(a):
return a * 2
And it works like this at the interactive console:
>>> (mysum + dup)(3, 3)
12
>>>
You can add parenthesis in many places without affecting how the code runs:
>>> (1)+(2)
3
>>> (1)+(((2)))
3
>>> (((int)))('123')
123
It's not casting, you're only surrounding the function object with parenthesis.
You can put parentheses around any expression. One kind of expression is a name. Names can refer to any value, and strings, functions, and types are all just different kinds of values. Multiple layers of parentheses aren't special, either: since a parenthesized expression is also an expression, you can put them in parentheses.
Say I have a list of similar functions:
def func1(a)
foo(a=a)
def func2(b)
foo(b=b)
...
the only difference of them is the argument name of foo, is that a short way to define them as one function, such as passing a argument name to the functions?
You can do it by unpacking a keyword argument dictionary:
def combined(name, value):
foo(**{name:value})
Then combined('a', a) is equivalent to func1(a). Whether this is a good idea is a separate consideration.
Why have a function as my_fun(an_arg, *arg) or even this a_func(dict=None, **args) why do people prefer to do this instead of saying just my_func(*args)? Are we not just repeating ourselves by using the former?
There's difference between my_fun(an_arg, *arg) and my_func(*args).
my_fun(an_arg, *arg)
Pass at least 1 argument or more arguments.
my_func(*args)
Pass any number of arguments, even 0.
Demo:
>>> def my_fun(an_arg, *arg):
pass
...
>>> def my_fun1(*arg):
pass
...
>>> my_fun()
...
TypeError: my_fun() missing 1 required positional argument: 'an_arg'
>>> my_fun1(1) #works fine
It's to help give your function a bit more meaning. Let's say that I'm trying to take in a function that increments a list of numbers by some parameter. Here's a silly example to illustrate:
def increase(increment, *nums):
return [num + increment for num in nums]
In this case, it's very clear what the first argument does, and what it's used for. In contrast, if we did this:
def increase(*args):
return [num + args[0] for num in args[1:]]
...then it's less clear what we're doing, and what all the arguments do.
In addition, it's also useful if we want to take in data, transform it, and pass in the rest of my arguments to another function.
Here's another contrived example:
def log(message, func, *args):
print message
func(*args)
Once again, if we just used only *args, our meaning is less clear:
def log(*args):
print args[0]
args[1](args[2:])
This would be much more error-prone, and hard to modify. It would also cause the function to fail if there weren't enough arguments -- by doing it the first way, you essentially make the first two elements mandatory and the rest optional.
They aren't equivalent forms. The other "repeat" forms bind arguments to discreet parameters and, more importantly, indicate that some parameters are required. Try to call def my_fun(an_arg, *arg): pass with 0 arguments.
While discreet (known) parameters work in many cases, *args allows for "sequence-variadic" functions and **kwargs allows "parameter-variadic" functions.
I've been looking at passing arrays, or lists, as Python tends to call them, into a function.
I read something about using *args, such as:
def someFunc(*args)
for x in args
print x
But not sure if this is right/wrong. Nothing seems to work as I want. I'm used to be able to pass arrays into PHP function with ease and this is confusing me. It also seems I can't do this:
def someFunc(*args, someString)
As it throws up an error.
I think I've just got myself completely confused and looking for someone to clear it up for me.
When you define your function using this syntax:
def someFunc(*args):
for x in args
print x
You're telling it that you expect a variable number of arguments. If you want to pass in a List (Array from other languages) you'd do something like this:
def someFunc(myList = [], *args):
for x in myList:
print x
Then you can call it with this:
items = [1,2,3,4,5]
someFunc(items)
You need to define named arguments before variable arguments, and variable arguments before keyword arguments. You can also have this:
def someFunc(arg1, arg2, arg3, *args, **kwargs):
for x in args
print x
Which requires at least three arguments, and supports variable numbers of other arguments and keyword arguments.
You can pass lists just like other types:
l = [1,2,3]
def stuff(a):
for x in a:
print a
stuff(l)
This prints the list l. Keep in mind lists are passed as references not as a deep copy.
You don't need to use the asterisk to accept a list.
Simply give the argument a name in the definition, and pass in a list like
def takes_list(a_list):
for item in a_list:
print item
Python lists (which are not just arrays because their size can be changed on the fly) are normal Python objects and can be passed in to functions as any variable. The * syntax is used for unpacking lists, which is probably not something you want to do now.
def sumlist(items=[]):
sum = 0
for i in items:
sum += i
return sum
t=sumlist([2,4,8,1])
print(t)