Python Class Name as Class Variable - python

I'm working as an application with classes and subclasses. For each class, both super and sub, there is a class variable called label. I would like the label variable for the super class to default to the class name. For example:
class Super():
label = 'Super'
class Sub(Super):
label = 'Sub'
Rather than manually type out the variable for each class, is it possible to derive the variable from the class name in the super class and have it automatically populated for the subclasses?
class Super():
label = # Code to get class name
class Sub(Super)
pass
# When inherited Sub.label == 'Sub'.
The reason for this is that this will be the default behavior. I'm also hoping that if I can get the default behavior, I can override it later by specifying an alternate label.
class SecondSub(Super):
label = 'Pie' # Override the default of SecondSub.label == 'SecondSub'
I've tried using __name__, but that's not working and just gives me '__main__'.
I would like to use the class variable label in #classmethod methods. So I would like to be able to reference the value without having to actually create a Super() or Sub() object, like below:
class Super():
label = # Magic
#classmethod
def do_something_with_label(cls):
print(cls.label)

you can return self.__class__.__name__ in label as a property
class Super:
#property
def label(self):
return self.__class__.__name__
class Sub(Super):
pass
print Sub().label
alternatively you could set it in the __init__ method
def __init__(self):
self.label = self.__class__.__name__
this will obviously only work on instantiated classes
to access the class name inside of a class method you would need to just call __name__ on the cls
class XYZ:
#classmethod
def my_label(cls):
return cls.__name__
print XYZ.my_label()
this solution might work too (snagged from https://stackoverflow.com/a/13624858/541038)
class classproperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class Super(object):
#classproperty
def label(cls):
return cls.__name__
class Sub(Super):
pass
print Sub.label #works on class
print Sub().label #also works on an instance
class Sub2(Sub):
#classmethod
def some_classmethod(cls):
print cls.label
Sub2.some_classmethod()

You can use a descriptor:
class ClassNameDescriptor(object):
def __get__(self, obj, type_):
return type_.__name__
class Super(object):
label = ClassNameDescriptor()
class Sub(Super):
pass
class SecondSub(Super):
label = 'Foo'
Demo:
>>> Super.label
'Super'
>>> Sub.label
'Sub'
>>> SecondSub.label
'Foo'
>>> Sub().label
'Sub'
>>> SecondSub().label
'Foo'
If class ThirdSub(SecondSub) should have ThirdSub.label == 'ThirdSub' instead of ThirdSub.label == 'Foo', you can do that with a bit more work. Assigning label at the class level will be inherited, unless you use a metaclass (which is a lot more hassle than it's worth for this), but we can have the label descriptor look for a _label attribute instead:
class ClassNameDescriptor(object):
def __get__(self, obj, type_):
try:
return type_.__dict__['_label']
except KeyError:
return type_.__name__
Demo:
>>> class SecondSub(Super):
... _label = 'Foo'
...
>>> class ThirdSub(SecondSub):
... pass
...
>>> SecondSub.label
'Foo'
>>> ThirdSub.label
'ThirdSub'

A metaclass might be useful here.
class Labeller(type):
def __new__(meta, name, bases, dct):
dct.setdefault('label', name)
return super(Labeller, meta).__new__(meta, name, bases, dct)
# Python 2
# class Super(object):
# __metaclass__ = Labeller
class Super(metaclass=Labeller):
pass
class Sub(Super):
pass
class SecondSub(Super):
label = 'Pie'
class ThirdSub(SecondSub):
pass
Disclaimer: when providing a custom metaclass for your class, you need to make sure it is compatible with whatever metaclass(es) are used by any class in its ancestry. Generally, this means making sure your metaclass inherits from all the other metaclasses, but it can be nontrivial to do so. In practice, metaclasses aren't so commonly used, so it's usually just a matter of subclassing type, but it's something to be aware of.

As of Python 3.6, the cleanest way to achieve this is with __init_subclass__ hook introduced in PEP 487. It is much simpler (and easier to manage with respect to inheritance) than using a metaclass.
class Base:
#classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if 'label' not in cls.__dict__: # Check if label has been set in the class itself, i.e. not inherited from any of its superclasses
cls.label = cls.__name__ # If not, default to class's __name__
class Sub1(Base):
pass
class Sub2(Base):
label = 'Custom'
class SubSub(Sub2):
pass
print(Sub1.label) # Sub1
print(Sub2.label) # Custom
print(SubSub.label) # SubSub

Related

I want to call parent class method which is overridden in child class through child class object in Python

class abc():
def xyz(self):
print("Class abc")
class foo(abc):
def xyz(self):
print("class foo")
x = foo()
I want to call xyz() of the parent class, something like;
x.super().xyz()
With single inheritance like this it's easiest in my opinion to call the method through the class, and pass self explicitly:
abc.xyz(x)
Using super to be more generic this would become (though I cannot think of a good use case):
super(type(x), x).xyz()
Which returns a super object that can be thought of as the parent class but with the child as self.
If you want something exactly like your syntax, just provide a super method for your class (your abc class, so everyone inheriting will have it):
def super(self):
return super(type(self), self)
and now x.super().xyz() will work. It will break though if you make a class inheriting from foo, since you will only be able to go one level up (i.e. back to foo).
There is no "through the object" way I know of to access hidden methods.
Just for kicks, here is a more robust version allowing chaining super calls using a dedicated class keeping tracks of super calls:
class Super:
def __init__(self, obj, counter=0):
self.obj = obj
self.counter = counter
def super(self):
return Super(self.obj, self.counter+1)
def __getattr__(self, att):
return getattr(super(type(self.obj).mro()[self.counter], self.obj), att)
class abc():
def xyz(self):
print("Class abc", type(self))
def super(self):
return Super(self)
class foo(abc):
def xyz(self):
print("class foo")
class buzz(foo):
def xyz(self):
print("class buzz")
buzz().super().xyz()
buzz().super().super().xyz()
results in
class foo
Class abc

Which is the correct way of using a class method into its subclass that maybe won't be instantiated?

Given the parent class:
class Parent():
_private_attr: Int = None
#classmethod
def set_private_attr(cls, value):
if not type(value) is int:
raise ValueError()
cls._private_attr = value
How do I use set_private_attr into a subclass, given that maybe it will not be instantiated and therefore I can't use super() in __init__?
E.g.:
def SubClass(Parent):
Parent.set_private_attr(a_value)
Is that right? There's is a better way of doing this?
You need to inherit from Parent, then the derived class can use the set_private_attr() class method.
Here's an example.
class Parent():
_private_attr = None
#classmethod
def set_private_attr(cls, value):
cls._private_attr = value
class Foo(Parent):
pass
foo = Foo()
foo.set_private_attr("bar")
If you want to set this before constructing the derived class:
Foo.set_private_attr("bar")
foo = Foo()
print(foo._private_attr)

create instance of class within class definition

I'm trying to have a default instance of a class. I want to have
class Foo():
def __init__(self):
....
_default = Foo()
#staticmethod
def get_default():
return _default
However _default = Foo() leads to NameError: name 'Foo' is not defined
Foo does not exist until the class definition is finalized. You can easily refer to it after the class definition, though:
class Foo(object):
def __init__(self):
# ....
Foo.default_instance = Foo()
Note also that I have removed the superfluous getter method in favor of a plain old attribute.
You can also solve the problem with a decorator:
def defaultinstance(Class):
Class.default_instance = Class()
return Class
#defaultinstance
class Foo(object):
# ...
Or, gratuitously, with a metaclass:
def defaultmeta(name, bases, attrs):
Class = type(name, bases, attrs)
Class.default_instance = Class()
return Class
# Python 2.x usage
class Foo(object):
__metaclass__ = defaultmeta
# ...
# Python 3.x usage
class Foo(metaclass=defaultmeta):
# ...
When might you might want to use each method?
Use the post-definition class attribute assignment for one-offs
Use the decorator if you want the same behavior in a lot of unrelated classes and to "hide" the implementation of it (it's not really hidden, or even that complicated, here, though)
Use the metaclass if you want the behavior to be inheritable in which case it's not really gratuitous. :-)
You cannot refer to a class that doesn't yet exist. Within the class definition body, the Foo class is not yet created.
Add the attribute after the class has been created:
class Foo():
def __init__(self):
....
#staticmethod
def get_default():
return Foo._default
Foo._default = Foo()
Note that you also need to alter the get_default() static method; the class body doesn't form a scope, so you cannot reach _default as a non-local from get_default().
You are now, however, repeating yourself a lot. Reduce repetition a little by making get_default() a classmethod instead:
class Foo():
def __init__(self):
....
#classmethod
def get_default(cls):
return cls._default
Foo._default = Foo()
or create the default on first call:
class Foo():
def __init__(self):
....
#classmethod
def get_default(cls):
if not hasattr(cls, '_default'):
cls._default = cls()
return cls._default
You may lazily initialize your default instance.
class Foo(object):
_default = None
#staticmethod
def get_default():
if not Foo._default:
Foo._default = Foo()
return Foo._default

Alternative for inheritance in python

How to save code duplication in the following scenario ?
say Aand B are two classes having a common function(say) name
class A(object):
name = 'foo'
#property
def name(self): # the common function
return self.name
similarly B
class B(object):
name = 'bar'
#property
def name(self):
return self.name
One way would be to make a class from which both of them inherit from, and define name there.
Any good alternatives ?
If you're really determined to avoid inheritance, just define a function outside of either class:
def get_name(object):
return object.name
class A(object):
name = 'foo'
def get_name(self): # the common function
return self.name
class B(A):
pass
In this case B would inherit from A
Is there a reason you can't have B inherit from A?
class B(A):
name = 'bar'
Since you are decorating name with #property, I am assuming you want this to be an instance variable. If you want this to return a more private variable, let's call it _name, you have to do:
class A(object):
def __init__(self):
self._name = 'foo'
#property
def name(self):
return self._name
You can't have both a variable and a function have the same name, since the latter will simply override the former. If you want a base class that takes care of this, it would look like this:
class HasName(object):
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
class A(HasName):
def __init__(self):
self._name = 'foo'
class B(HasName):
def __init__(self):
self._name = 'bar'
You can also call the constructor in HasName.
Assuming self.name stands in for a more complex method, the easiest way to cut down on duplicated code is to move the function out to the module and have it take an arbitrary object as a parameter. Then, if you still want to tie the method directly to the class, you can add a short method that dispatches to the module function.
def _name(obj):
return obj.name
class A(object):
# ...
#property
def name(self):
return _name(self)
class B(object):
# ...
#property
def name(self):
return _name(self)
Note that this will not work well if A.name and B.name have completely different behaviors. If the _name function starts checking the type of the object given, reconsider whether you really want to abstract that functionality in the first place.

Set attribute of subclass in method of super class

I have the following scenario:
class Baseclass(object):
extra_fields = []
#classmethod
def extend(cls, key):
cls.extra_fields.append(key)
class A(Baseclass):
pass
class B(Baseclass):
pass
A.extend("foo")
Now, extend of Baseclass will be called, setting Baseclass.extra_fields to ["foo"]. Then, A.extra_fields will be ["foo"], however so will B.extra_fields.
Is there a way in extend to only modify only the subclass on which it was invoked (without defining extend on all subclasses, as those may not be known in advance)?
class Baseclass(object):
#classmethod
def extend(cls, key):
if not 'extra_fields' in cls.__dict__:
cls.extra_fields=[]
cls.extra_fields.append(key)
class A(Baseclass):
pass
class B(Baseclass):
pass
A.extend("foo")
How about defining own extra_fields for each subclass?
class Baseclass(object):
#classmethod
def extend(cls, key):
cls.extra_fields.append(key)
class A(Baseclass):
extra_fields = []
class B(Baseclass):
extra_fields = []
A.extend("foo")
(You can automate that with some metaclasses magic as well.)

Categories

Resources