# Is this meant to be an expected behavior?

**URL:** <https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679>\
**Category:** Python Help\
**Created:** [January 20, 2024, 11:53am UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679 "2024-01-20T11:53:57Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![bwc-x](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bwc-x/32/17320_2.png) [@bwc-x](https://discuss.python.org/u/bwc-x)\
**Post date:** [January 20, 2024, 11:53am UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/1 "2024-01-20T11:53:57Z")

</div>

Suppose one wishes to instantiate a class:

> **Summary**
>
> ```python
> def __init__ (self,
> num: int = 5,
> text: str = 'txt',
> listed: list = [],
> dicted: dict = {}
> ):
> self.num = num
> self.text = text
> self.listed = listed
> self.dicted = dicted
> # for debugging purposes
> def print(self, last=None):
> print(self.num, self.text, 
> self.listed, self.dicted, 
> f'# {last}' if last else '')
> 
> ```

It may be important to have an option to provide both the `list` and the `dict` arguments manually - e.g. in the case one needs to create an instance dynamically. Such as:

> **Summary**
>
> ```python
> cls = Test 
> args = { 
> 'num': 5, 
> 'text': 'txt', 
> 'list': [], 
> 'dict': {} 
> }
> instance = cls(**args)
> 
> ```

Creating new instances of the class manually, however, introduces a rather interesting surprise to the behaviour of all those instances, at once. Specifically, the following code:

> **Summary**
>
> ```python
> t1 = Test()
> t2 = Test()
> 
> t1.print('t1: start')
> t2.print('t2: start\n')
> 
> t1.num = 3
> t2.text = 'next'
> 
> t1.print('t1: num_text')
> t2.print('t2: num_text\n')
> 
> t1.list.append('app')
> t2.dict['key'] = 'val'
> 
> t1.print('t1: list_dict')
> t2.print('t2: list_dict')
> 
> ```

Will print the following:

> **Summary**
>
> ```python
> 5 txt [] {} # t1: start
> 5 txt [] {} # t2: start
> 
> 3 txt [] {} # t1: num_text
> 5 next [] {} # t2: num_text
> 
> 3 txt ['app'] {'key': 'val'} # t1: list_dict
> 5 next ['app'] {'key': 'val'} # t2: list_dict
> 
> ```

If this is not the prime example of the most unintuitive feature a class / function declaration may have, I do not dare to imagine what that may even be. Certainly, it is possible to avoid such a problem via:

> **Summary**
>
> ```python
> def __init__ (self,
> num: int = 5,
> text: str = 'txt',
> listed = None, 
> dicted = None
> ):
> self.num = num
> self.text = text
> self.list = listed or []
> self.dict = dicted or []
> 
> ```

Yet is that not precisely what the author of the class would expect to happen in the first declaration, as well? Under which circumstances could one intend to link all the instances of a particular class to one single `list` / `dict` reference, declared in the instantiating ` __init__ ` in a manner identical to the passed/copied by value, not by reference, `int` / `str`? Was there any discussion about it? Is this simply “the way it’s come to be”?

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [January 20, 2024, 12:05pm UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/2 "2024-01-20T12:05:59Z")

</div>

Yes, that’s expected.

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [January 20, 2024, 12:07pm UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/3 "2024-01-20T12:07:36Z")

</div>

Welcome to the forum!

This is intended behaviour, the default arguments to a function or class are evaluated at function creation time, which means they will be shared by all calls.

As a side note, putting your code inside of detail elements makes it harder to read your post. I suggest you make them visible by default.

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [January 20, 2024, 12:32pm UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/4 "2024-01-20T12:32:43Z")

</div>

Without discussing why things are, I suggest you run a linter like Ruff over your code.

---

<div class="post-metadata">

**Author:** ![kknechtel](https://avatars.discourse-cdn.com/v4/letter/k/e47c2d/32.png) [@kknechtel](https://discuss.python.org/u/kknechtel)\
**Post date:** [January 20, 2024, 2:15pm UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/5 "2024-01-20T14:15:49Z")

</div>

Yes, this is one of the best known quirks of the language:

> <https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument>

It has nothing to do with classes, but with _functions_ (including methods). It has always worked this way; there has been an **incredible** amount of discussion of it; and the Ideas category contains many proposals for changing how it works.

It also isn’t clear to me what you mean by “creating new instances of the class manually” (_as opposed to what_?); and the observed behaviour is a result of **not** passing an explicit list or dict to the `Test` constructor. It’s also important to understand (this is thoroughly addressed in the linked explanation, but it’s a key conceptual issue) that doing something like `t1.listed.append('app')` is **fundamentally different from** doing something like `t1.num = 3`. In the first case, the object that `t1.listed` refers to is _modified_; in the second, the object that `t1.num` refers to is _replaced_ (there is no way from within Python to modify the integer object itself; it would be very bad if the _concept of `3` itself_ could be forced to change).

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [January 21, 2024, 12:12am UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/6 "2024-01-21T00:12:47Z")

</div>

4 posts were split to a new topic: [Mutating immutable objects with ctypes](https://discuss.python.org/t/mutating-immutable-objects-with-ctypes/43724)

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [January 20, 2024, 11:23pm UTC](https://discuss.python.org/t/is-this-meant-to-be-an-expected-behavior/43679/10 "2024-01-20T23:23:35Z")

</div>

> [@bwc-x](#):
>
> the passed/copied by value, not by reference, `int` / `str`

They’re “passed/copied by reference” as well, exactly the same way.
