Reputation: 55421
Is there a way to make a variable non-inheritable in python? Like in the following example: B is a subclass of A, but I want it to have its own SIZE value.
Could I get an Error to be raised (on __init__ or on getsize()) if B doesn't override SIZE?
class A:
SIZE = 5
def getsize(self): return self.SIZE
class B(A): pass
Edit: ... while inheriting the getsize() method...?
Upvotes: 3
Views: 2282
Reputation: 59633
The only approach that I can add is to use hasattr(self.__class__, 'SIZE')
in the implementation of getsize()
and toss an exception if the attribute is not found. Something like:
class A:
SIZE = 5
def getsize(self):
klass = self.__class__
if hasattr(klass, 'SIZE') and 'SIZE' in klass.__dict__:
return self.SIZE
raise NotImplementedError('SIZE is not defined in ' + klass.__name__)
There is some magic still missing since the derived class could define a method named SIZE
and getsize
wouldn't detect it. You can probably do some type(klass.SIZE)
magic to filter this out if you want to.
Upvotes: 1
Reputation: 882851
If metaclasses scare you (and I sympathize with that attitude!-), a descriptor could work -- you don't even have to make your custom descriptor (though that's easy enough), a plain good old property could work fine too:
class A(object):
@property
def SIZE(self):
if type(self) is not A:
raise AttributeError("Class %s MUST explicitly define SIZE!" %
type(self).__name__)
def getsize(self):
return self.SIZE
Of course, this way you'll get the error only when an instance of a subclass of A which doesn't override SIZE actually tries to use self.SIZE
(the metaclass approach has the advantage of giving the error earlier, when an errant subclass of A is created).
Upvotes: 2
Reputation: 57458
Another common idiom is to use NotImplemented. Think of it as the middle ground between metaclass enforcement and mere documentation.
class A:
SIZE = NotImplemented
Now if a subclass forgets to override SIZE, the runtime errors will be immediate and obvious.
Upvotes: 0
Reputation: 39414
Another approach might be to get classes A and B to inherit from a third class instead of one from the other:
class X:
def getsize(self):
return self.SIZE
class A(X):
SIZE = 5
class B(X): pass
a = A()
print a.getsize()
# Prints 5
b = B()
print b.getsize()
# AttributeError: B instance has no attribute 'SIZE'
Upvotes: 0
Reputation: 281875
Use a double-underscore prefix:
(Double-underscore solution deleted after Emma's clarification)
OK, you can do it like this:
class A:
SIZE = 5
def __init__(self):
if self.__class__ != A:
del self.SIZE
def getsize(self):
return self.SIZE
class B(A):
pass
a = A()
print a.getsize()
# Prints 5
b = B()
print b.getsize()
# AttributeError: B instance has no attribute 'SIZE'
Upvotes: 8
Reputation: 34715
If you want to make absolutely sure that subclasses of A
override SIZE
, you could use a metaclass for A
that will raise an error when a subclass does not override it (note that A
is a new-style class here):
class ClassWithSize(type):
def __init__(cls, name, bases, attrs):
if 'SIZE' not in attrs:
raise NotImplementedError('The "%s" class does not implement a "SIZE" attribute' % name)
super(ClassWithSize, cls).__init__(name, bases, attrs)
class A(object):
__metaclass__ = ClassWithSize
SIZE = 5
def getsize(self):
return self.SIZE
class B(A):
SIZE = 6
class C(A):
pass
When you put the above in a module and attempt to import it, an exception will be raised when the import reaches the C
class implementation.
Upvotes: 5
Reputation: 27986
It sounds like what you want is a private variable. In which case this is what you need to do:
class A:
__SIZE = 5
def getsize(self):
return self.__SIZE
def setsize(self,newsize):
self.__SIZE=newsize
class B(A):
pass
Upvotes: 0