Try to define a function to inline update input arguments, but doesn't find the way to go.
For example,
def swap(a, b):
# Result here not return to arguments after called
a, b = b, a
a, b = 5, 3
swap(a, b) # Expect a=3, b=5 after call swap
My question is how to define the function, not different way to call swap or class method. Python does not support call by reference, maybe no way for it.
Integers are immutable objects.
Thus, when you pass them into the function, all that gets changed are the local references. There is nothing you can do to make the function alter the values of the main-program variables a and b.
The way to return a variable from a function is by calling return. For example,
def swap(a, b):
a, b = b, a
return a, b
a, b = swap(b, a)
This seems entirely pointless, though. For something like this, it would be better to just do it outside the function.
a, b = 5, 3
a, b = b, a
If you really needed to change the variables inside a function, and you knew what they were referred to outside the function, you could use the global keyword.
a, b = 5, 3
def swap(): # No parameters necessary.
global a
global b
a, b = b, a
swap()
Related
I'm learning this language hence I'm new with Python. The code is:
def add(a, b):
return a + b
def double_add(x, a, b):
return x(x(a, b), x(a, b))
a = 4
b = 5
print(double_add(add, a, b))
The add function is simple, it adds two numbers. The double_add function has three arguments. I understand what is happening (With some doubts). The result is 18. I can't understand how double_add uses add to function.
The question is, what is the connection between these two functions?
It would be helpful if tell me some examples of using a function as an argument of another function.
Thanks in advance.
In python language, functions (and methods) are first class objects. First Class objects are those objects, which can be handled uniformly.
So, you just pass a method as an argument.
Your method will return add(add(4, 5), add(4, 5)) which is add(9, 9) and it's equals to 18.
A function is an object just like any other in Python. So you can pass it as argument, assign attributes to it, and well maybe most importantely - call it. We can look at a simpler example to understand how passing a function works:
def add(a, b):
return a + b
def sub(a, b):
return a - b
def operate(func, a, b):
return func(a, b)
a = 4
b = 5
print(operate(add, a, b))
print(operate(sub, a, b))
operate(print, a, b)
And this prints out:
9
-1
4 5
That is because in each case, func is assigned with the respective function object passed as an argument, and then by doing func(a, b) it actually calls that function on the given arguments.
So what happens with your line:
return x(x(a, b), x(a, b))
is first both x(a, b) are evaluated as add(4, 5) which gives 9. And then the outer x(...) is evaluated as add(9, 9) which gives 18.
If you would add print(x) in the double_add function you would see that it would print <function add at 0x10dd12290>.
Therefore, the code of double_add is basically the same as if you would do following:
print(add(add(a,b), add(a,b))) # returns 18 in your case
Functions are objects in Python, just like anything else such as lists, strings.. and you can pass them same way you do with variables.
The function object add is passed as an argument to double_add, where it is locally referred to as x. x is then called on each, and then on the two return values from that.
def double_add(x, a, b):
return x(x(a, b), x(a, b))
Let's write it differently so it's easier to explain:
def double_add(x, a, b):
result1 = x(a, b)
result2 = x(a, b)
return x(result1, result2)
This means, take the function x, and apply it to the parameters a and b. x could be whatever function here.
print(double_add(add, a, b))
Then this means: call the double_add function, giving itaddas the first parameter. Sodouble_add`, would do:
result1 = add(a, b)
result2 = add(a, b)
return add(result1, result2)
This is a very simple example of what is called "dependency injection". What it means is that you are not explicitly defining an interaction between the two functions, instead you are defining that double_add should use some function, but it only knows what it is when the code is actually run. (At runtime you are injecting the depedency on a specific function, instead of hardcoding it in the function itself),
Try for example the following
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def double_add(x, a, b):
return x(x(a, b), x(a, b))
a = 4
b = 5
print(double_add(add, a, b))
print(double_add(subtract, a, b))
In other words, double_add has become a generic function that will execute whatever you give it twice and print the result
I've been given this function. It returns the function pair that it also returns the function f I think. That is the part that tricks me, I don't know what f(a, b) is and how to use it.
def cons(a, b):
def pair(f):
return f(a, b)
return pair
To help you understand what is going on, consider the following example:
def cons(a, b):
def pair(f):
return f(a, b)
return pair
def my_func(a, b):
return a + b
# cons returns a function that takes a function arg and calls it with args (a, b),
# in this case (1, 3). Here a, b are considered "closured" variables.
apply_func = cons(1, 3)
print apply_func(my_func) # prints 4
Lets analyse this from inside out:
def cons(a, b):
def pair(f):
return f(a, b)
return pair
The innermost level is return f(a, b) - that obviously calls function f with arguments (a, b) and returns whatever the result of that is.
The next level is pair:
def pair(f):
return f(a, b)
Function pair takes a function as an argument, calls that function with two arguments (a, b) and returns the result. For example:
def plus(x, y):
return x + y
a = 7
b = 8
pair(plus) # returns 15
The outermost level is cons - it constructs function pair which has arbitrary a and b and returns that version of pair. E.g.
pair_2_3 = cons(2,3)
pair_2_3(plus) # returns 5, because it calls plus(2, 3)
. . . I don't know what f(a, b) is and how to use it.
f(a, b) is simply a function call. All the code you provided does is define a function that returns a function. The function returned from the first function, itself returns a function. I assume the way it would be used is perhaps something like:
>>> cons(1, 2)(lambda x, y: x + y)
3
>>>
The above code would be equivalent to:
>>> pair_func = cons(1, 2) # return the `pair` function defined in `cons`
>>> f = lambda x, y: x + y
>>> pair_func(f) # apply the `f` function to the arguments passed into `cons`.
3
>>>
It might also help to note that the pair function defined in this case, is what's know as a closure. Essentially, a closure is a function which has access to local variables from an enclosing function's scope, after the function has finished execution. In your specific case, cons is the enclosing function, pair is the closure, and a and b are the variables the closure is accessing.
Well if you could share the complete question then we might be able to help you better. Meanwhile what I can tell you here is that in the return of pair(f) the program is calling a function f which takes two arguments a and b. This function f(a,b) is called and then its value will be returned to pair(f).
But the point to note here is that in pair function we already have a local variable f, so when we will try to call the function f(a,b) it will give us UnboundedLocalVariable error. Therefore, we will need to change the name of this function from f to something else.
I've often been frustrated by the lack of flexibility in Python's iterable unpacking.
Take the following example:
a, b = range(2)
Works fine. a contains 0 and b contains 1, just as expected. Now let's try this:
a, b = range(1)
Now, we get a ValueError:
ValueError: not enough values to unpack (expected 2, got 1)
Not ideal, when the desired result was 0 in a, and None in b.
There are a number of hacks to get around this. The most elegant I've seen is this:
a, *b = function_with_variable_number_of_return_values()
b = b[0] if b else None
Not pretty, and could be confusing to Python newcomers.
So what's the most Pythonic way to do this? Store the return value in a variable and use an if block? The *varname hack? Something else?
As mentioned in the comments, the best way to do this is to simply have your function return a constant number of values and if your use case is actually more complicated (like argument parsing), use a library for it.
However, your question explicitly asked for a Pythonic way of handling functions that return a variable number of arguments and I believe it can be cleanly accomplished with decorators. They're not super common and most people tend to use them more than create them so here's a down-to-earth tutorial on creating decorators to learn more about them.
Below is a decorated function that does what you're looking for. The function returns an iterator with a variable number of arguments and it is padded up to a certain length to better accommodate iterator unpacking.
def variable_return(max_values, default=None):
# This decorator is somewhat more complicated because the decorator
# itself needs to take arguments.
def decorator(f):
def wrapper(*args, **kwargs):
actual_values = f(*args, **kwargs)
try:
# This will fail if `actual_values` is a single value.
# Such as a single integer or just `None`.
actual_values = list(actual_values)
except:
actual_values = [actual_values]
extra = [default] * (max_values - len(actual_values))
actual_values.extend(extra)
return actual_values
return wrapper
return decorator
#variable_return(max_values=3)
# This would be a function that actually does something.
# It should not return more values than `max_values`.
def ret_n(n):
return list(range(n))
a, b, c = ret_n(1)
print(a, b, c)
a, b, c = ret_n(2)
print(a, b, c)
a, b, c = ret_n(3)
print(a, b, c)
Which outputs what you're looking for:
0 None None
0 1 None
0 1 2
The decorator basically takes the decorated function and returns its output along with enough extra values to fill in max_values. The caller can then assume that the function always returns exactly max_values number of arguments and can use fancy unpacking like normal.
Here's an alternative version of the decorator solution by #supersam654, using iterators rather than lists for efficiency:
def variable_return(max_values, default=None):
def decorator(f):
def wrapper(*args, **kwargs):
actual_values = f(*args, **kwargs)
try:
for count, value in enumerate(actual_values, 1):
yield value
except TypeError:
count = 1
yield actual_values
yield from [default] * (max_values - count)
return wrapper
return decorator
It's used in the same way:
#variable_return(3)
def ret_n(n):
return tuple(range(n))
a, b, c = ret_n(2)
This could also be used with non-user-defined functions like so:
a, b, c = variable_return(3)(range)(2)
Shortest known to me version (thanks to #KellyBundy in comments below):
a, b, c, d, e, *_ = *my_list_or_iterable, *[None]*5
Obviously it's possible to use other default value than None if necessary.
Also there is one nice feature in Python 3.10 which comes handy here when we know upfront possible numbers of arguments - like when unpacking sys.argv
Previous method:
import sys.argv
_, x, y, z, *_ = *sys.argv, *[None]*3
New method:
import sys
match sys.argv[1:]: #slice needed to drop first value of sys.argv
case [x]:
print(f'x={x}')
case [x,y]:
print(f'x={x}, y={y}')
case [x,y,z]:
print(f'x={x}, y={y}, z={z}')
case _:
print('No arguments')
My code is as follows...
def addition(a, b):
c = a + b
return c
And I then want to be able to use C later on in the program as a variable. For example...
d = c * 3
However, I get a NameError that 'C' is not defined... But I have returned c, so why can I not use it later on in the code?! So confused. Thanks!
(This is obviously a simpler version of what I want to do but thought I'd keep it simple so I can understand the basics of why I cannot call on this variable outside my function even though I am returning the variable. Thanks)
You have returned the value of c but not the whole variable i.e. the name c exists only within the scope it is instantiated.
So, if you want to use the value returned, you should re-assign it to a new name. You can do it by re-assigning it to c again, but it could be any name you wanted.
def addition(a, b):
c = a + b
return c
new_var = addition(1,2) #new_var gets the value 3
c = addition(2,3) #c gets the value 5
Take a look at this nice explanation about variables and scopes (link)
You usually define a function to use it later in your code. For that case, use another global variable c:
def addition(a, b):
c = a + b
return c
c = addition(1, 2)
d = c * 3 # d == 9
Functions allow this usage of repeated code, or procedure distinction, so that you can later write in your code
m = addition(4, 5)
and it will store the required result of the functionality into m.
If you want to define c in the function and use it later, you can use global variables.
c = 0
def addition(a, b):
global c
c = a + b
return c
It's not considered good to use globals, though. You could also call the function in the variable assignment.
d = addition(a, b) * 3
For this, you need to put real numbers in the place of a and b. I recommend you use the second option.
I understand from this answer why the warning exists. However, why would the default value of it be 2?
It seems to me that classes with a single public method aside from __init__ are perfectly normal! Is there any caveat to just setting
min-public-methods=1
in the pylintrc file?
The number 2 is completely arbitrary. If min-public-methods=1 is a more fitting policy for your project and better matches your code esthetic opinions, then by all means go for it. As was once said, "Pylint doesn't know what's best".
For another perspective, Jack Diederich gave a talk at PyCon 2012 called "Stop Writing Classes".
One of his examples is the class with a single method, which he suggests should be just a function. If the idea is to set up an object containing a load of data and a single method that can be called later (perhaps many times) to act on that data, then you can still do that with a regular function by making an inner function the return value.
Something like:
def complicated(a, b, c, d, e):
def inner(k):
return (a*k, b*k, c*k, d*k, e*k)
return inner
foo = complicated(1, 2, 3, 4, 5)
result = foo(100)
This does seem much simpler to me than:
class Complicated:
def __init__(self, a, b, c, d, e):
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
def calc(self, k)
return (self.a*k, self.b*k, self.c*k, self.d*k, self.e*k)
foo = Complicated(1, 2, 3, 4, 5)
result = Complicated.calc(100)
The main limitation of the function based approach is that you cannot read back the values of a, b, c, d, and e in the example.