Default naming based on number of existing objects Python [duplicate] - python

Python Tutorial 4.7.1. Default Argument Values states the following:
Important warning: The default value is evaluated only once. This makes a difference
when the default is a mutable object such as a list, dictionary, or instances of most
classes. For example, the following function accumulates the arguments passed to it on
subsequent calls:
def f(a, L=[]):
L.append(a)
return L
print f(1)
print f(2)
print f(3)
This will print
[1]
[1, 2]
[1, 2, 3]
I don't quite understand the meaning of "evaluated only once" in terms of memory management. Apparently, the default value of the function is evaluated once when the function is first called and stored in a separate memory address even after the function has ended. (according to my understanding, after the function ended, all local variables should be freed?)
Am I correct?

In Python, functions are objects too, and the defaults are stored with the function object. Defaults are not locals; it is just that when the function is called, the arguments are bound to a default when not given an explicit value.
When Python encounters a def <functionname>(<arguments>): statement, it creates a function object for you there and then; this is 'definition time'; the function is not called but merely created. It is then that defaults are evaluated and stored, in an attribute on the function object.
Then when you call the function, the defaults have already been created and are used when you didn't provide a more concrete value for the argument. Because the defaults are stored with the function object, you get to see changes to mutable objects between function calls.
The locals are still cleared up of course, but as they are references (all identifiers in Python are), the objects they were bound to are only cleared up if nothing else is referencing them anymore either.
You can take a look a the defaults of any function object:
>>> def foo(bar='spam', eggs=[]):
... eggs.append(bar)
... return eggs
...
>>> foo.__defaults__
('spam', [])
>>> foo()
['spam']
>>> foo.__defaults__
('spam', ['spam'])
>>> foo() is foo.__defaults__[1]
True
The foo() function has a __defaults__ attribute, a tuple of default values to use when no values for the arguments have been passed in. You can see the mutable list change as the function is called, and because the function returns the eggs list, you can also see that it is the exact same object as the second value in that tuple.
If you don't want your defaults to be shared and instead need a new value for a parameter every time the function is called, but the parameter is not given, you need to set the default value to a sentinel object. If your parameter is still set to that sentinel in the function body, you can execute code to set a fresh default value. None is usually the best choice:
def foo(bar='spam', eggs=None):
if eggs is None:
eggs = []
If it should be possible to use None as a non-default value, use a singleton sentinel created beforehand:
_sentinel = object()
def foo(bar='spam', eggs=_sentinel):
if eggs is _sentinel:
eggs = []

The function that you have defined f is an object in its own regard. When you define defaults, these defaults are bound to the function that you have created.
You can see this in action:
>>> def f(a, L=[]):
... L.append(a)
... return L
>>> print id(f)
4419902952
>>> print f.__defaults__
([],)
>>> f(1)
[1]
>>> print id(f)
4419902952
>>> print f.__defaults__
([1],)
edit, further, you can see that the list container does not change either:
>>> print id(f.__defaults__[0])
4419887544
>>> f(2)
[1, 2]
>>> print id(f.__defaults__[0])
4419887544
On each subsequent call, the default list ("L") of your f function will have your a value appended.

A function is just an object in python, that is created using the def syntax. Default values are stored within the function object when the function is defined, and they are not re-evaluated later.
This is sometimes used to create function variables that persist to subsequent invocations. You can use the __defaults__ methods to check what the default values are for your function.
A common way to initialize new objects instead of reusing the same is:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
You can check this page for more details.

Sorry this answer was meant for a different question, but I'll leave it here as a reference if anyone who wants to look at it. Define once means that at the first point when the code is executed, the default variable gets assigned to an object which is retained within the function object itself.
Notice only 1 object address gets printed, the default list object is used.
def f(a, L=[]):
print("id default: ", id(L))
L.append(a)
print("id used: ", id(L)
return L
Notice 2 different object addresses are printed, when you perform L=[] within the function, you are binding L to a different list object, therefore the default list object does not get change.
def f(a, L=[]):
print("id default: ", id(L))
if L == []:
L = []
L.append(a)
print("id used: ", id(L))
return L
The function above is basically the same as the one below except it uses the None object instead of a empty list object.
def f(a, L=None):
print("id default", id(L))
if L is None:
L = []
L.append(a)
print("id used: ", id(L))
return L

Related

Why can't I change the default value of a function after it was defined?

i = 5
def f(arg=i):
print(arg)
i = 6
f()
I am learning Python from the official documentation. There I find the above piece of code which I am unable to understand as to why 5 is printed instead of 6. I am relatively new to Python. Can somebody help me understand the concept?
def f(arg=i) says "make me a function f where the default value for arg is whatever i is right now". At the time of defining the function, i=5.
i = 5
def f(arg=i)
print(arg)
The i is evaluated at the time of definition, so the code above has the same meaning as the code below:
def f(arg=5)
print(arg)
This means that, when the function is called without arguments, arg will have the value 5, no matter what the value of i is now.
In order to get what you want, just do the following:
def f(arg)
print(arg)
i = 6
f(i)
Because the function takes its default value on the first declaration of 'i'.
Change to i=6 on the first line if you want you code to print 6.
Hope I helped !
This is the difference between something being handled by reference vs by value. When you defined the function f you told it to set the argument's default value to i this is done by value, not by reference, so it took whatever the value of i was at that time and set the default for the function to that. Changing the value of i after that point does not change the value of arg. If you want it to work that way you could do this:
i = 5
def f(arg = None):
if (arg = None)
arg = i
print(arg)
i = 6
f()
This lets you pass a value for arg into the function as normal, but if you don't (or you explicitly pass None) it updates arg to the current value of i if arg is still None (Python's version of NULL if you're familiar with other languages)
Something similar can be done using the or operator, arg = arg or i,but that will check if arg is falsy, and when using integers like you are in your example, 0 will be caught by the check.
What others have said is true...the default is evaluated at the time of function creation, but it is not that it takes the "value of i" at the time of creation. The default is assigned the object referred to by "i" at the time of creation. This is an important point, because if that object is mutable, the default can be changed!
Here's what happens:
import inspect
i = 5 # name "i" refers to an immutable Python integer object of value 5.
print(f'i = {i} (id={id(i)})') # Note value and ID
# Create function "f" with a parameter whose default is the object
# referred to by name "i" *at this point*.
def f(arg=i):
print(f'arg = {arg} (id={id(arg)})')
# Use the inspect module to extract the defaults from the function.
# Note the value and ID
defaults = dict(inspect.getmembers(f))['__defaults__']
print(f'defaults = {defaults} (id={id(defaults[0])})')
# name "i" now refers to a different immutable Python integer object of value 6.
i = 6
print(f'i = {i} (id={id(i)})') # Note value and ID (changed!)
f() # default for function still referes to object 5.
f(i) # override the default with object currently referred to by name "i"
Output:
i = 5 (id=2731452426672) # Original object
defaults = (5,) (id=2731452426672) # default refers to same object
i = 6 (id=2731452426704) # Different object
arg = 5 (id=2731452426672) # f() default is the original object
arg = 6 (id=2731452426704) # f(i) parameter is different object
Now see the results of a mutable default:
import inspect
i = [5] # name "i" refers to an mutable Python list containing immutable integer object 5
print(f'i = {i} (id={id(i)})') # Note value and ID
# Create function "f" with a parameter whose default is the object
# referred to by name "i" *at this point*.
def f(arg=i):
print(f'arg = {arg} (id={id(arg)})')
# Use the inspect module to extract the defaults from the function.
# Note the value and ID
defaults = dict(inspect.getmembers(f))['__defaults__']
print(f'defaults = {defaults} (id={id(defaults[0])})')
# name "i" now refers to a different immutable Python integer object of value 6.
i[0] = 6 # MUTATE the content of the object "i" refers to.
print(f'i = {i} (id={id(i)})') # Note value and ID (UNCHANGED!)
f() # default for function still refers to original list object, but content changed!
i = [7] # Create a different list object
print(f'i = {i} (id={id(i)})') # Note value and ID (changed)
f(i) # override the default currently refered to by name "i"
Output:
i = [5] (id=2206901216704) # Original object
defaults = ([5],) (id=2206901216704) # default refers to original object
i = [6] (id=2206901216704) # Still original object, but content changed!
arg = [6] (id=2206901216704) # f() default refers to orginal object, but content changed!
i = [7] (id=2206901199296) # Create a new list object
arg = [7] (id=2206901199296) # f(i) parameter refers to new passed object.
This can have strange side effects if not understood well:
>>> def f(a,b=[]): # mutable default
... b.append(a)
... return b
...
>>> x = f(1)
>>> x
[1]
>>> y = f(2) # some would think this would return [2]
>>> y
[1, 2]
>>> x # x changed from [1] to [1,2] as well!
[1, 2]
Above, b refers to the original default list object. Appending to it mutates the default list. Returning it makes x refer to the same object. The default list now contains [1] so appending in the 2nd call make it [1,2]. y refers to the same default object as x so both names refer see the same object content.
To fix, make the default immutable and create a new list when the default is seen:
>>> def f(a,b=None):
... if b is None:
... b = []
... b.append(a)
... return b
...
>>> f(1)
[1]
>>> f(2)
[2]
This is because you are assigning the value when the function is created. arg at the time of creation will be defaulted to what i is in that moment. Since at the time of the function being created the value of i is 5 then that's what the default value of that argument becomes. After the initial creation of the function i in the function argument is no longer linked to i in the body.

Python default arguments confusion

As I'm new to python I've started the topic of default arguments
As per that of the definition I've understood that the default arguments are evaluated only once and that at the point of function definition but this code fragment created the confusion
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
In the above code L being a variable
Modified to list on the first function call
ex.f(1)
But even for the second time when the function is called L is being modified to list
ex.. f(1)
f(2)
Results in [1]
[2]
Could you'll actually be precise in explaining how the above code evaluation is done
Everytime you call f without a 2nd parameter, a new list is created. If you want to reuse the list, you need to store the result of f
new_list = f(1)
f(2, new_list)
print(new_list)
Will output [1,2]
You can read this for better understanding of python arguments passing https://www.python-course.eu/passing_arguments.php
Long story short - you can't override value of argument, you can only create local variable L that points to new list, which will shadow argument L. But on next call of function argument L is still None, unless it will be passed

Understanding Python style, default parameters of functions [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 7 years ago.
This is covered by the Python Tutorial, but I still don't quite understand the reason why Python has this style. Is it purely convention, or is there some explanation behind why Python has the following style regarding default parameters:
My understanding is that Python prefers something=None as opposed to something=[] for default parameters for functions. But...why not use something=[]? Surely this is the convention in other languages, like C
As an example, take these two examples, which are equivalent
def function(arr, L=[]):
L.append(arr)
return L
and
def function(arr, L=None):
if L is None:
L = []
L.append(arr)
return L
My understanding is that the first is "incorrect style" for Python. Why?
EDIT: Ah, I finally understand. I am incorrect above: the two functions are NOT equivalent. Default arguments are evaluated once when the function is defined, not each time the function is called!
When you set a parameter to the value of a list, it is assigned when the function is defined, not when it is called. That is why you'll get different results for calling the function multiple times with the same input parameter. Beware!!!
def function(arr, L=[]):
L.append(arr)
return L
arr = [1, 2, 3]
>>> function(arr)
[[1, 2, 3]]
>>> function(arr)
[[1, 2, 3], [1, 2, 3]]
A default value that's an empty list will refer to one specific variable each time you call it, instead of creating a new empty list every time.
>>> def add(defList=[]):
defList.append(1)
return defList
>>> add()
[1]
>>> add()
[1,1]
It's a quirk of how mutable data works in Python. It could be occasionally useful, but often it's safer to use None. Then create an empty list if a value hasn't been passed.
The reason is that Python stores a value for L. In other words L is a reference to a constant. But that constant can be updated.
You can say that Python stores it as:
|--> []
|
function (arr, L)
But that [] is an ordinary object (thus with state), that can be modified as well. Now if the function can modify or return L, you start modifying the state of L. In the example:
def function(arr, L=[]):
L.append(arr)
return L
You modify L. If you call this the first time (for instance with function(123)), the object is updated so, now it is represented as:
|--> [123]
|
function (arr, L)
As a result the behavior of function depends on a global state. In general a global state is seen as a bad smell in code design and furthermore it is not what people might expect. This doesn't hold for None, since you modify the local reference L (not the object itself).
You can say that the object is the same, but each time you call the function, you copy a reference to a local variable L.
Now for the second case:
|--> None
|
def function(arr, L):
if L is None:
L = []
L.append(arr)
return L
If you call this method, you assign a value to L, but L itself is not global (only the object to which L refers). So if you call this method, after the if, the function (in progress) will look like.
Because the argument l is assigned an initial value of [] NOT when you call the function but when it's first declared and the module it's in is interpreted.
See: Python: Common Gotcha (Thanks #ferhat elmas)
Example: (showing what's going on)
$ python
Python 2.7.9 (default, Mar 19 2015, 22:32:11)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(L=[]):
... L.append(1)
... return id(L), L
...
>>> def g(L=None):
... L = [] if L is None else L
... L.append(1)
... return id(L), L
...
We'll ignore what happens when you pass in another list to the L argument because that behaviour is well defined and acceptable. (takes a list, appends to it and returns it).
>>> f()
(139978918901088, [1])
>>> g()
(139978918964112, [1])
The first time we call f() and g() we might mistakenly think that we have successfully returned a new list from an initial empty one right? What what happens when we call f() again compared with the correct g():
>>> f()
(139978918901088, [1, 1])
>>> g()
(139978918964112, [1])
So the initial value you assigned for the argument L is set once and only only when the function is defined; not when it's called. This is a common gotcha as explained in the link aove.
NB: id() here returns the unique identity of objects so you can clearly see why the first is an incorrect way to define a default value for a mutable object such as a list.
Also Note: In the above examples that the identity of L does not change when calling f().

Python function: Optional argument evaluated once?

Python Tutorial 4.7.1. Default Argument Values states the following:
Important warning: The default value is evaluated only once. This makes a difference
when the default is a mutable object such as a list, dictionary, or instances of most
classes. For example, the following function accumulates the arguments passed to it on
subsequent calls:
def f(a, L=[]):
L.append(a)
return L
print f(1)
print f(2)
print f(3)
This will print
[1]
[1, 2]
[1, 2, 3]
I don't quite understand the meaning of "evaluated only once" in terms of memory management. Apparently, the default value of the function is evaluated once when the function is first called and stored in a separate memory address even after the function has ended. (according to my understanding, after the function ended, all local variables should be freed?)
Am I correct?
In Python, functions are objects too, and the defaults are stored with the function object. Defaults are not locals; it is just that when the function is called, the arguments are bound to a default when not given an explicit value.
When Python encounters a def <functionname>(<arguments>): statement, it creates a function object for you there and then; this is 'definition time'; the function is not called but merely created. It is then that defaults are evaluated and stored, in an attribute on the function object.
Then when you call the function, the defaults have already been created and are used when you didn't provide a more concrete value for the argument. Because the defaults are stored with the function object, you get to see changes to mutable objects between function calls.
The locals are still cleared up of course, but as they are references (all identifiers in Python are), the objects they were bound to are only cleared up if nothing else is referencing them anymore either.
You can take a look a the defaults of any function object:
>>> def foo(bar='spam', eggs=[]):
... eggs.append(bar)
... return eggs
...
>>> foo.__defaults__
('spam', [])
>>> foo()
['spam']
>>> foo.__defaults__
('spam', ['spam'])
>>> foo() is foo.__defaults__[1]
True
The foo() function has a __defaults__ attribute, a tuple of default values to use when no values for the arguments have been passed in. You can see the mutable list change as the function is called, and because the function returns the eggs list, you can also see that it is the exact same object as the second value in that tuple.
If you don't want your defaults to be shared and instead need a new value for a parameter every time the function is called, but the parameter is not given, you need to set the default value to a sentinel object. If your parameter is still set to that sentinel in the function body, you can execute code to set a fresh default value. None is usually the best choice:
def foo(bar='spam', eggs=None):
if eggs is None:
eggs = []
If it should be possible to use None as a non-default value, use a singleton sentinel created beforehand:
_sentinel = object()
def foo(bar='spam', eggs=_sentinel):
if eggs is _sentinel:
eggs = []
The function that you have defined f is an object in its own regard. When you define defaults, these defaults are bound to the function that you have created.
You can see this in action:
>>> def f(a, L=[]):
... L.append(a)
... return L
>>> print id(f)
4419902952
>>> print f.__defaults__
([],)
>>> f(1)
[1]
>>> print id(f)
4419902952
>>> print f.__defaults__
([1],)
edit, further, you can see that the list container does not change either:
>>> print id(f.__defaults__[0])
4419887544
>>> f(2)
[1, 2]
>>> print id(f.__defaults__[0])
4419887544
On each subsequent call, the default list ("L") of your f function will have your a value appended.
A function is just an object in python, that is created using the def syntax. Default values are stored within the function object when the function is defined, and they are not re-evaluated later.
This is sometimes used to create function variables that persist to subsequent invocations. You can use the __defaults__ methods to check what the default values are for your function.
A common way to initialize new objects instead of reusing the same is:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
You can check this page for more details.
Sorry this answer was meant for a different question, but I'll leave it here as a reference if anyone who wants to look at it. Define once means that at the first point when the code is executed, the default variable gets assigned to an object which is retained within the function object itself.
Notice only 1 object address gets printed, the default list object is used.
def f(a, L=[]):
print("id default: ", id(L))
L.append(a)
print("id used: ", id(L)
return L
Notice 2 different object addresses are printed, when you perform L=[] within the function, you are binding L to a different list object, therefore the default list object does not get change.
def f(a, L=[]):
print("id default: ", id(L))
if L == []:
L = []
L.append(a)
print("id used: ", id(L))
return L
The function above is basically the same as the one below except it uses the None object instead of a empty list object.
def f(a, L=None):
print("id default", id(L))
if L is None:
L = []
L.append(a)
print("id used: ", id(L))
return L

mutable default value in python [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
class Foo:
def __init__(self, stuff = []):
print(stuff)
self.stuff = stuff
def add(self,x):
self.stuff.append(x)
>>>f = Foo()
[]
>>>f.add(1)
>>>g = Foo()
[1]
And I make only one change to the code(line 4)
class Foo:
def __init__(self, stuff = []):
print(stuff)
self.stuff = stuff or []
def add(self,x):
self.stuff.append(x)
>>>f = Foo()
[]
>>>f.add(1)
>>>g = Foo()
[]
I change something in the line 4 but resulting in the change in the printing outcome(which is in the line 3)
I just want to know how it works.
The list you pass as fallback parameter value is instantiated once and reused throughout every call to __init__. So adding a value into it will propagate to every next instance of Foo.
The difference between the first and second examples is stuff or []. You need to know that an empty list, when evaluated as a boolean value is False, so the stuff or [] expression returns the second operand if stuff is empty, which will always be the case in your example. The second operand is a list that gets instantiated in the method call, so is a different instance everytime __init__ is called. This ensures that the added value doesn't propagate.
In this line:
def __init__(self, stuff = []):
all instances created with Foo() will use the same list, created as the default value for all calls of __init__. That's why in the first example adding anything to f results in modifying the default list for all further instance of Foo. This is because the part stuff = [] is evaluated when the interpreter evaluates the def line. From the documentation:
Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.
(bold added by me).
In the second example, as the default is always empty the second part of the or clause is evaluated which creates a new, individual instance of a list each time a new instance of Foo is created. So all Foo instances will have their own lists.
Here you can find more explanations and examples.

Categories

Resources