Class properties and object properties

S

SuperZE

Learning python I was rewriting some of my old programs to see the
pros and cons of python when a steped in some weird (at least for me)
behavior.

Here it is simplified

The code:
myList = [4 for n in range(4)]
myInt = 4
a = Test1()
b = Test1()
a.myList [4, 4, 4, 4]
a.myInt 4
b.myList [4, 4, 4, 4]
b.myInt 4
b.myList[2] = 3
b.myInt = 3
b.myList [4, 4, 3, 4]
b.myInt 3
a.myList [4, 4, 3, 4]
a.myInt
4


I would not expect the second a.myList to have changed as it did
since, for me, I have only changed b.myList. And also, why only the
list changed and not the integer?

One thing i tried was:
myList = []
myInt = 4
def __init__(self):
self.myList = [4 for n in range(4)]
a = Test2()
b = Test2()
a.myList [4, 4, 4, 4]
b.myList [4, 4, 4, 4]
b.myList[2] = 3
b.myList [4, 4, 3, 4]
a.myList
[4, 4, 4, 4]


And as you see it worked as I expected.

Now the question, why?
 
D

Diez B. Roggisch

SuperZE said:
Learning python I was rewriting some of my old programs to see the
pros and cons of python when a steped in some weird (at least for me)
behavior.

Here it is simplified

The code:
myList = [4 for n in range(4)]
myInt = 4
a = Test1()
b = Test1()
a.myList [4, 4, 4, 4]
a.myInt 4
b.myList [4, 4, 4, 4]
b.myInt 4
b.myList[2] = 3
b.myInt = 3
b.myList [4, 4, 3, 4]
b.myInt 3
a.myList [4, 4, 3, 4]
a.myInt
4


I would not expect the second a.myList to have changed as it did
since, for me, I have only changed b.myList. And also, why only the
list changed and not the integer?

One thing i tried was:
myList = []
myInt = 4
def __init__(self):
self.myList = [4 for n in range(4)]
a = Test2()
b = Test2()
a.myList [4, 4, 4, 4]
b.myList [4, 4, 4, 4]
b.myList[2] = 3
b.myList [4, 4, 3, 4]
a.myList
[4, 4, 4, 4]


And as you see it worked as I expected.

Now the question, why?

Because you declare myList to be a *class*-level variable, which means *all*
instances of that class (a and b in your case) *share* it. Python does not
declare *instance* variables the way you do.

Instead, do this:

class Foo(object):
def __init__(self):
self.myList = []

And to avoid a common pitfall you are likely to hit next: default arguments
in functions (and methods) are evaluated only *once*, when defined - not
later!

So

def foo(some_arg=[]):
some_arg.append("a")
print some_arg

foo()
foo()

will result in

['a']
['a', 'a']

being printed out.

Diez
 
S

SuperZE

Because you declare myList to be a *class*-level variable, which means *all*
instances of that class (a and b in your case) *share* it. Python does not
declare *instance* variables the way you do.

Instead, do this:

class Foo(object):
    def __init__(self):
        self.myList = []


Interesting, but that does not explain the difference in the behavior
of myList and myInt

Both were class-level variables, as far as I can see, and therefor a
and b should also share it


And good remind on default arguments :)
 
D

Diez B. Roggisch

SuperZE said:
Because you declare myList to be a *class*-level variable, which means
*all* instances of that class (a and b in your case) *share* it. Python
does not declare *instance* variables the way you do.

Instead, do this:

class Foo(object):
def __init__(self):
self.myList = []


Interesting, but that does not explain the difference in the behavior
of myList and myInt

Sorry, I forgot that.

The reason is simple - lists are mutable. Numbers aren't. And when you
*assign* to an instance a value, it will be set on the instance, not on the
class.

The same happens (as you've already seen for yourself) when you do

a.myList = []

as this creates a new list, stored only on a.

Both were class-level variables, as far as I can see, and therefor a
and b should also share it

Nope. To change it, you need to do

Test1.myInt = 5

The thing is that the lookup triggered by getattr/dotted notation goes
roughly like this:

1) try to find the attribute (value *or* method! Python doesn't distinguish)
in the instance itself.

2) if not found there, look at the class.

3) if not found there, look at the base-classes in the method resolution
order (MRO)

There are some other things involved - descriptor protocol calls on the
class-level lookups - but that's not relevant for the problem at hand.

OTOH, when setting with setattr/dotted notation, it will always set on the
object passed.

Diez
 
J

Jerry Hill

Interesting, but that does not explain the difference in the behavior
of myList and myInt

Both were class-level variables, as far as I can see, and therefor a
and b should also share it

They did share it, until you assigned an instance variable in b, which
shadowed the class variable. Example:
myInt = 4

4

As soon as you bound the name b.myInt to a new value, it created an
instance variable. That hides the value of Test1.myInt.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top