0

Object list is global if append, but not if list=value

I've created a class, that takes in a list, and uses it for math functions. Anyways, I noticed a weird quirk with the system: if I append the list of element x, all objects in the class have access to it, but if I set the list=x, than the list is local. X is also a list.

class Wrong:
    list=[]

    def __init__(self, foo):
        for i in foo:
            self.list.append(i)

class Right:
    list=[]

    def __init__(self, foo):
        self.list=foo

list=[1,2,3]
wrong1 = Wrong(list)
wrong2 = Wrong(list)
right1 = Right(list)
right2 = Right(list)

print(wrong1.list)
print(wrong2.list)
print(right1.list)
print(right2.list)

I expect the outputs to be the same, but when printed, it's

[1, 2, 3, 1, 2, 3]
[1, 2, 3, 1, 2, 3]
[1, 2, 3]
[1, 2, 3]

I'm using python 3.7.2, in case that changes anything.

Zaki
  • 3
  • 1
  • 2
    for starters, don't name your `list` a `list` – gold_cy Feb 04 '19 at 17:05
  • 1
    You should read https://nedbatchelder.com/text/names.html, as well as learn the difference between class and instance attributes. This is also related to https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument. – chepner Feb 04 '19 at 17:10
  • [stop writing python classes when they are not necessary](https://www.youtube.com/watch?v=o9pEzgHorH0) – eagle33322 Feb 04 '19 at 17:24

2 Answers2

2

You're misunderstanding what class attributes and instance attributes are. In the first example you've declared Wrong.list as a class attribute that will be shared across all instances of Wrong. In the second example, you've created an instance of Right and then replaced its reference to the class attribute Right.list with a new reference to an instance attribute named self.list. The lookup hierarchy goes something like this when accessing an attribute:

  1. Request for attribute <name>.
  2. Check if an instance attribute exists for <name>. If yes, return it.
  3. If no instance attribute found, check if a class attribute for <name> exists. If yes, return it.
  4. Return AttributeError because this instance does not have the named attribute.

It's not that it's doing anything incorrectly, it's that you're not fully following what it's doing for you.

g.d.d.c
  • 46,865
  • 9
  • 101
  • 111
  • Thanks man. I just always assume you decleare the instance varibles outside of init, and than you give them value inside init. – Zaki Feb 04 '19 at 20:57
0

You just need to define your list inside __init__ function:

class Wrong:

    def __init__(self, foo):
        self.y = []
        for i in foo:
            self.y.append(i)

class Right:

    def __init__(self, foo):
        self.z = foo

x = [1,2,3]
wrong1 = Wrong(x)
wrong2 = Wrong(x)
right1 = Right(x)
right2 = Right(x)

print(wrong1.y)
print(wrong2.y)
print(right1.z)
print(right2.z)
Lucas Hort
  • 812
  • 7
  • 9