1

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

3 Answers 3

4

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
Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

0

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.

4 Comments

I need to reference it as an attribute, though. I want to do C.foo.this_val and B.foo.this_val and get different results. Sorry if that wasn't clear. I have no instance :(
Override it in the individual class definition(s) then?
I had really hoped not to. There must be some obscure python private method that I can override to achieve this?
Er, even if there were, it'd be more complex than just overriding it in the class defs...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.