Referring to the class name from a class variable where inheritanceis involved

P

Paul Moore

I want to set up an inheritance hierarchy. The base class will define
a string value which should include the class name, but I don't want
people who inherit from my class to have to remember to override the
value.

If I do this using an instance variable, it's reasonably easy:
.... def __init__(self):
.... self.key = 'Key_for_' + self.__class__.__name__
.... def display(self):
.... print self.key
........ pass
....Key_for_Inherited

Rather than having the key for every instance, I'd like to use a class
variable, but I can't see how I'd make that work (a class variable
which is inherited but has a different value in derived classes). I
could use a classmethod,but that feels like even more overkill than an
instance attribute.

Is there a way of doing this via class variables or something, or more
relevantly, I guess, what would be the idiomatic way of doing
something like this?

Thanks,
Paul
 
M

Matt Saxton

I want to set up an inheritance hierarchy. The base class will define
a string value which should include the class name, but I don't want
people who inherit from my class to have to remember to override the
value.

If I do this using an instance variable, it's reasonably easy:

... def __init__(self):
... self.key = 'Key_for_' + self.__class__.__name__
... def display(self):
... print self.key
...
... pass
...
Key_for_Inherited

Rather than having the key for every instance, I'd like to use a class
variable, but I can't see how I'd make that work (a class variable
which is inherited but has a different value in derived classes). I
could use a classmethod,but that feels like even more overkill than an
instance attribute.

Is there a way of doing this via class variables or something, or more
relevantly, I guess, what would be the idiomatic way of doing
something like this?

Thanks,
Paul

You can use a metaclass for this:
.... def __new__(mcs, name, bases, dict):
.... dict['key'] = 'Key_for_%s' % name
.... return type.__new__(mcs, name, bases, dict)
........ __metaclass__ = BaseMeta
........ pass
....'Key_for_Inheritor'

You can find more info on metaclasses here:
http://http://docs.python.org/reference/datamodel.html#customizing-class-creation


Regards
Matt
 
I

Ian Kelly

I want to set up an inheritance hierarchy. The base class will define
a string value which should include the class name, but I don't want
people who inherit from my class to have to remember to override the
value.

If I do this using an instance variable, it's reasonably easy:

...     def __init__(self):
...         self.key = 'Key_for_' + self.__class__.__name__
...     def display(self):
...         print self.key
...
...     pass
...
Key_for_Inherited

Rather than having the key for every instance, I'd like to use a class
variable, but I can't see how I'd make that work (a class variable
which is inherited but has a different value in derived classes). I
could use a classmethod,but that feels like even more overkill than an
instance attribute.

Is there a way of doing this via class variables or something, or more
relevantly, I guess, what would be the idiomatic way of doing
something like this?

How about a class property?

class classproperty(object):
def __init__(self, fget):
self.__fget = fget
def __get__(self, instance, owner):
return self.__fget(owner)

class BaseClass(object):
@classproperty
def key(cls):
return "Key_for_" + cls.__name__

class DerivedClass(BaseClass):
pass

assert BaseClass.key == BaseClass().key == "Key_for_BaseClass"
assert DerivedClass.key == DerivedClass().key == "Key_for_DerivedClass"

If you like, you can also expand classproperty to allow setters and
deleters like property does.

Cheers,
Ian
 
I

Ian Kelly

If you like, you can also expand classproperty to allow setters and
deleters like property does.

My mistake, you can't do this. __set__ and __delete__ are only
invoked on instances, not on the class. Get-only class properties are
fine, though.
 
P

Paul Moore

How about a class property?

class classproperty(object):
   def __init__(self, fget):
       self.__fget = fget
   def __get__(self, instance, owner):
       return self.__fget(owner)

Nice :) About as heavyweight as a classmethod, though, so it doesn't
buy me much over that (beyond avoiding a set of parens, which isn't a
big deal). Either classmethod or classproperty gives me the key
benefit I'm after, which is avoiding the per-instance overhead,
though.

TBH, this is a huge case of premature optimisation, though, so it's
probably little more than an academic or style question. :)

Paul
 
P

Paul Moore

You can use a metaclass for this:

 >>> class BaseMeta(type):
...     def __new__(mcs, name, bases, dict):
...         dict['key'] = 'Key_for_%s' % name
...         return type.__new__(mcs, name, bases, dict)
...
 >>> class Base:
...     __metaclass__ = BaseMeta
...
 >>> class Inherited(Base):
...     pass
...
 >>> Base.key
'Key_for_Base'
 >>> Inherited.key
'Key_for_Inheritor'

I like that! I tend to think of metaclasses as for "complicated"
stuff. It's nice to see a simple use.
Paul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,697
Latest member
AugustNabo

Latest Threads

Top