0

Is there a cool pythonic way to use a class field (class variable) as a mutable default value for a keyword argument in __init__?
Here is the example:

class Foo():

    field = 5

    def __init__(self, arg=field): # arg=Foo.field => name 'Foo' is not defined
        self.arg = arg

# ok, let's start
obj_1 = Foo()
print(obj_1.arg) # 5, cool

# then I want to change Foo.field
Foo.field = 10
print(Foo.field) # 10, obviously
obj_2 = Foo()
print(obj_2.arg) # still 5, that's sad :(

Why is this happening?

I know I can do smth like that:

class Qux():

    field = 5

    def __init__(self, arg='default'): 
        self.arg = {True:Qux.field, False:arg}[arg == 'default']


Qux.field= 10
obj_3 = Qux()
print(obj_3.arg) # 10

But is there a simpler way?
Thanks in advance.

  • Wait, this isn't a duplicate of either of those questions... – Jared Goguen Aug 19 '17 at 22:43
  • This is not, but the information from those questions is helpful. Actually, [Fred Foo's answer] (https://stackoverflow.com/a/5555470/8488611) explains everything - "default arguments are evaluated at function definition time, not at call time". So, default value for a keyword argument, including inside a class, always points to the same object, in my case - immutable 5. If I want to mutate a default value, I should use a mutable type, like `field = [5]` `self.arg = arg[0]` and then `Foo.field[0] = 10`. It will work fine, but it's even more ugly than my Qux's example :) – KalyanKruno Aug 20 '17 at 01:01
  • You could also use a class property or a custom mutable integer object. – Jared Goguen Aug 20 '17 at 13:20
  • If the value is intended to be constant, there is not a lot to be gained by scoping it to the class. The underlying problem is that the class doesn't exist yet, and `Foo` hasn't become a name for it, at the time that the method is being defined; therefore, `Foo.field` can't be evaluated when it needs to be (ahead of time). If the code says `field` instead, it will only work if a **global** `field` exists, and it will use the value **at class creation time**. Essentially, two different problems intersect here; I have tried to add duplicates for each. – Karl Knechtel Mar 27 '23 at 08:11

1 Answers1

0

Just set the argument to None. Then test if the argument is None in __init__. If so, set it to it's default value:

class Foo():
    field = 5
    def __init__(self, arg=None):
        if arg is None:
            self.arg = Foo.field
        else:
            self.arg = arg

The above logic could also be condensed into:

self.arg = Foo.field if arg is None else arg
Christian Dean
  • 22,138
  • 7
  • 54
  • 87