1

How can I use keyword args in an initialiser without sharing it between instances of a class?

Example of the bad behaviour below, if I were to add anything to the set foo then it would be added in both instances.

In [1]: class Klass:
    def __init__(self, foo=set()):
        self.foo = foo
In [2]: a = Klass()
In [3]: b = Klass()
In [4]: a.foo is b.foo
Out[4]: True
BookOfGreg
  • 3,550
  • 2
  • 42
  • 56

1 Answers1

3

Note that this issue will only occur with mutable default arguments - see "Least Astonishment" and the Mutable Default Argument. To use a mutable default argument, it is conventional to set the default to None in the function definition, then check inside the function whether or not a value has been supplied:

class Klass:

    def __init__(self, foo=None):
        if foo is None:
            foo = set()
        self.foo = foo

or:

self.foo = foo if foo is not None else set()

Note that None is tested by identity (if foo is None:), not equality (if foo == None:) or truthiness (if not foo:). For example, the latter is an issue if you want to explicitly pass in an empty set you have referenced elsewere.

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Is there a stylistic reason to not inline the `foo = set()` with the `if`? – BookOfGreg Feb 23 '15 at 16:32
  • @BookOfGreg per [the style guide](https://www.python.org/dev/peps/pep-0008/#other-recommendations), *"Compound statements (multiple statements on the same line) are generally discouraged."*, although it subsequently adds *"...sometimes it's okay to put an if/for/while with a small body on the same line"* – jonrsharpe Feb 23 '15 at 16:34