Python - Accessing parent class decorators inside class declaration - python

Let's say I have this class:
class Foo:
#classmethod
def some_decorator(cls, ...):
...
And then I create a subclass which uses the parent class decorator:
class Bar(Foo):
#Foo.some_decorator(...)
def some_function(...)
...
How do I remove the need for Foo. before the decorator name? The below code doesn't work:
class Bar(Foo):
#some_decorator(...)
def some_function(...)
...
I believe this is possible, because the sly library does this.
See their example:
from sly import Lexer, Parser
class CalcLexer(Lexer):
...
#_(r'\d+')
def NUMBER(self, t):
t.value = int(t.value)
return t
...
As you can see, you can type in #_(...) instead of #Lexer._(...).
How do they accomplish this?

This is done with a metaclass that implements a __prepare__ method. Excerpt from the docs:
3.3.3.4. Preparing the class namespace
Once the appropriate metaclass has been identified, then the class
namespace is prepared. If the metaclass has a __prepare__ attribute,
it is called as namespace = metaclass.__prepare__(name, bases, **kwds)
(where the additional keyword arguments, if any, come from the class
definition).
To put it in simple terms: You make your __prepare__ method return a dictionary that contains an entry for the decorator. Proof of concept:
class MyMeta(type):
def __prepare__(name, bases):
return {'x': 'foobar'}
class MyClass(metaclass=MyMeta):
print(x) # output: foobar

I have looked inside the library you are talking about and the Lexer class inherits a metaclass:
class Lexer(metaclass=LexerMeta):
Inside the LexerMeta you can find the following:
#classmethod
def __prepare__(meta, name, bases):
d = LexerMetaDict()
def _(pattern, *extra):
patterns = [pattern, *extra]
def decorate(func):
pattern = '|'.join(f'({pat})' for pat in patterns )
if hasattr(func, 'pattern'):
func.pattern = pattern + '|' + func.pattern
else:
func.pattern = pattern
return func
return decorate
d['_'] = _
d['before'] = _Before
return d
A metaclass is used to create the class object which then is used to instantiate objects. From what i can see in that method is that here d['_'] = _ that metaclass dynamically attaches the _ method to the class you are going to use.
This means that what they are doing is not much different from:
class Bar:
#staticmethod
def some_decorator(f):
...
#some_decorator
def some_function(self):
...

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

Inheriting a non-abstract class and turning all of its functions into abstractmethods

An upstream interface was given to me with all of its functions defined as non-abstract when in reality they should be decorated with #abstractmethods. I want to receive an error when I did not implement one of its functions when it's called. To do this, I would create a wrapper class and manually go through each of its defined functions and do something like this:
from abc import ABC, abstractmethod
class Foo(object):
def foo(self):
print("Foo")
class AbstractFoo(Foo, ABC):
#abstractmethod
def foo(self):
return super().foo()
class ConcreteFoo(AbstractFoo):
def foo(self):
print("Concrete Foo")
super().foo()
f = ConcreteFoo()
f.foo()
Which outputs:
Concrete Foo
Foo
I would like some way of just doing this to all functions defined by Foo. Obviously, inherited magic functions like __str__ and __repr__ should be forwarded appropriately.
Does anyone know a nice, pythonic way of doing this?
def validate_base_class_implemntation(cls):
base_cls_funcs = []
for attr in cls.__bases__[0].__dict__:
if callable(getattr(cls, attr)):
base_cls_funcs.append(attr)
cls_funcs = []
for attr in cls.__dict__:
if callable(getattr(cls, attr)):
cls_funcs.append(attr)
missing_funcs = [x for x in base_cls_funcs if x not in cls_funcs]
if len(missing_funcs) > 0:
print("Not implemented functions are: {}".format(','.join(missing_funcs)))
raise Exception("Not implement function exception!")
return cls
class Foo(object):
def foo(self):
print("Foo")
def boo(self):
print("Wow")
#validate_base_class_implemntation
class ConcreteFoo(Foo):
def foo(self):
print("Concrete Foo")
super().foo()
f = ConcreteFoo()
f.foo()
Not sure in 100% if that what you meant.
this decorator checks that the class decorated implements all the base class function(in your case, they are not decorated with abstract). if there is a function that your decorated class does not implement, it raises exception.
You can modify the original class Foo and turn all its methods into abstract methods and then define a blank subclass of Foo with metaclass=ABCMeta in order to handle the checks:
from abc import ABCMeta, abstractmethod
from types import FunctionType
class AbstractFoo(Foo, metaclass=ABCMeta):
pass
names = set()
for k, v in vars(Foo).items():
if k.startswith('__') and k.endswith('__'):
continue
elif isinstance(v, FunctionType):
names.add(k)
v.__isabstractmethod__ = True
AbstractFoo.__abstractmethods__ = frozenset(names)
Side note: This approach relies on dunder attributes being used by abc and as such can break without deprecation.

Python Class Name as Class Variable

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

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

Making a class method recognize which class context it's running in

I need to refactor existing code by collapsing a method that's copy-and-pasted between various classed that inherit from one another into a single method.
So I produced the following code:
class A(object):
def rec(self):
return 1
class B(A):
def rec(self):
return self.rec_gen(B)
def rec_gen(self, rec_class):
return super(rec_class, self).rec() + 1
class C(B):
def rec(self):
return self.rec_gen(C)
if __name__=='__main__':
b = B(); c = C()
print c.rec()
print b.rec()
And the output:
3
2
What still bothers me is that in the 'rec' method I need to tell 'rec_gen' the context of the class in which it's running. Is there a way for 'rec_gen' to figure it out by itself in runtime?
This capability has been added to Python 3 - see PEP 3135. In a nutshell:
class B(A):
def rec(self):
return super().rec() + 1
I think you've created the convoluted rec()/rec_gen() setup because you couldn't automatically find the class, but in case you want that anyway the following should work:
class A(object):
def rec(self):
return 1
class B(A):
def rec(self):
# __class__ is a cell that is only created if super() is in the method
super()
return self.rec_gen(__class__)
def rec_gen(self, rec_class):
return super(rec_class, self).rec() + 1
class C(B):
def rec(self):
# __class__ is a cell that is only created if super() is in the method
super()
return self.rec_gen(__class__)
The simplest solution in Python 2 is to use a private member to hold the super object:
class B(A):
def __init__(self):
self.__super = super(B)
def rec(self):
return self.__super.rec() + 1
But that still suffers from the need to specify the actual class in one place, and if you happen to have two identically-named classes in the class hierarchy (e.g. from different modules) this method will break.
There were a couple of us who made recipes for automatic resolution for Python 2 prior to the existence of PEP 3135 - my method is at self.super on ActiveState. Basically, it allows the following:
class B(A, autosuper):
def rec(self):
return self.super().rec() + 1
or in the case that you're calling a parent method with the same name (the most common case):
class B(A, autosuper):
def rec(self):
return self.super() + 1
Caveats to this method:
It's quite slow. I have a version sitting around somewhere that does bytecode manipulation to improve the speed a lot.
It's not consistent with PEP 3135 (although it was a proposal for the Python 3 super at one stage).
It's quite complex.
It's a mix-in base class.
I don't know if the above would enable you to meet your requirements. With a small change to the recipe though you could find out what class you're in and pass that to rec_gen() - basically extract the class-finding code out of _getSuper() into its own method.
An alternative solution for python 2.x would be to use a metaclass to automatically define the rec method in all your subclasses:
class RecGen(type):
def __new__(cls, name, bases, dct):
new_cls = super(RecGen, cls).__new__(cls, name, bases, dct)
if bases != (object,):
def rec(self):
return super(new_cls, self).rec() + 1
new_cls.rec = rec
return new_cls
class A(object):
__metaclass__ = RecGen
def rec(self):
return 1
class B(A):
pass
class C(B):
pass
Note that if you're just trying to get something like the number of parent classes, it would be easier to use self.__class__.__mro__ directly:
class A(object):
def rec(self):
return len(self.__class__.__mro__)-1
class B(A):
pass
class C(B):
pass
I'm not sure exactly what you're trying to achieve, but if it is just to have a method that returns a different constant value for each class then use class attributes to store the value. It isn't clear at all from your example that you need to go anywhere near super().
class A(object):
REC = 1
def rec(self):
return self.REC
class B(A):
REC = 2
class C(B):
REC = 3
if __name__=='__main__':
b = B(); c = C()
print c.rec()
print b.rec()

Categories

Resources