Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

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

80%
+6 −0
Q&A Understanding mutable default arguments in Python

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 ...

posted 8mo ago by Karl Knechtel‭  ·  edited 8mo ago by Karl Knechtel‭

Answer
#2: Post edited by user avatar Karl Knechtel‭ · 2023-09-20T07:21:47Z (8 months ago)
  • ## 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 by user avatar Karl Knechtel‭ · 2023-09-20T04:04:27Z (8 months ago)
## 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.