Take the following minimal example:
import abc
class FooClass(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def FooMethod(self):
raise
I know this topic is really old but... That is really a nice question.
It doesn't work because abc can only check for abstract methods during instatiation of types, that is, when type('Derived', (FooClass,), {})
is running. Any setattr done after that is not accessible from abc.
So, setattr wont work, buuut... Your problem of addressing the name of a class that wasn't previously declared or defined looks solvable:
I wrote a little metaclass that lets you use a placeholder "clazz" for accessing any class that will eventually get the method you are writing outside a class definition.
That way you won't get TypeError from abc anymore, since you can now define your method BEFORE instatiating your type, and then pass it to type at the dict argument. Then abc will see it as a proper method override.
Aaand, with the new metaclass you can refer to the class object during that method. And this is super, because now you can use super! =P I can guess you were worried about that too...
Take a look:
import abc
import inspect
clazz = type('clazz', (object,), {})()
def clazzRef(func_obj):
func_obj.__hasclazzref__ = True
return func_obj
class MetaClazzRef(type):
"""Makes the clazz placeholder work.
Checks which of your functions or methods use the decorator clazzRef
and swaps its global reference so that "clazz" resolves to the
desired class, that is, the one where the method is set or defined.
"""
methods = {}
def __new__(mcs, name, bases, dict):
ret = super(MetaClazzRef, mcs).__new__(mcs, name, bases, dict)
for (k,f) in dict.items():
if getattr(f, '__hasclazzref__', False):
if inspect.ismethod(f):
f = f.im_func
if inspect.isfunction(f):
for (var,value) in f.func_globals.items():
if value is clazz:
f.func_globals[var] = ret
return ret
class MetaMix(abc.ABCMeta, MetaClazzRef):
pass
class FooClass(object):
__metaclass__ = MetaMix
@abc.abstractmethod
def FooMethod(self):
print 'Ooops...'
#raise NotImplementedError()
def main():
@clazzRef
def BarOverride(self):
print "Hello, world! I'm a %s but this method is from class %s!" % (type(self), clazz)
super(clazz, self).FooMethod() # Now I have SUPER!!!
derived_type = type('Derived', (FooClass,), {'FooMethod': BarOverride})
instance = derived_type()
instance.FooMethod()
class derivedDerived(derived_type):
def FooMethod(self):
print 'I inherit from derived.'
super(derivedDerived,self).FooMethod()
instance = derivedDerived()
instance.FooMethod()
main()
The output is:
Hello, world! I'm a but this method is from class !
Ooops...
I inherit from derived.
Hello, world! I'm a but this method is from class !
Ooops...