I am relatively new to Python and I was messing around with decorators and I found myself stuck on how to explain the output I was getting after running a decorated function.
Here's the code:
I first defined a function add like this
def add(a, b):
'''Returns the sum of two numbers a and b'''
return a + b
I then created a decorator like so
def decorator_function(somefunction):
def wrapper_function(*args, **kwargs):
return f'{somefunction(*args, **kwargs)}!!!'
return wrapper_function
#decorator_function
def add(a, b):
'''Returns the sum of two numbers a and b and since it is decorated it is going to return the
result with 3 !!!'''
return a + b
I then ran the function add like so and got the following output
>>> add(5, 15)
'20!!!'
I then ran the function like so and got a different output
>>> result = decorator_function(add)
>>> result(5, 15)
'20!!!!!!' # Why did I get 6 '!'
>>> add = decorator_function(add)
>>> add(5, 15)
'20!!!!!!' # Why did I get 6 '!'?
Now I don't understand why I got 6 exclamation marks
You already have decorated your add function with
#decorator_function
If you write later write
decorator_function(add)
it is actually:
decorator_function(decorator_function(add))
Related
I came across a problem for python decorator.
The question is:
Create a decorator which is responsible for multiplication of 2 numbers (say a,b), this multiplication of the numbers gives another number(say c).
Then needed to a create a actual function which does the summation of a,b and c.
Something like:
Decorator : a*b => c
Actual function: a+b+c
I have reached until this stage:
def my_decorator(func):
def addition(a,b):
c = a*b
return c
return addition
#my_decorator
def actual_func(a,b):
return a+b+c
But it gives error all the time.
Actual func
Takes a, b and multiplies them
decorator sums original numbers plus ouput of multiply func
Code
def my_decorator(func):
def addition(a,b):
c = func(a, b) # use multiplication function
return a + b + c # performs desired calculation
return addition # modified function for desired calculation
#my_decorator
def actual_func(a,b):
return a*b # does multiplication
actual_func(2, 4) # return 14 i.e. 2 + 4 + 2*4
Please include your error in the question.
Your current state looks promising but there are small mistakes:
the inner function (addition) should call func with the correct arguments (a,b,c) and return its return value
actual_func should add the three numbers and needs to take these three as parameters. Else c is not defined in actual_func.
Complete code:
def my_decorator(func):
def addition(a,b):
c = a*b
return func(a,b,c)
return addition
#my_decorator
def actual_func(a,b,c):
return a+b+c
First of all to find "lcm" of two numbers I made a function lcm(a, b). Then I thought of finding "hcf" too so I made a decorator decor and defined a function hcf(a, b) in it. And then I returned this function by just typing the name of the function and I didn't put brackets with it but it is still working. I cant understand why this function is working even though I didn't used brackets.
def decor(lcm_arg): # just to practice decorators
def hcf(a, b):
if a > b:
a, b = b, a
while True:
if b % a == 0:
print("hcf is", a)
break
else:
a, b = b % a, a
return lcm_arg(a, b)
return hcf # how hcf function is working without using brackets
#decor
def lcm(a, b):
if a > b:
a, b = b, a
for x in range(b, a*b+1, b):
if x % a == 0:
print("lcm is", x)
break
lcm(2, 4)
Output:
hcf is 2
lcm is 4
I don't think you understand decorators. Let's make a minimal example.
def my_decorator(some_function):
def new_function(*args, **kwargs):
'announces the result of some_function, returns None'
result = some_function(*args, **kwargs)
print('{} produced {}'.format(some_function.__name__, result))
return new_function # NO FUNCTION CALL HERE!
#my_decorator
def my_function(a, b):
return a + b
my_function(1, 2) # will print "my_function produced 3"
We have a simple function my_function which returns the sum of its two arguments and a decorator which will just print out the result of whatever function it decorates.
Note that
#my_decorator
def my_function(a, b):
return a + b
is equivalent to
def my_function(a, b):
return a + b
my_function = my_decorator(my_function)
Since my_decorator accepts a function as an argument (here we are giving it my_function) and returns a new function new_function (without calling it!), we effectively override my_function because we reassign the name to whatever my_decorator returns.
In action:
>>> my_function(1, 2)
my_function produced 3
Note that at every point in the example when a function is called, it happens with the parentheses-syntax. Here are all the function calls that happen in the first block of code I posted, in order:
my_decorator(my_function) is called and the return value is reassigned to the name my_function. This either happens through the # syntax or more explicitly in the equivalent code snippet.
my_function(1, 2) is called. At this point, my_function is the new_function that got returned by the decorator. Brain-parse it as new_function(1, 2).
Inside the body of new_function, the argument we gave to my_decorator is called (result = some_function(*args, **kwargs)) which happens to be the value of my_function before the reassignment that happened in step 1.
print is called.
If you want to understand how new_function is holding on to some_function despite my_decorator already having returned from its call, I suggest looking into the topics free variables and closures.
return hcf does not call the function because there are no parentheses, as you noticed. The decor function is used as a decorator which reassigns the name lcm to refer to the returned function. What I mean by this is that
#decor
def lcm(a, b):
// ...
is equivalent to
def lcm(a, b):
// ...
lcm = decor(lcm)
After this executes, lcm refers to the function hcf. So calling lcm(2, 4) now executes the code in hcf. I think the key here is to understand that at the line lcm(2, 4), lcm and hcf are two names which refer to the same function.
My task is the following: "Write a function named operate that takes as parameters 2 integers named a, b and a function named func which that takes 2 integers as parameters. Also write the functions add, sub, mul, and div that take 2 integer parameters and perform the operation corresponding to their name and print the result. Calling operate(a, b, func) should result in a call to func(a, b)". I've done the first four parts, but I'm stuck on how to implement operate. Here is my code so far:
# this adds two numbers given
def add(a,b):
print (a + b)
# this subtracts two numbers given
def sub(a,b):
print (b - a)
# this multiplies two numbers given
def mul(a,b):
print (a * b)
# this divides two numbers given
def div(a,b):
print (a / b)
To achieve this you need to return something from your functions, not just print something. This lets you use the result later. To do this just use the return statement with some expression:
def add(a, b):
return a + b
def sub(a, b):
return a - b
def mul(a, b):
return a * b
def div(a, b):
return a / b
I've changed the order of your sub operation to be more in line with how subtraction is generally defined.
To now write an operate function is actually pretty easy. You've been given two parts already: the signature should be operate(a, b, func) and you should call func(a, b). This is actually almost all of what it will end up as - all you need to do is again return it (you could also print it here if you wanted):
def operate(a, b, func):
return func(a, b)
You can now do something like this:
print(operate(3, 2, add))
print(operate(3, 2, sub))
print(operate(3, 2, mul))
print(operate(3, 2, div))
Which will result in the output:
5
1
6
1.5
In a comments I asked about the standard library - you see, all of these are already implemented by Python. You can replace the first four function definitions with this:
from operator import add, sub, mul, truediv as div
Leaving you to only define operate and do some testing.
I'm trying to clean up some code in Python to vectorize a set of features and I'm wondering if there's a good way to use apply to pass multiple arguments. Consider the following (current version):
def function_1(x):
if "string" in x:
return 1
else:
return 0
df['newFeature'] = df['oldFeature'].apply(function_1)
With the above I'm having to write a new function (function_1, function_2, etc) to test for each substring "string" that I want to find. In an ideal world I could combine all of these redundant functions and use something like this:
def function(x, string):
if string in x:
return 1
else:
return 0
df['newFeature'] = df['existingFeature'].apply(function("string"))
But trying that returns the error TypeError: function() takes exactly 2 arguments (1 given) Is there another way to accomplish the same thing?
Edit:
def function(string, x):
if string in x:
return 1
else:
return 0
df['newFeature'] = df['oldFeature'].apply(partial(function, 'string'))
I believe you want functools.partial. A demo:
>>> from functools import partial
>>> def mult(a, b):
... return a * b
...
>>> doubler = partial(mult, 2)
>>> doubler(4)
8
In your case you need to swap arguments in function (because of idea of partial), and then just
df['existingFeature'].apply(partial(function, "string"))
I was wondering if it is possible in python to do the following:
def func1(a,b):
return func2(c,d)
What I mean is that suppose I do something with a,b which leads to some coefficients that can define a new function, I want to create this function if the operations with a,b is indeed possible and be able to access this outside of func1.
An example would be a simple fourier series, F(x), of a given function f:
def fourier_series(f,N):
...... math here......
return F(x)
What I mean by this is I want to creat and store this new function for later use, maybe I want to derivate it, or integrate or plot or whatever I want to do, I do not want to send the point(s) x for evaluation in fourier_series (or func1(..)), I simply say that fourier_series creates a new function that takes a variable x, this function can be called later outside like y = F(3)... if I made myself clear enough?
You should be able to do this by defining a new function inline:
def fourier_series(f, N):
def F(x):
...
return F
You are not limited to the arguments you pass in to fourier_series:
def f(a):
def F(b):
return b + 5
return F
>>> fun = f(10)
>>> fun(3)
8
You could use a lambda (although I like the other solutions a bit more, I think :) ):
>>> def func2(c, d):
... return c, d
...
>>> def func1(a, b):
... c = a + 1
... d = b + 2
... return lambda: func2(c,d)
...
>>> result = func1(1, 2)
>>> print result
<function <lambda> at 0x7f3b80a3d848>
>>> print result()
(2, 4)
>>>
While I cannot give you an answer specific to what you plan to do. (Looks like math out of my league.)
I can tell you that Python does support first-class functions.
Python may return functions from functions, store functions in collections such as lists and generally treat them as you would any variable.
Cool things such as defining functions in other functions and returning functions are all possible.
>>> def func():
... def func2(x,y):
... return x*y
... return func2
>>> x = func()
>>> x(1,2)
2
Functions can be assigned to variables and stored in lists, they can be used as arguments for other functions and are as flexible as any other object.
If you define a function inside your outer function, you can use the parameters passed to the outer function in the definition of the inner function and return that inner function as the result of the outer function.
def outer_function(*args, **kwargs):
def some_function_based_on_args_and_kwargs(new_func_param, new_func_other_param):
# do stuff here
pass
return some_function_based_on_args_and_kwargs
I think what you want to do is:
def fourier_series(f,N):
#...... math here......
def F(x):
#... more math here ...
import math #blahblah, pseudo code
return math.pi #whatever you want to return from F
if f+N == 2: #pseudo, replace with condition where f,N turn out to be useful
return F
else:
return None
Outside, you can call this like:
F = fourier_series(a,b)
if F:
ans = F(x)
else:
print 'Fourier is not possible :('
The important thing from Python's point of view are:
Yes, you can write a function inside a function
Yes, you can return a function from a function. Just make sure to return it using return F (which returns the function object) as compared to return F(x) which calls the function and returns the value
I was scraping through some documentation and found this.
This is a Snippet Like your code:
def constant(a,b):
def pair(f):
return f(a,b)
return pair
a = constant(1,2) #If You Print variable-> a then it will display "<function constant.
#<locals>.pair at 0x02EC94B0>"
pair(lambda a, b: a) #This will return variable a.
Now, constant() function takes in both a and b and return a function called "Anonymous Function" which itself takes in f, and calls f with a and b.
This is called "closures". Closures is basically an Instance of a Function.
You can define functions inside functions and return these (I think these are technically closures):
def make_f(a, b):
def x(a, b):
return a+b
return x(a, b)