python function: scope of function parameter initialized with default value [duplicate] - python

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
I have a python programming question, I am using a caching pattern to speed up computation, here is an example code:
def f(a=1, a_dict={}):
try:
b=a_dict['one']
print 'fetched from cache'
except:
b=10
a_dict['one']=10
print 'put in cache'
return b
Now if I call this function the first time the result is:
>>> f(1)
put in cache
10
I call again:
>>> f(1)
fetched from cache
10
This is nice a behaviour since it uses caching. However, I find it strange because the variable a_dict has been defined within the function, so once the function is over it should be out of scope... Note that this variable is not visible from outside the function:
>>> a_dict
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a_dict' is not defined
I would have guessed that when I call the function the second time, the default variable a_dict should have been initialized to {}, so I find it strange that when I call f() the second time the previous value of a_dict is somehow still in scope.
In summary, I would have expected to achieve the desired behaviour like this:
the_dict={}
f(1, the_dict)
# call second time
f(1, the_dict)
Because the object the_dict is passed by reference and hence stays in scope.
Can someone please explain the semantics of parameter initialization in a function and their scope?

Functions are objects in python, so default parameters can be thought of as 'members'. It is explained fully: http://effbot.org/zone/default-values.htm
To get the behaviour you expect, you can have the function defined as follows:
def f(a=1, a_dict=None):
if a_dict is None:
a_dict = {}
try:
b=a_dict['one']
print 'fetched from cache'
except:
b=10
a_dict['one']=10
print 'put in cache'
return b
This means that, rather than the {} being made when the function is defined (and therefore retained in it's object def), it's made when the function is run (and therefore is a local variable).

Related

Python: Update a variable inside the print function [duplicate]

This question already has answers here:
How to define a variable inside the print function?
(2 answers)
Closed 3 years ago.
a=1
print(a=2)
I am new in python. I tried to change the value for the variable a inside the print function. However, it's showing a syntax error. This concept will work in other languages. Could you please explain why it's showing a syntax error?
print is in-built function which takes positional and some keyword args.
Here you are calling print function with keyword args a with value 2, but there is no keyword args named a in print function.
You can't update the value inside a function call
Actually,what you would encounter is a TypeError,
>>> a=1
>>> print(a=2)
Traceback (most recent call last):
File "<pyshell#44>", line 1, in <module>
print(a=2)
TypeError: 'a' is an invalid keyword argument for print()
From the python documentation, you can find that if there was an = sign, the print() function would take it as an keyword argument unless it is given as a string.
So what actually happened here is that python tries to identify a as an argument, which obviously is not defined in the print() function. Therefore, it finds it as an invalid argument.
If you need to update the value of a, it is advisable to do it before the print() function and then print it.
Considering you are using python< v3.8, in my opinion, the closest you can get (pun intended) is:
>>> a = 1
>>> print(dict().get(exec('a=2'),a))
2
>>> a
2
Or,
>>> print({exec('a=2'):a}[None])
2
>>> a
2
Or,
>>> exec('a=2;print(a)')
2
I think this is the closest you can get:
a = 1
print('{} {}'.format(exec('a=2'), a))
Edit: I thought wrong. #Sayandip Dutta fixed my problem.

I'm trying to understand Python's exec() function

So I have a string which is a function like
code = """def somefn(x):
return x + x
y = somefn(z)"""
and I am trying to run this in another function like
def otherfn(codestr):
z = 2
exec(codestr)
return y
otherfn(code)
but it gives me the error:
Traceback (most recent call last):
File "C:/Users/Admin/Desktop/heh.py", line 11, in
otherfn(code)
File "C:/Users/Admin/Desktop/heh.py", line 9, in otherfn
return y
NameError: name 'y' is not defined
it works fine outside the function like
z=2
exec(codestr)
print(y)
it finds y just fine but not sure why it is bugging out when it is in the function.
How can I fix this? Is it something to do with globals() and locals()? Using Python 3.6 btw.
There are several problems with your code. First of all, you have an indentation problem - the y gets 'defined' inside the somefn() function, after the return so it never actually gets the chance to get onto the stack. You need to redefine your code to :
code = """def somefn(x):
return x + x
y = somefn(z)"""
But that's just the tip of the iceberg. The greater issue is that exec() cannot modify the local scope of a function. This is due the fact that Python doesn't use a dict for lookup of variables in the local scope so all changes from the exec() do not get reflected back to the stack to enable lookup. This causes a weird issue where exec() seemingly changes the locals() dictionary, but Python still throws a NameError:
def otherfn(codestr):
z = 2
exec(codestr)
print(locals()["y"]) # prints 4
return y # NameError
otherfn(code)
This is an intended behavior, as explained in the issue4831, and further pontificated in the official docs:
Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.
But if you have to reflect the changes you can just do a post-exec local scope update:
def otherfn(codestr):
z = 2
locals_ = locals()
exec(codestr, globals(), locals_)
y = locals_["y"]
return y
otherfn(code)

What is the difference between calling function with parentheses and without in python? [duplicate]

This question already has answers here:
What does it mean when the parentheses are omitted from a function or method call?
(6 answers)
Closed 2 years ago.
I have a question. Lets assume that we have function hello(). What is the difference between calling it with parentheses and without? When I call hello() it is referring to an object with value equals this function. Or maybe am I wrong? What happens when I call it without parentheses?
I wonder why
def hello():
pass
print(id(hello))
print(id(hello()))
returns different results
4400606744
4398942536
Short answer: see https://nedbatchelder.com/text/names.html to get a better understanding of the difference between objects and the names used to refer to objects.
A function is called if and only if you use parentheses. hello() calls the function; hello is simply a name bound to the function, and can be used, for example, to pass the function object as an argument to another function.
def caller(f):
f()
def hello():
print("hi")
def goodbye():
print("bye")
caller(hello) # Prints "hi"
caller(goodbye) # Prints "bye"
Regarding your update, id returns different values because each call to id receives a completely separate object as its argument. With id(hello), id gets the function object itself. With id(hello()), id is getting the object returned by the call to hello; it's the same as
x = hello()
print(id(x))
TL;DR: the parentheses execute the function itself; without them you treat the function as it were a variable
In python you append the parentheses (with optional variables inside) to the function name only when you want to invoke it.
But there is also another way to use function names.
In fact, in python functions are first class objects, that means that the function can be passed around as if it where a variable name. To do you must not append the parentheses () at the end of the function name.
Check the following:
def f():
return 'f'
print( type(f) ) # result: <class 'function'>
print( type(f()) ) # result: <class 'str'>
As you can see, in the first instance we print the type of f (without parentheses), which is a function
In the second instance we print the type of f() (with the parentheses), which is a string, that is the type returned by f when it is invoked.
This behavior is useful to pass functions as parameters of other functions, etc, etc.
The fact that id() and id(function-invoked>) returns different ids:
print(id(hello)) # here it returns the id of the function name
print(id(hello())) # here it returns the id
# of the object returned by the function itself
In short, function with parenthesis () is actually calling (invoke) that function whereas function without parenthesis () is an object of that function which can be passed as an argument and later be called by appending () to it.
def hello():
return 1
i.e.
hello >> returns object
hello() >> outputs 1
hello refers to the function object, hello() calls the function itself.
For example:
>>> def hello():
... print "hello"
...
>>> hello
<function hello at 0x106c91398>
>>> hello()
hello
A function is an object just like any other object in Python. If you define a new function, a new function object is created and is assigned to the name of the function.
def spam():
print('The knights who say Ni!')
print(spam) # => <function spam at 0x7fc64efc6f28>
spam() # => output: 'The knights who say Ni!
As you can see after the function was defined it got assigned to the name of spam. It is essentially similar to an assignment statement being run. Now that name is going to give you access to the function but in order to actually use it you have to call it using the parentheses like spam(). If you don't call it using the parentheses, it will just return the function object that has been assigned to the name spam.

How can I change a global variable from within a function? [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 7 months ago.
I'm trying to add or subtract from a defined variable, but how can I overwrite the old value with the new one?
a = 15
def test():
a = a + 10
print(a)
test()
Error message:
Traceback (most recent call last):
File "test.py", line 7, in <module>
test()
File "test.py", line 4, in test
a = a +10
UnboundLocalError: local variable 'a' referenced before assignment
The error that you get when you try to run your code is:
UnboundLocalError: local variable 'a' referenced before assignment
… which, on the face of it, seems strange: after all, the first statement in the code above (a = 15) is an assignment. So, what's going on?
Actually, there are two distinct things happening, and neither of them are obvious unless you already know about them.
First of all, you actually have two different variables:
The a in your first line is a global variable (so called because it exists in the global scope, outside of any function definitions).
The a in the other lines is a local variable, meaning that it only exists inside your test() function.
These two variables are completely unrelated to each other, even though they have the same name.
A variable is local to a function if there's a statement assigning to it inside that function - for instance, your a = a +10 line.
Even so, the error still looks strange - after all, the very first thing you do inside test() is assign to a, so how can it be referenced beforehand?
The answer is that, in an assignment statement, Python evaluates everything on the right hand side of the = sign before assigning it to the name on the left hand side – so even though the assignment is written first in your code, a gets referenced first in that right hand side: a +10.
There are two ways you can get around this. The first is to tell Python that you really want the a inside test() to be the same a in the global scope:
def test():
global a
a = a + 10
print(a)
This will work, but it's a pretty bad way to write programs. Altering global variables inside functions gets hard to manage really quickly, because you usually have lots of functions, and none of them can ever be sure that another one isn't messing with the global variable in some way they aren't expecting.
A better way is to pass variables as arguments to functions, like this:
a = 15
def test(x):
x = x + 10
print(x)
test(a)
Notice that the name doesn't have to be the same - your new definition of test() just says that it accepts a value, and then does something with it. You can pass in anything you like – it could be a, or the number 7, or something else. In fact, your code will always be easier to understand if you try to avoid having variables with the same name in different scopes.
If you play with the code above, you'll notice something interesting:
>>> a = 15
>>> test(a)
25
>>> a
15
… a didn't change! That's because although you passed it into test() and it got assigned to x, it was then x that got changed, leaving the original a alone.
If you want to actually change a, you need to return your modified x from the function, and then reassign it back to a on the outside:
>>> a = 15
>>>
>>> def test(x):
... x = x + 10
... print(x)
... return x
...
>>> a = test(a)
25
>>> a
25
You're modifying a variable a created in the scope of the function test(). If you want the outer a to be modified, you could do:
a = 15
def test():
global a
a = a + 1
print(a)
test()
I would do it this way:
def test(a):
a = a +10
return a
print(test(15))
Note that in the version hereby proposed there are some things differing from yours.
First, what I wrote down would create a function that has, as an input, the value a (in this case set to 15 when we call the function -already defined in the last line-), then assigns to the object a the value a (which was 15) plus 10, then returns a (which has been modified and now is 25) and, finally, prints a out thanks to the last line of code:
print(test(15))
Note that what you did wasn't actually a pure function, so to speak. Usually we want functions to get an input value (or several) and return an input value (or several). In your case you had an input value that was actually empty and no output value (as you didn't use return). Besides, you tried to write this input a outside the function (which, when you called it by saying test(a) the value a was not loaded an gave you the error -i.e. in the eyes of the computer it was "empty").
Furthermore, I would encourage you to get used to writing return within the function and then using a print when you call it (just like I wrote in the last coding line: print(test(15))) instead of using it inside the function. It is better to use print only when you call the function and want to see what the function is actually doing.
At least, this is the way they showed me in basic programming lessons. This can be justified as follows: if you are using the return within the function, the function will give you a value that can be later used in other functions (i.e. the function returns something you can work with). Otherwise, you would only get a number displayed on screen with a print, but the computer could not further work with it.
P.S. You could do the same doing this:
def test(a):
a +=10
return a
print(test(15))
a = 15
def test():
global a
a = a + 10
print(a)
test()
If you want to change the values of local variables inside a function you need to use the global keyword.
The scope of the variable is local to the block unless defined explicitly using keyword global. There is another way to access the global variable local to a function using the globals function:
a = 15
def test():
a = globals()['a']
a += 10
print(a)
test()
The above example will print 25 while keeping the global value intact, i.e., 15.
The issue here is scope.
The error basically means you're using the variable a, but it doesn't exist yet. Why? The variable a has not been declared in the test() function. You need to "invite" global variables before using them.
There are several ways to include a variable in the scope of a function. One way is to use it as a parameter like this:
def test(number):
number = number + 10
print(number)
Then implement it as:
test(a)
Alternatively, you could also use the global keyword to show the function that a is a global variable. How? Like this:
def test():
global a
a = a + 10
print(a)
Using the global keyword just tells the test where to look for a.
With the first method, you can't modify the global variable a, because you made a copy of it and you're using the copy. In the second method, though, any changes you make in the function test() to a, will affect the global variable a.
# All the mumbo jumbo aside, here is an experiment
# to illustrate why this is something useful
# in the language of Python:
a = 15 # This could be read-only, should check docs
def test_1():
b = a + 10 # It is perfectly ok to use 'a'
print(b)
def test_2():
a = a + 10 # Refrain from attempting to change 'a'
print(a)
test_1() # No error
test_2() # UnboundLocalError: ...
Your error has nothing to do with it being already defined…
A variable is only valid inside its so-called scope: If you create a variable in a function it is only defined in this function.
def test():
x = 17
print(x) # Returns 17
test()
print(x) # Results in an error.

Default values [anomaly] for function parameter

I came across something I don't quite understand with how the parameters in the def function works:
ie:
def test(a=1, b=9, c='what'):
return a
test(a,b,c)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-295-937fe43dbbd5> in <module>()
2 return a
3
----> 4 test(a,b,c)
NameError: name 'a' is not defined
and
def test(a=1, b=9, c='what'):
return a
test('what')
Output: 'what'
Well, I am looking to set the parameters a,b as default values. Now, for the 1st example, if I call test(a,b,c), it says a is not defined. However, I have already "defined" it in the function parameter? That means, if let's say a, b and c are default parameters, I cannot execute test(a,b,c)? Only test() works? WHAT!
In example 2, I don't even know anymore...
The problem is that you are trying to call your method test with three variables which do not exist in the global scope where you are running the method.
The way you are defining your method:
def test(a=1, b=1, c=1)
does not imply that the arguments a, b, c are available to be used globally the way you are trying to use them.
By defining your method as a=1, b=1, c=1, you are setting default values to your arguments. What this means, is that you can call your method with or without passing arguments to your method.
So, for your first example, you can call it like this:
test(a="what")
Which indicates that now you are assigning "what" to a and will no longer hold its default value of "1".
Alternatively, you can simply call it without any arguments, which in turn would assign default values to your local arguments inside your method. So, if you actually call your method like this:
test()
Then, the default value of "1" will now be assigned to "a".
In your second example, you are passing a string, and by default, your parameters get assigned from left to right of your function definition. So, when you do this:
test("what")
You are assigning "what" to a.

Categories

Resources