0

I am trying to add to a python class an alternate constructor who initialise an object from a config file. I use the following code, who according to my understanding of @classmethod should do the job.

class Dummy:
    _list = []

    def __init__(self, list=None):
        if list is not None:
            self._list = list

    @classmethod
    def from_config(cls, config):
        temp = Dummy()
        for c in config:
            temp._list.append(c)

        return temp

    def __str__(self):
        return '\n'.join([str(c) for c in self._list])

t = Dummy.from_config([1, 2])
t2 = Dummy.from_config([3, 4])

print(t)
print("---")
print(t2)

This print:

1234
---
1234

I don't understand why ? I was expecting the first class get 12 and the second 34

Edited:

Based on reply received I edited the code like this to remove class variable and to always set instance variable (self._list) in constructor. This still show the same issue:

class Dummy:

    def __init__(self, list=[]):
        self._list = list

    @classmethod
    def from_config(cls, config):
        temp = Dummy()
        for c in config:
            temp._list.append(c)

        return temp

    def __str__(self):
        return '\n'.join([str(c) for c in self._list])

t = Dummy.from_config([1, 2])
t2 = Dummy.from_config([3, 4])

print(t)
print("---")
print(t2)

I also see that if from the from_config method I call:

temp = Dummy([])

instead of:

temp = Dummy()

it works as expected. I don't understand why as for me both variant do the same as default value for constructor parameter is [].

kpa
  • 1
  • 2
  • 1
    `def from_config` needs to be un-indented one level, assuming your example is the same as the code you're running. – Random Davis Jan 05 '21 at 21:30
  • @RandomDavis correct. I edited my post with the correct indentation. Thanks – kpa Jan 06 '21 at 13:47
  • I reply myself, I found the solution in this other post: https://stackoverflow.com/questions/4841782/python-constructor-and-default-value – kpa Jan 06 '21 at 14:16

2 Answers2

1

From official Python tutorial:

Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:


class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):
        self.name = name    # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # shared by all dogs
'canine'
>>> e.kind                  # shared by all dogs
'canine'
>>> d.name                  # unique to d
'Fido'
>>> e.name                  # unique to e
'Buddy'
alex_noname
  • 26,459
  • 5
  • 69
  • 86
0

When you call Dummy in your class method, no argument is passed, so no instance attribute name _list is created. Both t._list and t2._list refer to the same list as the class attribute Dummy._list.

It's not entirely clear how or if Dummy.from_config(x) should behave differently from Dummy(x). Without more information, I would just define the class method as

@classmethod
def from_config(cls, config):
    temp = Dummy([])
    for c in config:
        temp._list.append(c)

    return temp

The main difference between this implementation and Dummy([1,2]) is that the elements from the argument are copied into a fresh list, rather than simply saving a reference to the argument list itself.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • the from_config populate the list from a config file, line by line. I removed this parsing code here as it's not important for my question – kpa Jan 06 '21 at 14:04