Last active
May 26, 2016 02:42
-
-
Save rmorshea/7a1b130f0792a5b1babffb37bfc30744 to your computer and use it in GitHub Desktop.
call methods of a parent class during class construction.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import inspect | |
def metamethod(cls, name): | |
"""Get a method owned by a metaclass | |
The method is retrieved from the instantiating metaclass. | |
Parameters | |
---------- | |
cls: class | |
The class whose instantiating metaclass will be searched. | |
""" | |
meta = cls.__class__ | |
try: | |
cm = classmethod(getattr(meta, name)) | |
except: | |
# attribute error or type error | |
m = "no metamethod named '%s'" | |
raise AttributeError(m % name) | |
return cm.__get__(None, cls) | |
class MetaSuperClass(type): | |
"""A class proxy injected by a metasuper object""" | |
def __new__(mcls, msuper, head): | |
if isinstance(msuper, metasuper): | |
return super(MetaSuperClass, mcls).__new__( | |
MetaSuperClass, msuper.__base__.__name__, (object,), | |
{'__metasuper__': msuper, '__mropos__': head}) | |
else: | |
raise TypeError('expected a metasuper instance') | |
def __init__(cls, *args, **kwargs): pass | |
def __getattr__(cls, name): | |
return getattr(cls.__metasuper__.__base__, name) | |
def __setattr__(cls, name, value): | |
setattr(cls.__metasuper__.__base__, name, value) | |
@property | |
def __class__(cls): | |
return cls.__metasuper__.__base__.__class__ | |
class metasuper(object): | |
"""Access super classmethods during class construction""" | |
def __new__(cls, base, head=None): | |
if inspect.isclass(base): | |
if hasattr(base, '__metasuper__') and hasattr(base, '__mropos__'): | |
return base.__metasuper__.copy(base.__mropos__) | |
else: | |
inst = super(metasuper, cls).__new__(cls) | |
inst.__head__ = head or base | |
inst.__base__ = base | |
return inst | |
else: | |
m = "expected a class, not: '%s'" | |
raise ValueError(m % repr(type(cls))) | |
def __getattr__(self, name): | |
initial_find = False | |
for c in self.__head__.mro(): | |
if name in c.__dict__: | |
v = c.__dict__[name] | |
if initial_find: | |
if isinstance(v, classmethod): | |
return c.__dict__[name].__get__( | |
None, MetaSuperClass(self, c)) | |
else: | |
m = "Expected '%s' to be a classmethod" | |
raise TypeError(m % name) | |
else: | |
initial_find = True | |
continue | |
else: | |
cls = self.__class__.__name__ | |
m = "'%s' object has no attribute '%s'" | |
raise AttributeError(m % (cls, name)) | |
def copy(self, head): | |
return self.__class__.__new__( | |
self.__class__, self.__base__, | |
head) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
MetaSuper
Call methods of a parent class during class construction.
The Hack
A class's method that gets called before its metaclass has completed
__new__
and__init__
can't use super (see why). However,metasuper
is super-like substitute which allows these kinds of methods to be properly overridden and called in subclasses. Essentially this meansmetasuper
along withmetamethod
are simple hacks that makes it possible to bypass metaclass inheritance during class construction:metasuper inheritance
metaclass inheritance
output