So i'm working on the daily coding problems and the one i got today got me stumped.
cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the
first and last element of that pair. For example, car(cons(3, 4))
returns 3, and cdr(cons(3, 4)) returns 4.
Given this implementation of cons:
def cons(a, b):
def pair(f):
return f(a, b)
return pair
Implement car and cdr.
I don't understand what the "f" represents.
I tried printing the thing i get from that function:
x = cons(3, 4)
<function cons.<locals>.pair at 0x2adc0ec45ae8>
But i still don't understand what it is. Any ideas?
Let's examine cons:
def cons(a, b):
def pair(f):
return f(a, b)
return pair
So calling cons(3, 4) dynamically creates a function as if you statically define it like this :
def pair_3_4(f)
return f(3, 4)
Similarly :
pair1 = cons(2, 6)
pair2 = cons(5, 8)
pair3 = cons("a", "b")
is equivalent to :
def pair1(f)
return f(2, 6)
def pair2(f)
return f(5, 8)
def pair3(f)
return f("a", "b")
Now, let's examine pair:
def pair(f):
return f(a, b)
From this, you can guess that f must be a callable object, and that it takes two arguments. The most simple callable object is a function, so let's say that f is a function.
Also you can see that pair simply calls f with whatever a and b were bound to it by cons.
Here is an example where I use print as the f :
>>> pair_3_4 = cons(3, 4)
>>> pair_3_4(print)
3 4
>>> cons(3, 4)(print)
3 4
>>> print(3, 4)
3 4
Related
I am trying to understand the concept of decorators in python when we have different arguments (In fact I am not sure how to pass arguments to the decorator). I have written the small and simple code below but I am not able to run it:
def advance(*arg, function):
result = a * function(b, c)
print(result)
#advance
def Sum1(b, c):
return b + c
print(Sum1(1, 2, 3))
When running the code, I get TypeError: advance() missing 1 required keyword-only argument: 'function'.
Decorator has to return a new function, so you would like to have something like:
def advance(function):
def wrapped(a, b, c):
return a * function(b, c)
return wrapped
#advance
def Sum1(b, c):
return b + c
print(Sum1(10, 2, 3))
I've made example functions, I know that i will use meta_function only with arguments sum and mult
def sum(a, b):
return a + b
def mult(a, b):
return a*b
def meta_function(function):
if function == sum:
c = 1
print('SUM')
elif function == mult:
c = 2
print("MULT")
print(c)
meta_function(sum)
meta_function(mult)
Output:
SUM
1
MULT
2
PyCharm informing me that Local variable might be referenced before assignment. I know, if some other arguments will be taken except sum and mult, this will lead to error. What is the best practice to handle this sophisticated issue? Try - except? Am I use wright way to take another functions in meta_function?
Pycharm warns you because if the function isn't sum or mult, your code will crash.
You need to define c at the top, just in case, or add an else statement.
# I changed the name of sum_ in order to avoid shadowing with
# the python built-in function sum()
def sum_(a, b):
return a + b
def mult(a, b):
return a * b
def meta_function(function):
c = 0 # if function isn't sum or mult, at least c exist
# here, "is" is better then "=="
if function is sum_:
c = 1
print('SUM')
# here, "is" is better then "=="
elif function is mult:
c = 2
print("MULT")
# Or you can do the else, but since you don't do anything in it,
# the best solution is to define a base state of c
# else:
# c = 0
print(c)
meta_function(sum_)
meta_function(mult)
Here is a better solution in my opinion, in order to prevent changing the core of meta_function() if you want to add more functions:
def sum_(a, b):
return a + b
def mult(a, b):
return a * b
function_switch = {
sum_: ('SUM', 1),
mult: ('MULT', 2),
}
def meta_function(function):
function_res = function_switch.get(function)
if function_res is None:
print(0)
return
print(function_res[0])
print(function_res[1])
meta_function(sum_)
meta_function(mult)
In Python 3.9, you can do:
def meta_function(function):
if (function_res := function_switch.get(function)) is None:
print(0)
return
print(function_res[0])
print(function_res[1])
Warning is shown because if both cases are not happened, then variable c will be not defined and print fill fail. The variable must be defined in the current scope.
It is better to make a generic function, executes the function and returns desired values. I've refactored your function as taking callable function and parameters for given function. Executes given callable and return some string that is built with return results.
def sum(a, b):
return a + b
def mult(a, b):
return a*b
def meta_function(func, *args):
# initialize default variables
c: int = None
return_str: str = None
# try execute given callable `func`
try:
# execute
c = func(*args)
# build some result string and assign
return_str = f"Function:{func.__name__}, Args: {args}, Result: {c}"
except Exception as exp:
# build result string for failed functions
return_str = f"Error calling given function `{func.__name__}` with params: {params}"
return return_str
def incompatible_func(x, y, z): # takes three parameters, will fail
return x + y * z
params = (1,2)
rslt = meta_function(sum, *params)
print(rslt)
rslt = meta_function(mult, *params)
print(rslt)
rslt = meta_function(incompatible_func, *params)
print(rslt)
And the output is:
Function:sum, Args: (1, 2), Result: 3
Function:mult, Args: (1, 2), Result: 2
Error calling given function `incompatible_func` with params: (1, 2)
I've been having trouble understanding what h(a)(b) means. I'd never seen one of those before yesterday, and I couldn't declare a function this way:
def f (a)(b):
return a(b)
When I tried to do def f (a, b):, it didn't work either. What do these functions do? How can I declare them? And, finally, what's the difference between f(a, b)and f(a)(b)?
Functions with multiple parameter brackets don't exist, as you saw when you tried to define one. There are, however, functions which return (other) functions:
def func(a):
def func2(b):
return a + b
return func2
Now when you call func() it returns the inner func2 function:
>>> func2 = func(1) # You don't have to call it func2 here
>>> func2(2)
3
But if you don't need the inner function later on, then there's no need to save it into a variable and you can just call them one after the other:
>>> func(1)(2) # func(1) returns func2 which is then called with (2)
3
This is a very common idiom when defining decorators that take arguments.
Notice that calling func() always creates a new inner function, even though they're all named func2 inside of the definition of our func:
>>> f1 = func(1)
>>> f2 = func(1)
>>> f1(1), f2(1)
(2, 2)
>>> f1 is f2
False
And, finally, what's the difference between f(a, b)and f(a)(b)?
It should be clear now that you know what f(a)(b) does, but to summarize:
f(a, b) calls f with two parameters a and b
f(a)(b) calls f with one parameter a, which then returns another function, which is then called with one parameter b
f(a)(b) just means that the expression f(a) returns a value that is itself callable. It's a short form of
g = f(a)
g(b)
You might be more comfortable adding a pair of redundant parentheses to emphasize that this is not a single syntactic construct.
(f(a))(b) # f(a) is evaluated first, then the result is applied to b
It is exactly analogous to the same doubling of square brackets for indexing nested dictionaries.
d1[x][y]
is equivalent to
d2 = d1[x]
d2[y]
Lets say we have an expression like
f(a)(b)
then, f(a) returns a function itself which gets invoked with argument b. Consider the following example
def f(a):
def g(b):
return a * b
return g
Then f(5)(4) evaluates to 5 * 4, since f(5) returns a function which is basically
def g(b):
return 5 * b
One could now do stuff like this
mult_by_5 = f(5)
[mult_by_5(x) for x in range(10)]
Let's be fancy, what about more nested functions?:
def f(a):
def g(b):
def h(c):
return a * b *c
return h
return g
f(2)(3)(4) # 24
I have a function, and when it is called, I'd like to know what the return value is going to be assigned to - specifically when it is unpacked as a tuple. So:
a = func() # n = 1
vs.
a, b, c = func() # n = 3
I want to use the value of n in func. There must be some magic with inspect or _getframe that lets me do this. Any ideas?
Disclaimer (because this seems to be neccessary nowadays): I know this is funky, and bad practice, and shouldn't be used in production code. It actually looks like something I'd expect in Perl. I'm not looking for a different way to solve my supposed "actual" problem, but I'm curious how to achive what I asked for above. One cool usage of this trick would be:
ONE, TWO, THREE = count()
ONE, TWO, THREE, FOUR = count()
with
def count():
n = get_return_count()
if not n:
return
return range(n)
Adapted from http://code.activestate.com/recipes/284742-finding-out-the-number-of-values-the-caller-is-exp/:
import inspect
import dis
def expecting(offset=0):
"""Return how many values the caller is expecting"""
f = inspect.currentframe().f_back.f_back
i = f.f_lasti + offset
bytecode = f.f_code.co_code
instruction = ord(bytecode[i])
if instruction == dis.opmap['UNPACK_SEQUENCE']:
return ord(bytecode[i + 1])
elif instruction == dis.opmap['POP_TOP']:
return 0
else:
return 1
def count():
# offset = 3 bytecodes from the call op to the unpack op
return range(expecting(offset=3))
Or as an object that can detect when it is unpacked:
class count(object):
def __iter__(self):
# offset = 0 because we are at the unpack op
return iter(range(expecting(offset=0)))
There is little magic about how Python does this.
Simply put, if you use more than one target name on the left-hand side, the right-hand expression must return a sequence of matching length.
Functions that return more than one value really just return one tuple. That is a standard Python structure, a sequence of a certain length. You can measure that length:
retval = func()
print len(retval)
Assignment unpacking is determined at compile time, you cannot dynamically add more arguments on the left-hand side to suit the function you are calling.
Python 3 lets you use a splat syntax, a wildcard, for capturing the remainder of a unpacked assignment:
a, b, *c = func()
c will now be a list with any remaining values beyond the first 2:
>>> def func(*a): return a
...
>>> a, b, *c = func(1, 2)
>>> a, b, c
(1, 2, [])
>>> a, b, *c = func(1, 2, 3)
>>> a, b, c
(1, 2, [3])
>>> a, b, *c = func(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 1 value to unpack
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)