472,960 Members | 2,023 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,960 software developers and data experts.

Class Variable Inheritance

I'm sure the solution may be obvious, but this problem is driving me
mad. The following is my code:

class a(object):

mastervar = []

def __init__(self):
print 'called a'

class b(a):

def __init__(self):
print 'called b'
self.mapvar()

def mapvar(self):
self.mastervar.append(['b'])

class c(b):

def __init__(self):
print 'called c'
self.mapvar()

def mapvar(self):
super(c, self).mapvar()
self.mastervar.append(['c'])

if __name__ == '__main__':

a1 = a()
b1 = b()
c1 = c()
d1 = c() # Call C again

print a1.mastervar
print b1.mastervar
print c1.mastervar
print d1.mastervar

What I don't understand is why mastervar gets modified by each _seperate
instance_ of classes that happen to extend the base class 'a'.
Shouldn't mastervar be contained within the scope of the inheriting
classes? Why is it being treated like a global variable and being
modified by the other instances?
Thanks,

Brian "bojo" Jones
Jul 18 '05 #1
6 3139
It became clear to me that mastervar inside of class a is a static
variable, and is associated with all instances of classes that extend
class a. To get around this, I implemented a seperate mapping class:

class mapper:

def __init__(self):
self.mastermap = []

def add(self, map):
self.mastermap.append(map)

def get(self):
return self.mastermap

class a(object):

def __init__(self):
self.map = mapper()
print 'called a'

class b(a):

def __init__(self):
self.map = mapper()
print 'called b'
self.mapvar()

def mapvar(self):
self.map.add('b')

class c(b):

def __init__(self):
self.map = mapper()
print 'called c'
self.mapvar()

def mapvar(self):
super(c, self).mapvar()
self.map.add('c')

if __name__ == '__main__':

a1 = a()
a2 = a()
b1 = b()
c1 = c()
d1 = c() # Call C again

print a1.map.get()
print a1.map.get()
print b1.map.get()
print c1.map.get()
print d1.map.get()

Brian Jones wrote:
I'm sure the solution may be obvious, but this problem is driving me
mad. The following is my code:

Jul 18 '05 #2
On Wed, 08 Dec 2004 15:55:09 -0900, Brian Jones wrote:
I'm sure the solution may be obvious, but this problem is driving me
mad. The following is my code:

class a(object):

mastervar = []

def __init__(self):
print 'called a'

class b(a):

def __init__(self):
print 'called b'
self.mapvar()

def mapvar(self):
self.mastervar.append(['b'])

class c(b):

def __init__(self):
print 'called c'
self.mapvar()

def mapvar(self):
super(c, self).mapvar()
self.mastervar.append(['c'])

if __name__ == '__main__':

a1 = a()
b1 = b()
c1 = c()
d1 = c() # Call C again

print a1.mastervar
print b1.mastervar
print c1.mastervar
print d1.mastervar

What I don't understand is why mastervar gets modified by each _seperate
instance_ of classes that happen to extend the base class 'a'.
Shouldn't mastervar be contained within the scope of the inheriting
classes? Why is it being treated like a global variable and being
modified by the other instances?
Thanks,

Brian "bojo" Jones

Brian,

This is the first time I've responded to a post in this newsgroup so bear
with me.

First of all I believe that the place you declare mastervar is important.
As you have placed it above (or outside) any class methods/functions it
becomes a class variable. This gives it class scope and ALL instances of
that class (and sub-classes I think) will reference the SAME variable. If,
instead, you declare it inside the __init__ function of the class a object
it will be an instance variable and inheritable.

Secondly, in order to inherit methods and functions from base classes it
is important to remember to call the base class __init__ function in the
sub-class __init__ function.

If you do both these things, that is declare mastervar as 'self.mastervar
= []' inside class a.__init__ and call a.__init__ from b.__init__ (and
likewise for b in c) then you should get the expected result. I hope.

All I can say at this point is that I and not what you would call a
programmer, and I could be quite wrong in my explanation. However if this
doesn't help then there are many folk here who are better qualified to
give you the correct solution.
Regards
Jul 18 '05 #3
On Wed, 08 Dec 2004 16:49:34 -0900, Brian "bojo" Jones wrote:
class a(object):

def __init__(self):
self.map = mapper()
print 'called a'


What is the advantage of this over

def __init__(self):
self.map = []

?
Jul 18 '05 #4
Brian "bojo" Jones wrote:
It became clear to me that mastervar inside of class a is a static
variable, and is associated with all instances of classes that extend
class a.


Yeah, that's basically what's happening. AFAICT, a variable declared at
class level is shared with all subclasses (and is available to all
instances unless hidden by an instance variable). You can simulate the
kind of behavior you want using descriptors:
import copy
class subclass_copied(object): .... def __init__(self, initial_value):
.... self.initial_value = initial_value
.... self.instances = {}
.... def __get__(self, instance, owner):
.... if owner not in self.instances:
.... self.instances[owner] = copy.copy(self.initial_value)
.... return self.instances[owner]
.... class A(object): .... x = subclass_copied([])
.... class B(A): .... pass
.... A.x.append(1)
A().x.append(2)
A.x [1, 2] B.x [] B.x.append(3)
B().x.append(4)
B.x [3, 4] A.x

[1, 2]

Basically, the subclass_copied descriptor returns a different object for
each class by keeping a type -> object dict. If you wanted to be
thorough with this, you would probably define a __set__ method too; see:

http://docs.python.org/ref/descriptors.html

Steve

Steve
Jul 18 '05 #5
On Thu, 2004-12-09 at 08:55, Brian Jones wrote:
I'm sure the solution may be obvious, but this problem is driving me
mad. The following is my code:

class a(object):

mastervar = []

def __init__(self):
print 'called a'

class b(a):

def __init__(self):
print 'called b'
self.mapvar()

def mapvar(self):
self.mastervar.append(['b'])

class c(b):

def __init__(self):
print 'called c'
self.mapvar()

def mapvar(self):
super(c, self).mapvar()
self.mastervar.append(['c'])

if __name__ == '__main__':

a1 = a()
b1 = b()
c1 = c()
d1 = c() # Call C again

print a1.mastervar
print b1.mastervar
print c1.mastervar
print d1.mastervar

What I don't understand is why mastervar gets modified by each _seperate
instance_ of classes that happen to extend the base class 'a'.
Shouldn't mastervar be contained within the scope of the inheriting
classes? Why is it being treated like a global variable and being
modified by the other instances?


A variable declared in a class definition is shared by all instances of
the class. Python uses, essentially, a search path for names that goes
from most specific to least specific scope. In your example, a lookup in
c1 for mastervar will search for mastervar in:

c1.__dict__
c1.__class__.dict (ie b.__dict__)
a.__dict__

Note that "class variables" are not _copied_, they're just looked up in
the class object if not found in the instance. Remember, the class
declaration is only executed _once_ - then __init__ is executed for each
new instance. If you want a per-instance copy of a variable, you need to
generate a new copy in the __init__ method of the instance (or the
parent class and call the parent's __init__ with super() ).

If you actually assigned c1.mastervar, rather than modifying the dict,
you would get the per-instance dictionary you expected.

I strongly recommend a read of the book "Learning Python" if you want to
really _understand_ this stuff (and they do a much better job explaining
it than I ever could in my overcomplicated and incoherent way).

I think the Python behaviour is counter-intuitive to C++ and Java
programmers, but makes good sense in the context of the way Python
classes, inheritance, and namespaces work. It's also extremely
consistent with the way namespaces and inheritance work in the rest of
Python - there's no special magic for objects and classes. A good rule
might be "If you want Java-style instance variables, create instances
variables in __init__ not in the class declaration."

This might help explain things - ore might just confuse even more:
class A(object): .... ina = ["in A"]
.... class B(A): .... inb = ["in B"]
.... class C(A): .... ina = ["in C"]
.... inc = ["in C"]
.... # examine the class dictionaries .... A.__dict__.keys() ['ina', ...] B.__dict__.keys() ['inb', ...] C.__dict__.keys() ['ina', 'inc', ...] # Now look up some variables to demonstrate the namespace .... # search in class inheritance
.... A.ina ['in A'] B.ina ['in A'] C.ina # remember, we redefined this in C ['in C'] B.inb ['in B'] C.inc ['in C'] # This should help explain things .... B.ina is A.ina # True, because B.ina just looks up A.ina True C.ina is A.ina # False, because C.ina is found first False # Now modify B.ina. Because asking for B.ina searches B, then A, .... # for ina, we'll actually end up modifying A.ina
.... B.ina.append("blah")
A.ina ['in A', 'blah'] B.ina ['in A', 'blah'] # but if we do the same to C.ina, which is redefined in C, .... # a.ina won't be modified
.... C.ina.append("change")
A.ina ['in A', 'blah'] C.ina ['in C', 'change'] # Now we're going to assign to B.ina, rebinding the name, .... # instead of just modifying the existing mutable object.
.... B.ina = "fred"
B.ina "fred" B.__dict__.keys() ['inb', 'ina'] # Note that there's now a new value for ina in B's dictionary. It .... # is found by the search BEFORE looking for A.ina, and used. A.ina
.... # is not modified.
.... A.ina ['in A', 'blah']
What you can see happening here is the combination of a couple of
principles:
- Name lookups in classes happen as a search through the class
dictionary then all its parent classes' dictionaries for a name.
- Name assignments to a class are made directly in its dictionary.
- A modification of a mutable value is a lookup of a name followed
by the modification of an object, not an assignment to a name.

The principle is pretty similar for instances and classes - a variable
defined in a class is a single object shared between all instances of a
class, and if mutable can be modified by all of them. Example:
class D(object): .... cvar = []
.... def __init__(self, name):
.... self.cvar.append(name)
.... print "I am ", name
.... a = D("fred") I am fred b = D("jones") I am jones D.cvar ['fred', 'jones'] a.cvar is D.cvar True

If, on the other hand, I assign to that name - rather than modifying a
mutable object:
class E(object): .... name = "fred"
.... def __init__(self, name):
.... self.name = name
.... a = E("smith")
b = E("anna")
E.name 'fred' a.name 'smith' b.name 'anna' E.name is a.name False a.__dict__.keys() ['name'] b.__dict__.keys() ['name'] E.__dict__.keys()
E.__dict__.keys() ['name', ...]

a new entry is inserted into each instances dictionary. The class
variable is still there, and unmodified, but the name search finds the
copy each instance has in its dict before the class one.
a.__class__.name 'fred' a.__class__.name = "Albert"
a.__class__.name 'fred' a.name

'smith'

See?

The handling of names on instances and subclasses is a lot like the way
names are handled in scopes in functions. Lookups scan upward from most
specific to least specific scope, ending at the global module scope,
while assignments bind a name into the local scope. Again, modifying a
mutable global works the same way as modifying a mutable member of a
class object.

I know it sounds darn complicated, but once you "get it" about namespace
searches, the difference between modifying an object and binding an
name, etc it's all very clean and nice and easy. I love the way
inheritance and name lookups work in Python - it's all 100% consistent.

The best way to learn it is play with it, though reading Learning Python
is strongly recommended IMHO.

You can use this behaviour to your _strong_ benefit, with
instance-caching classes (requires overriding __new__), classes that
automatically register and keep track of instances, and instances that
can all inherit a change made to the class object even after they're
instantiated. I've found instance caching, in particular, to be just
magic when performing lots of data processing.

--
Craig Ringer

Jul 18 '05 #6
Brian Jones wrote:
class a(object):
mastervar = []
def __init__(self):
print 'called a'

class b(a):
def __init__(self):
print 'called b'
self.mapvar()
def mapvar(self):
self.mastervar.append(['b'])

class c(b): mastervar = [] # Adding this should make things clearer def __init__(self):
print 'called c'
self.mapvar()
def mapvar(self):
super(c, self).mapvar()
self.mastervar.append(['c'])

if __name__ == '__main__':
a1 = a()
b1 = b()
c1 = c()
d1 = c() # Call C again for object in a1, b1, c1, d1:
print id(object.mastervar), object.mastervar
What I don't understand is why mastervar gets modified by each _seperate
instance_ of classes that happen to extend the base class 'a'. Shouldn't
mastervar be contained within the scope of the inheriting classes? Why
is it being treated like a global variable and being modified by the
other instances?

By over-riding mastervar in class c, I hope I've shown that a class
variable is shared by all of its instances, but can be over-ridden by
a subclass's class variable.

--Scott David Daniels
Sc***********@Acm.Org
Jul 18 '05 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

11
by: Ricky Romaya | last post by:
Hi, Are there any ways to get multiple inheritace in PHP4? For example, I have 3 parent class, class A, B, and C. I want class X to inherit all those 3 classes. Consider merging those 3 classes...
2
by: Jim Schueler | last post by:
Here is some sample code that uses inherited class methods: sub parseHTML::docomment { my $comment = shift ; print $comment, "\n" ; } sub parseASP::AUTOLOAD { use vars qw( $AUTOLOAD ) ;
8
by: Nick | last post by:
I have the following code: var obj = {a:0, b:1, ...} function f() {...} obj.f = f // This will make a instance method? How to make a Class method for the class of obj? Or what's the...
9
by: Banaticus Bart | last post by:
I wrote an abstract base class from which I've derived a few other classes. I'd like to create a base class array where each element is an instance of a derived object. I can create a base class...
8
by: Bryan Parkoff | last post by:
I find an interesting issue that one base class has only one copy for each derived class. It looks like that one base class will be copied into three base classes while derived class from base...
166
by: Graham | last post by:
This has to do with class variables and instances variables. Given the following: <code> class _class: var = 0 #rest of the class
9
by: David A. Osborn | last post by:
I have a set of classes that each have an enumeration in them, and based on dynamic input I need to access a different enumeration. For example Three classes Class_A, Class_B, and Class_C that...
7
by: WXS | last post by:
Vote for this idea if you like it here: http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=5fee280d-085e-4fe2-af35-254fbbe96ee9...
30
by: Logos | last post by:
I have what may be a bug, or may be a misunderstanding on how pass by reference and class inheritance works in PHP. Since I'm relatively new to PHP, I'm hoping for a little outside help to shed...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
by: Aliciasmith | last post by:
In an age dominated by smartphones, having a mobile app for your business is no longer an option; it's a necessity. Whether you're a startup or an established enterprise, finding the right mobile app...
0
tracyyun
by: tracyyun | last post by:
Hello everyone, I have a question and would like some advice on network connectivity. I have one computer connected to my router via WiFi, but I have two other computers that I want to be able to...
4
NeoPa
by: NeoPa | last post by:
Hello everyone. I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report). I know it can be done by selecting :...
3
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be using a very simple database which has Form (clsForm) & Report (clsReport) classes that simply handle making the calling Form invisible until the Form, or all...
1
by: Teri B | last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course. 0ne-to-many. One course many roles. Then I created a report based on the Course form and...
3
by: nia12 | last post by:
Hi there, I am very new to Access so apologies if any of this is obvious/not clear. I am creating a data collection tool for health care employees to complete. It consists of a number of...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.