Here is a simple example showing a problem that I don't understand
callbacks = [lambda : 1, lambda : 2]
for i, c in enumerate(callbacks):
if i == 0:
cb1 = c
cb2 = lambda : c()
print(cb1()) # print 1
print(cb2()) # print 2
It seems that in cb1, I'm correctly able to copy the "correct" callback, ie the one I'm refering to in the loop iteration
However in cb2, even though I'm defining cb2 at a time when c refers to the 1st callback, it's updated afterwards to refer to the second callback
Can someone please shed some light on whatis going on ? this is pretty disconcerting
Is it possible that I write a lambda for cb2 and still refer to the first callback ?
This is expected behavior. To see why, consider the following example:
x = 1
def d():
return x
d() # returns 1
x = 2
d() # now returns 2
In the above example, the function d returns whatever value is assigned to the variable x at the time of execution. Variables in an enclosing scope are not copied in a function body, but referenced. The same is true if the variable holds a function value. Remember, functions are just values in Python. The behavior doesn't change just because you're using lambda syntax to define your functions.
The lambda is evaluated on runtime, when c already changed.
Try this instead:
cb2 = lambda c=c: c()
Using a default value, you can fixate a certain local variable.
Related
Goal
I am trying to write a function where one or more of the input parameters is a global variable that is updated by the function, without having to return values from within the function. I am aware I could just return a tuple or two separate values from the function, but I think updating the global variables from within the function would be another interesting method if it is possible.
Reason to do this
Updating global variables with a function is easy when the global variable is known (ie. defined previously in the python script). However, I want to define the function in a separate .py file to easily use the function within other python scripts. Therefore, I need to be able to support different variable names to update.
While this is not at all necessary, I am just interested if this is even possible.
Example Pseudocode
I'm thinking something like this:
def math_function(input_val, squared_result, cubed_result):
squared_result = input_val**2 #update the var input as the squared_result parameter
cubed_result = input_val**3 #update the var input as the cubed_result parameter
where you would input a number for input_val and then global variables for squared_result and cubed_result that the function updates with the result. It would then theoretically work like:
#Declare global variables
b = 0
c = 0
#then somewhere in the code, call the function
math_function(2, b, c)
#check the new values
print(b) #Output: b = 4
print(c) #Output: c = 8
This would allow me to use the function in different python scripts without having to worry about what order the results are returned in.
First: I am in no way advocating this.
You could use the globals builtin function to access a global variable by name:
def gtest(name,value):
globals()[name] = value
gtest('new_global','new_value')
print(new_global)
I am not advocating this too, I would like to hear everyone's thoughts, but this can be done -
my_global = [0, 0]
def my_math_func(x, g):
g[0] = x ** 2
g[1] = x ** 3
my_math_func(3, my_global)
Suppose I have a Python function foo which takes a default argument, where that default is set to some global variable. If I now change that global variable before calling the function, the default argument is still set to the original value of that global variable.
For example:
x = 1
def foo(a=x):
print a
x = 2
foo()
This prints 1, instead of 2.
How should I be writing my code, so that I can change this global variable, and have it update this default argument?
A default variable is only evaluated and set once. So Python makes a copy of the reference and from then on, it always passes that reference as default value. No re-evaluation is done.
You can however solve this by using another object as default object, and then use an if statement to substitute it accordingly. Something like:
the_default = object()
x = 1
def foo(a = the_default):
if a is the_default:
a = x
print a
x = 2
foo()
Note that we use is to perform reference equality. So we check if it is indeed the default_object. You should not use the the_default object somewhere else in your code.
In many Python code, they use None as a default (and thus reduce the number of objects the construct). For instance:
def foo(a = None):
if a is None:
a = x
print a
Note however that if you do that, your program cannot make a distinction between a user calling foo() and foo(None) whereas using something as the_object makes it harder for a user to obtain a reference to that object. This can be useful if None would be a valid candidate as well: if you want foo(None) to print 'None' and not x.
For example: in this code, function changes when the variable changes. I would like to learn how to prevent the change in function behaviour when i change the variable. Is there some way to only get the value of the variable instead of the variable itself? Also are there any sources where I can learn more about problems like this?
a = 5
adder = lambda x: x + a
print(adder(5)) # prints 10
a = 50
print(adder(5)) # prints 55
Just like the equivalent function defined by a def statement (which is what you should be using, rather than assigning the result of a lambda expression to a name explicitly)
def adder(x):
return x + a
the name a isn't looked up until the function is called.
One way to make a function that specifically computes x + 5 when a == 5 at definition time is use a default argument value:
def adder(x, a=a):
return x + a
where the left-hand a is a new parameter (which isn't intended to be set explicitly), and the right-hand a is the value of the a in the current scope.
A better idea, though, is to define a closure, so that your function doesn't have a "hidden" parameter that can be abused.
# This is where a lambda expression does make sense: you want
# a function, but don't need to give it a name.
def make_adder(a):
return lambda x: x + a
adder = make_adder(5)
a in adder is still a free variable, but now it refers to a variable in a scope which you don't "outside" access to after make_adder returns.
Is there a way to make function B to be able to access a non global variable that was declared in only in function A, without return statements from function A.
As asked, the question:
Define two functions:
p: prints the value of a variable
q: increments the variable
such that
Initial value of the variable is 0. You can't define the variable in the global
enviroment.
Variable is not located in the global environment and the only way to change it is by invoking q().
The global enviroment should know only p() and q().
Tip: 1) In python, a function can return more than 1 value. 2) A function can be
assigned to a variable.
# Example:
>>> p()
0
>>> q()
>>> q()
>>> p()
2
The question says the global enviroment should know only p and q.
So, taking that literally, it could be done inline using a single function scope:
>>> p, q = (lambda x=[0]: (lambda: print(x[0]), lambda: x.__setitem__(0, x[0] + 1)))()
>>> p()
0
>>> q()
>>> q()
>>> p()
2
Using the tips provided as clues, it could be done something like this:
def make_p_and_q():
context = {'local_var': 0}
def p():
print('{}'.format(context['local_var']))
def q():
context['local_var'] += 1
return p, q
p, q = make_p_and_q()
p() # --> 0
q()
q()
p() # --> 2
The collection of things that functions can access is generally called its scope. One interpretation of your question is whether B can access a "local variable" of A; that is, one that is defined normally as
def A():
x = 1
The answer here is "not easily": Python lets you do a lot, but local variables are one of the things that are not meant to be accessed inside a function.
I suspect what your teacher is getting at is that A can modify things outside of its scope, in order to send information out without sending it through the return value. (Whether this is good coding practise is another matter.) For example, functions are themselves Python objects, and you can assign arbitrary properties to Python objects, so you can actually store values on the function object and read them from outside it.
def a():
a.key = "value"
a()
print a.key
Introspection and hacking with function objects
In fact, you can sort of get at the constant values defined in A by looking at the compiled Python object generated when you define a function. For example, in the example above, "value" is a constant, and constants are stored on the code object:
In [9]: a.func_code.co_consts
Out[9]: (None, 'value')
This is probably not what you meant.
Firstly, it's bad practise to do so. Such variables make debugging difficult and are easy to lose track of, especially in complex code.
Having said that, you can accomplish what you want by declaring a variable as global:
def funcA():
global foo
foo = 3
def funcB():
print foo # output is 3
That's one weird homework assignment; especially the tips make me suspect that you've misunderstood or left out something.
Anyway, here's a simpler solution than the accepted answer: Since calls to q increment the value of the variable, it must be a persistent ("static") variable of some sort. Store it somewhere other than the global namespace, and tell p about it. The obvious place to store it is as an attribute of q:
def q():
q.x += 1
q.x = 0 # Initialize
def p():
print(q.x)
def a(b=[]):
b.append(1)
return b
print a()
print a()
All of a sudden i got a list with 2 elems, but how? Shouldn't b be getting set to empty list every time.
Thanks for the help
Default arguments are only evaluated once, when the function is defined. It retains the same object from one invocation to the next, which means that the same list keeps getting appended to. Use a default value of None and check for that instead if you want to get around this.
Nothing to do with closures, at least not in the usual sense.
The default value for b is not "a new empty list"; it is "this particular object which I just created right now while defining the function, initializing it to be an empty list". Every time the function is called without an argument, the same object is used.
The corrected version, for the reasons given in other answers, is:
def a(b=None):
b = [] if b is None else b
b.append(1)
return b
default arguments are evaluated (once) when the function is defined, not each time it is called.
try this:
def a(b=None):
if b is None
b = []
b.append(1)
return b
print a()
print a()