Reputation: 2195
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
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
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
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