Python: Why does this work? - python

I'd like some help clarifying the logic behind this.
from operator import add, sub
def a_plus_abs_b(a, b):
if b < 0:
f = sub # subtract a negative value
else:
f = add # add a positive one
return f(a, b)
I originally thought that I would have to think of some complex mathematical expression to allow this function to work, but someone showed me that just adding sub and add to f allows it to work properly. I'm having difficulty trying seeing how this works, so any clarifying remarks would be greatly appreciated.

Well, the logic itself works because a + abs(b) == a + b when b >= 0 and a + abs(b) == a - b when b < 0. I have commented out your code below.
from operator import add, sub
# two functions are imported:
# add(x,y) -> (x + y)
# sub(x,y) -> (x - y)
def a_plus_abs_b(a, b):
# when b < 0, (a + abs(b)) == (a - b)
if b < 0:
# f is a temporary variable holding the function sub
f = sub
# when b >= 0, (a + abs(b)) == (a + b)
else:
# f is a temporary variable holding the function add
f = add
# apply the arguments to the function determined above
return f(a, b)
However, this function could be written in a single line.
def a_plus_abs_b(a, b):
return a + abs(b)
The interesting thing about your function is that it highlights that functions are first-order objects in Python. This means that they can be passed around and assigned to variables and many other things.

sub is just the name of a reference to a function object that implements subtraction. Like any other reference, you can assign its value to another name, so f = sub simply makes f another name for reference stored in sub. As such, f(a, b) and sub(a, b) both call the same underlying function.

Related

Returning a function acting on an array

Consider a function getf taking a numpy array a and integer b. I want to return a callable function f such that f has value 0 for a< b and a>b and value 1 for a=b.
Something like
getf(a,b):
if a< b & b>a:
return 0
else:
return 1
does not work.
How do I can ensure that my return value is a function?
Example
a = [1,2,3,4,5]
b=2
return function with result array [0,1,0,0,0]
I don't understand why you need this, but this code defines a function and returns it, which can be called with two keyword arguments.
def getf():
def f(a, b):
return [int(b==i) for i in a]
return f
print(getf()(a = [1,2,3,4,5], b=2))
You need to use vectorial code, not return a scalar. Also, use a numpy array for a.
Finally, your conditions were incorrect (you had twice the same condition). I imagine you want to check if a is greater of equal b and lower or equal b (which is a == b, but let's keep it like this for the sake of the example).
def getf(a,b):
return ((a >= b) & (b >= a)).astype(int)
a = np.array([1,2,3,4,5])
b = 2
getf(a, b)
# array([0, 1, 0, 0, 0])

Using a function as an argument in another function through decorators

Here is the code:
def smart_divide(func):
def inner(a,b):
print("I am going to divide",a,"and",b)
if b == 0:
print("Whoops! cannot divide")
else:
return func(a,b)
return inner
#smart_divide
def divide(c,d):
return c/d
divide(5,2)
This is a program to divide two numbers and give an error message if the denominator is 0.
We are passing 5 and 2 as an argument to the function divide. How does inner function know that the value of a and b is 5 and 2 respectively?
According to what I read on internet : #smart_divide means that divide = smart_divide(divide(c,d))
The function divide has parameters c,d while the function inner has parameters a,b. How does the interpreter know that a = c and b = d?
#smart_divide means that
result = smart_divide(divide)(c,d)
That means, divide no points to inner function, and inner function calls the original function divide via func.
divide = smart_divide(divide(c,d)) is not really a good description of what happens. Have a look at the logical expansion step by step, in some kind of pseudo-Python:
divide(x, y)
= smart_divide(lambda c, d: c / d)(x, y)
= inner[func ~> lambda c, d: c / d](x, y)
= (def inner(a,b):
print("I am going to divide",a,"and",b)
if b == 0:
print("Whoops! cannot divide")
else:
return (lambda c, d: c / d)(a, b))(x, y)
= print("I am going to divide",a,"and",b)
if y == 0:
print("Whoops! cannot divide")
else:
return (lambda c, d: c / d)(x, y))
= print("I am going to divide",a,"and",b)
if y == 0:
print("Whoops! cannot divide")
else:
return x / y
The important thing that happens is lexical scoping. If you substitute an expression in another expression (the ~> operation above), then you assume that the names don't get mangled. That is, (lambda a: lambda b: a)(b)) does not become lambda b: b, but (something equivalent to) lambda b_prime: b. Names don't matter: this is called alpha equivalence.
Your understanding of decorators is almost right, this should clarify things.
The code in your example:
#smart_divide
def divide(c,d):
return c/d
Is functionally identical to:
def simple_div(c,d):
return c/d
divide = smart_divide(simple_div)
divide is defined as the inner of the smart_divide decorator.
smart_divide is given simple_div as a parameter (func), which is what inner uses to call the originally decorated simple_div method.
Furthermore: smart_divide can be used to decorate multiple functions because a new inner function is created each time smart_divide is called, so the func reference is different each time.

Python: variables not cumulative passing through functions

I am writing a function for a text adventure I'm making that acts as a progress bar when the player receives experience (which then levels the player up upon reaching 100). For some reason altering the values of my variables inside the function does not change their values outside the function even though I've returned all three. This becomes apparent when I try calling the function twice, each time adding 85 to the variable a.
Within the function, the objective is to pass the value of a to b, check if b is greater than or equal to 100, if so add 1 to c and remove 100 from b, then reset a to 0, print the result, and return the new values.
a = new experience b = current experience c = player level
a = 0
b = 0
c = 1
def func_1(a, b, c):
b = b + a
loop1 = 0
while loop1 < 1:
if b >= 100:
print(" ")
print("Add 1!")
print(" ")
c = c + 1
b = b - 100
else:
loop1 = loop1 + 1
a = a - a
print("c is " + str(c))
print("b is " + str(b))
print("a is " + str(a))
return a
return b
return c
a = a + 85
func_1(a, b, c)
a = a + 85
func_1(a, b, c)
print a, b, c
I'm really new to programming so I apologize for any inefficiencies. Thank you for any help and let me know if something doesn't make sense/is unclear.
Couple of things I see here:
First, out of the three return statements at the end of your code, only the first, return a, is executed. A return statement immediately stops execution of the function; return b and return c are never touched.
Second, you're having some confusion with the scope of the variables. a, b, and c defined outside of the function are global variables, while the a, b, and c passed into the function are local to the scope of the function. Thus, any modifications to the variables in your function won't affect the global variables.
You can do two things. First, have a global declaration at the beginning of the function:
def func_1():
global a
global b
global c
# Function body
This indicates to the function that a, b, and c are global variables that exist outside the function. Thus, passing in arguments is no longer needed; the function will search for variables outside the function. However, this is bad programming practice. The better option is to have func_1 return the modified values and reassign the global values to these new values, like so:
def func_1(a, b, c):
# Function body
return (a, b, c)
Then, to call the function and modify the variables outside the function:
a, b, c = func_1(a, b, c)
A couple suggestions:
You have a lot of incrementing going on, and python has specialized syntax for it: b = b + a can be shortened to b += a, c = c + 1 to c += 1, b = b - 100 to b -= 100. Also, simply reset a to 0 with a = 0 instead of subtracting a - a. Also, you don't need print(" "); print() will suffice.
Next, your while loop is unnecessary. Your function only needs to check once whether b >= 100, so there's no need to set up a loop. Increment b and then use a simple if statement to level up if necessary:
def func_1(a, b, c):
b += a
if b >= 100:
print("\nAdd 1!\n")
c += 1
b -= 100
a = 0
# Rest of the function
Inside func_1() the names a,b,c are local. When you change them nothing happens to the external a,b,c. You return the values correctly in the function, but then when calling the function you need to assign the values to the variables like this: a,b,c=func_1(a,b,c).
Returning a value doesn't set it unless you explicitly assign it in the calling function:
a, b, c = func_1(a, b, c)
Assigning inside the function doesn't affect the outer ones because they are considered "local scope". To counter that declare them global to affect the outer variables:
def func_1():
global a
global b
global c
Only one of these should be implemented
It is generally preferred not to declare globals. Ideally you should make a class for all of this, but these two options would require the least refactoring of your existing code
Note that a global variable (the variables outside of the function) are completely separate from the local variables (the variables inside the function).
This doesn't work because you can only return once. When you returned a, the function immediately stopped.
Also, since you didn't set any variable to the returned value, the global variables outside of the loop were unaffected. What you can do is return the a, b, and c values as a tuple, and then set the a, b and c global variables to that tuple.
return (a, b, c)
a, b, c = func_1(a, b, c)

How to add a to the absolute value of b without call abs

I'm having a little problem defining a function. I'm trying to add a to the absolute value of b without calling abs
from operator import add, sub
def a_plus_absolute_b(a, b):
"""Return a+abs(b), but without calling abs."""
if b < 0:
op = add(a, (b * -1))
else:
op = add(a, b)
return op(a, b)
You don't need to import add() for this.
Why don't you simply do
def a_plus_absolute_b(a, b):
"""Return a+abs(b), but without calling abs."""
if b < 0:
result = a - b
else:
result = a + b
return result
The solution you're looking for, which you're overlooking because you're obsessed with the idea of "negative", is as follows:
from operator import add, sub
def a_plus_absolute_b(a, b):
"""Return a+abs(b), but without calling abs."""
if b < 0:
op = sub
else:
op = add
return op(a, b)
Note how the parens, used to call a function, are only in the last line.

Python 3.x - Simple function that returns another function

I just recently started programming in Python and I've been trying to create a simple function that takes two parameters, a and b, and returns the result of the sum of a and |b|. I want to return f(a, b) and not just f. I know that I'm assigning f to be an int in my current code and so it returns "int not callable error" when I run. So I have to assign f to be a function of some sort. I'm fairly certain I'm missing something fundamental here, I'm just not sure exactly what. Thanks for any help!
from operator import add, sub
def a_plus_abs_b(a, b):
"""Return a+abs(b), but without calling abs.
>>> a_plus_abs_b(2, 3)
5
>>> a_plus_abs_b(2, -3)
5
"""
if b < 0:
f = sub(a, b)
else:
f = add(a, b)
return f(a, b)
f = sub(a, b)
doesn't create a function it computes the result, so when you're calling f(a, b) you're calling an integer.
To fix it, assign the function in-line with a ternary to select which function to create depending on the sign of b
f = sub if b < 0 else add
Jean-Fançois's answer is great, but if you're not understanding the fundamentals behind this, you should look into another example that uses lambdas:
def returns_function_example():
return lambda arg1: arg1 + 2
function_returned = returns_function_example()
print(function_returned(1))
# Output = 3
Run this in your debugger to see that "function_returned" in indeed a function. You can connect the dots after the "print" function is called.
Functions are first-class citizens in Pythonland, so that you can manipulate them as any other object.
Suppose you create your function:
def add(a, b): return a + b
If you write add, that's a function. But if you write add(2,4), or add(a, b) assuming that a and b are defined, then you are calling the function, so you get a result. These are two completely different things: there is f, a callable, and f(a,b) which returns a value.
So if you write:
>>> f = add
>>> type(f)
<class 'function'>
That's what you want, you now have a function that does exactly what add does (and you could say it actually is add).
By contrast, if you write:
>>> f = add(a,b)
>>> type(f)
<class 'int'>
>>> f
11
That's a value.
So to make a long story short:
from operator import add, sub
def a_plus_abs_b(a, b):
"""
Return a+abs(b), but without calling abs.
>>> a_plus_abs_b(2, 3)
5
>>> a_plus_abs_b(2, -3)
5
"""
if b < 0:
f = sub
else:
f = add
return f(a, b)

Categories

Resources