Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
Terminology "Mutable default argument" means exactly what the individual words would suggest: it's an argument which is supplied as the default value for that parameter, which also is mutable. To ...
Answer
#2: Post edited
- ## Terminology
- "Mutable default argument" means exactly what the individual words would suggest: it's an argument which is supplied as the default value for that parameter, which also is *mutable*. To *mutate* an object means to change its state - for a list, that includes adding, removing, replacing or mutating elements of the list. A *mutable* object is one that provides *any way* to mutate it - so, lists are mutable, because there is an interface to add and remove elements, etc.
- On the other hand, a tuple is usually not thought of as "mutable" - even one that contains a list object. If such a tuple-containing-a-list were used as a default argument, it could cause the same problem. For our purposes, that also counts as "mutable".
- However, it's important to understand that the problem *does not occur simply because the object is mutable*; it occurs **because the function's code actually mutates** that object.
- ## Implementation and Rationale
- Exactly as the results would suggest, the **same list object** is used every time that `example` is called without an explicit argument. What happens is that *when the function is created*, an empty list is created - just that one time - and set up as the default value to use whenever an argument isn't passed explicitly.
- So, the first time that the code calls `example()`, the previously-made empty list gets an element appended. It's still the same object, but its contents have changed. So the next time that the code calls `example()`, that list - which still contains the element from last time - is used again, and another element is appended.
- Essentially, `[]` is *code that runs ahead of time* in order to create a default argument object.
- <details><summary>We can verify this by using another function to create the default argument:</summary>
- ```
- >>> def setup():
- ... print('calling setup')
- ... return []
- ...
- >>> def example(param=setup()):
- ... param.append('test')
- ... print('The list is now:', param)
- ...
- calling setup
- ```
- The `setup` function runs **while creating** the `example` function. A key observation here is that Python's `def` statement *is a code statement*, not just a definition used by a compiler: the code *runs when it is encountered*, and running that code involves calling the `setup` function. After this, repeatedly calling `example()` will build up the list, and won't call the `setup` function again:
- ```
- >>> example()
- The list is now: ['test']
- >>> example()
- The list is now: ['test', 'test']
- >>> example()
- The list is now: ['test', 'test', 'test']
- ```
- </details>
This actually more or less explains *why* it works that way: since Python's functions are first-class objects, created "on the fly" by the `def` statement, it makes sense that the code for the default values is evaluated right away. There isn't anything in the syntax that would suggest that the code execution should be deferred.
- ## Terminology
- "Mutable default argument" means exactly what the individual words would suggest: it's an argument which is supplied as the default value for that parameter, which also is *mutable*. To *mutate* an object means to change its state - for a list, that includes adding, removing, replacing or mutating elements of the list. A *mutable* object is one that provides *any way* to mutate it - so, lists are mutable, because there is an interface to add and remove elements, etc.
- On the other hand, a tuple is usually not thought of as "mutable" - even one that contains a list object. If such a tuple-containing-a-list were used as a default argument, it could cause the same problem. For our purposes, that also counts as "mutable".
- However, it's important to understand that the problem *does not occur simply because the object is mutable*; it occurs **because the function's code actually mutates** that object.
- ## Implementation and Rationale
- Exactly as the results would suggest, the **same list object** is used every time that `example` is called without an explicit argument. What happens is that *when the function is created*, an empty list is created - just that one time - and set up as the default value to use whenever an argument isn't passed explicitly.
- So, the first time that the code calls `example()`, the previously-made empty list gets an element appended. It's still the same object, but its contents have changed. So the next time that the code calls `example()`, that list - which still contains the element from last time - is used again, and another element is appended.
- Essentially, `[]` is *code that runs ahead of time* in order to create a default argument object.
- <details><summary>We can verify this by using another function to create the default argument:</summary>
- ```
- >>> def setup():
- ... print('calling setup')
- ... return []
- ...
- >>> def example(param=setup()):
- ... param.append('test')
- ... print('The list is now:', param)
- ...
- calling setup
- ```
- The `setup` function runs **while creating** the `example` function. A key observation here is that Python's `def` statement *is a code statement*, not just a definition used by a compiler: the code *runs when it is encountered*, and running that code involves calling the `setup` function. After this, repeatedly calling `example()` will build up the list, and won't call the `setup` function again:
- ```
- >>> example()
- The list is now: ['test']
- >>> example()
- The list is now: ['test', 'test']
- >>> example()
- The list is now: ['test', 'test', 'test']
- ```
- </details>
- This actually more or less explains *why* it works that way: since Python's functions are first-class objects, created "on the fly" by the `def` statement, it makes sense that the code for the default values is evaluated right away. There isn't anything in the syntax that would suggest that the code execution should be deferred.
- One practical consequence of this behaviour is that a default value can be determined by a config file, and the file won't need to be read and parsed again very time the function is called.
#1: Initial revision
## Terminology "Mutable default argument" means exactly what the individual words would suggest: it's an argument which is supplied as the default value for that parameter, which also is *mutable*. To *mutate* an object means to change its state - for a list, that includes adding, removing, replacing or mutating elements of the list. A *mutable* object is one that provides *any way* to mutate it - so, lists are mutable, because there is an interface to add and remove elements, etc. On the other hand, a tuple is usually not thought of as "mutable" - even one that contains a list object. If such a tuple-containing-a-list were used as a default argument, it could cause the same problem. For our purposes, that also counts as "mutable". However, it's important to understand that the problem *does not occur simply because the object is mutable*; it occurs **because the function's code actually mutates** that object. ## Implementation and Rationale Exactly as the results would suggest, the **same list object** is used every time that `example` is called without an explicit argument. What happens is that *when the function is created*, an empty list is created - just that one time - and set up as the default value to use whenever an argument isn't passed explicitly. So, the first time that the code calls `example()`, the previously-made empty list gets an element appended. It's still the same object, but its contents have changed. So the next time that the code calls `example()`, that list - which still contains the element from last time - is used again, and another element is appended. Essentially, `[]` is *code that runs ahead of time* in order to create a default argument object. <details><summary>We can verify this by using another function to create the default argument:</summary> ``` >>> def setup(): ... print('calling setup') ... return [] ... >>> def example(param=setup()): ... param.append('test') ... print('The list is now:', param) ... calling setup ``` The `setup` function runs **while creating** the `example` function. A key observation here is that Python's `def` statement *is a code statement*, not just a definition used by a compiler: the code *runs when it is encountered*, and running that code involves calling the `setup` function. After this, repeatedly calling `example()` will build up the list, and won't call the `setup` function again: ``` >>> example() The list is now: ['test'] >>> example() The list is now: ['test', 'test'] >>> example() The list is now: ['test', 'test', 'test'] ``` </details> This actually more or less explains *why* it works that way: since Python's functions are first-class objects, created "on the fly" by the `def` statement, it makes sense that the code for the default values is evaluated right away. There isn't anything in the syntax that would suggest that the code execution should be deferred.