Python Class scope & lists
I'm still fairly new to Python, and my OO experience comes from Java. So I have some code I've written in Python that's acting very unusual to me, given the following code:
开发者_Go百科class MyClass():
mylist = []
mynum = 0
def __init__(self):
# populate list with some value.
self.mylist.append("Hey!")
# increment mynum.
self.mynum += 1
a = MyClass()
print a.mylist
print a.mynum
b = MyClass()
print b.mylist
print b.mynum
Running this results in the following output:
['Hey!']
1
['Hey!', 'Hey!']
1
Clearly, I would expect the class variables to result in the same exact data, and the same exact output... What I can't seem to find anywhere is what makes a list different than say a string or number, why is the list referencing the same list from the first instantiation in subsequent ones? Clearly I'm probably misunderstanding some kind of scope mechanics or list creation mechanics..
tlayton's answer is part of the story, but it doesn't explain everything.
Add a
print MyClass.mynum
to become even more confused :). It will print '0'. Why? Because the line
self.mynum += 1
creates an instance variable and subsequently increases it. It doesn't increase the class variable.
The story of the mylist is different.
self.mylist.append("Hey!")
will not create a list. It expects a variable with an 'append' function to exist. Since the instance doesn't have such a variable, it ends up referring the one from the class, which does exist, since you initialized it. Just like in Java, an instance can 'implicitly' reference a class variable. A warning like 'Class fields should be referenced by the class, not by an instance' (or something like that; it's been a while since I saw it in Java) would be in order. Add a line
print MyClass.mylist
to verify this answer :).
In short: you are initializing class variables and updating instance variables. Instances can reference class variables, but some 'update' statements will automagically create the instance variables for you.
I believe the difference is that +=
is an assignment (just the same as =
and +
), while append
changes an object in-place.
mylist = []
mynum = 0
This assigns some class variables, once, at class definition time.
self.mylist.append("Hey!")
This changes the value MyClass.mylist
by appending a string.
self.mynum += 1
This is the same as self.mynum = self.mynum + 1
, i.e., it assigns self.mynum
(instance member). Reading from self.mynum
falls through to the class member since at that time there is no instance member by that name.
What you are doing here is not just creating a class variable. In Python, variables defined in the class body result in both a class variable ("MyClass.mylist") and in an instance variable ("a.mylist"). These are separate variables, not just different names for a single variable.
However, when a variable is initialized in this way, the initial value is only evaluated once and passed around to each instance's variables. This means that, in your code, the mylist variable of each instance of MyClass are referring to a single list object.
The difference between a list and a number in this case is that, like in Java, primitive values such as numbers are copied when passed from one variable to another. This results in the behavior you see; even though the variable initialization is only evaluated once, the 0 is copied when it is passed to each instance's variable. As an object, though, the list does no such thing, so your append() calls are all coming from the same list. Try this instead:
class MyClass():
def __init__(self):
self.mylist = ["Hey"]
self.mynum = 1
This will cause the value to be evaluated separately each time an instance is created. Very much unlike Java, you don't need the class-body declarations to accompany this snippet; the assignments in the __init__() serve as all the declaration that is needed.
精彩评论