Bacon
Bacon

Reputation: 2195

help with python inheritance

Given an arbitrary object:

class Val(object):
    def __init__(self):
        this_val = 123

I want to create an abstract base class which has an attribute that is a Val():

class A(object):
    foo = Val()

I would expect that when my children inherit from that class, they would get copies of Val(). For example:

class B(A):
    pass
class C(A):
    pass

I would expect the following behavior:

>>> b = B()
>>> c = C()
>>> c.foo.this_val = 456
>>> b.foo.this_val
123

But instead I get:

>>> b.this_val
456

I understand that I could just self.foo = Val() into the init to achieve that behavior, but I have a requirement that foo remain an attribute (it is a model manager in django). Can anyone suggest a work around for this?

EDIT: I really need to be able to access the value as a class attribute, so my desired behavior is:

>>> C.foo.this_val = 456
>>> B.foo.this_val
123

Upvotes: 1

Views: 137

Answers (3)

Lauritz V. Thaulow
Lauritz V. Thaulow

Reputation: 51015

Maybe using a descriptor would suit your requirements:

class Val(object):
    def __init__(self):
        self.this_val = 123

class ValDesc(object):
    def __init__(self):
        self.cls_lookup = {}

    def __get__(self, obj, objtype=None):
        return self.cls_lookup.setdefault(objtype, Val())

class A(object):
    foo = ValDesc()

class B(A):
    pass
class C(A):
    pass

Now, as long as you make sure you don't set the instance attribute "foo" of any of your objects, they will have a class attribute that is individual to each subclass:

b = B()
c = C()
cc = C()
c.foo.this_val = 456
print c.foo.this_val   # 456
print cc.foo.this_val  # 456
print b.foo.this_val   # 123

EDIT: With the edit I made some hours ago, changing the key in __get__ to be objtype instead of obj.__class__, this also works when accessing the class attributes directly:

print B.foo.this_val   # 123
print C.foo.this_val   # 456

Upvotes: 2

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799560

The attribute foo only exists on A. You will have to use a metaclass to add a new Val to each class.

class Val(object):
    def __init__(self):
        self.this_val = 123

class MC(type):
  def __init__(self, name, bases, d):
    super(MC, self).__init__(name, bases, d)
    self.foo = Val()

class A(object):
  __metaclass__ = MC

class B(A):
  pass

B.foo.this_val = 456
print A.foo.this_val
print B.foo.this_val

Upvotes: 4

Amber
Amber

Reputation: 527538

Do both.

Make it a class attribute, but also initialize it to a fresh instance in the __init__ function. That way the reference stored isn't a shared one.

Upvotes: 0

Related Questions