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.
Related
I am learning python 3 and programming in general for the first time, but I can't seem to distinguish a parameter and a variable?
A variable is just something that refers/points to some data you have.
x = 5
Here x is a variable. Variables can point to more kinds of data than just numbers, though. They can point to strings, functions, etc.
A parameter is something that is passed into a function
def my_function(y):
print(y)
Here y is a parameter. It doesn't contain a value yet. But if I want to call the function, I need to provide an argument to the function.
An argument is the actual value you provide to the function that replaces the parameter.
my_function(5)
Here, 5 is the argument. Of course, since x points to the value "5", I can do this too:
my_function(x)
which also prints 5
A parameter is a variable that was received as an argument to a function. Once the function has begun executing, the parameter is just like any other variable; it can be reassigned (and stops being tied to the caller's argument) like anything else.
global_variable = ... still a variable, just defined globally ...
def foo(parameter):
function_local_variable = ... something defined locally ...
foo(... argument that is bound to parameter in foo ...)
A parameter is a type of variable that serves as the input to a method.
A variable is a name (identifier) that refers to some value. Values can be either immutable types, that is, types that cannot be changed such as strings, bytes, integers and floating point numbers:
x = 5 # immutable type
y = x # y points to what x currently points to, namely 5
x = 9 # x points to a new value, 9
print(x, y) # 9 5
A variable can also name a mutable type:
class MyType:
pass
t1 = MyType()
t1.x = 5 # set member x to 5
t2 = t1 # t2 points to the same object as t1
t1.x = 9
print(t1.x, t2.x) # 9 9
A parameter (or argument) is a value passed to a function:
def foo(x):
print(x)
foo(some_value)
x is an argument to function foo. foo(some_value) invokes function foo with the value some_value as the actual value. The Python interpreter implicitly assigns x = some_value at the entry to function foo. Note that x is a variable is every sense of the word but that it is defined in the scope of foo so that it hides any other definition of x that may exist outside of foo.
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.
I need to monkey-patch my library to replace an instance of a symbol, and it's getting referenced by some function closures. I need to copy those functions (since I also need access to original unpatched version of the function as well), but __closure__ is immutable, and I can't copy.copy it, so how can I create new closure cells objects in Python 2.7?
I for example given this function
def f():
def incorrectfunction():
return 0
def g():
return incorrectfunction()
return g
def correctfunction():
return 42
func = f()
patched_func = patchit(f) # replace "incorrectfunction"
print func(), patched_func()
And I want to see
0, 42
The simple way to make a closure cell would be to make a closure:
def make_cell(val=None):
x = val
def closure():
return x
return closure.__closure__[0]
If you want to reassign an existing cell's contents, you'll need to make a C API call:
import ctypes
PyCell_Set = ctypes.pythonapi.PyCell_Set
# ctypes.pythonapi functions need to have argtypes and restype set manually
PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object)
# restype actually defaults to c_int here, but we might as well be explicit
PyCell_Set.restype = ctypes.c_int
PyCell_Set(cell, new_value)
CPython only, of course.
in lambda:
def make_cell(value):
fn = (lambda x: lambda: x)(value)
return fn.__closure__[0]
got anwer from https://github.com/nedbat/byterun/blob/master/byterun/pyobj.py#L12
If you want an empty cell (which is what I found this question for) (One where referencing it raises a NameError: free variable '...' referenced before assignment in enclosing scope and accessing it's cell.cell_contents raises a ValueError: Cell is empty), you can make a value a local variable, but never let it be assigned:
def make_empty_cell():
if False:
# Any action that makes `value` local to `make_empty_cell`
del value
return (lambda: value).__closure__[0]
You can combine them like this:
_SENTINEL = object()
def make_cell(value=_SENTINEL):
if value is not _SENTINEL:
x = value
return (lambda: x).__closure__[0]
So calling with no arguments returns an empty cell, and with any value, a cell with that value.
If you don't care about empty cells, you can do:
def make_cell(value):
return (lambda: value).__closure__[0]
Note that it is func_closure in older Python 2, instead of __closure__.
I have just read an article: The 10 Most Common Mistakes That Python Developers Make. The problem #1 still puzzled me a lot. I will describe my problem with the codes below.
>>>def foo(bar=[]):
... bar.append("baz")
... return bar
>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
It not works, the answer in the article says list is an multable type and the default value changes during the call.But when I try this one
def foo(bar=[]):
if bar == []:bar=[]
...
it works, so what makes the difference?
Default arguments are created at function definition in Python. This means that when you provide a list as a default argument, that list will persist for all function calls in which that argument is not explicitly passed. In the second example, you are reassigning the argument to a different value. That creates a new list and will fix the problem. A more common solution to this "bug"/"feature" is to assign the default argument a value of None and then check it at the top of the function. That would look something like this:
def foo(bar=None):
if bar is None:
bar = []
However, the code that you wrote in the second example achieves the same effect.
In your code you assign a new list to bar instead using the default argument. Now if you modify the new list the changes of course aren't shown in the default argument which is different object.
The typical solution is to assign None to the default argument and explicitly check it at the beginning of function:
def foo(bar=None):
if bar is None:
bar = []
Note that although it looks a lot like your version there are differences:
def foo(bar=[]):
if bar == []:
bar = []
bar.append(1)
def foo2(bar=None):
if bar is None:
bar = []
bar.append(1)
l = []
foo(l)
print l # []
l = []
foo2(l)
print l # [1]
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)