How do I give a decorator acces to the class of a decorated function
Peter Otten
__peter__ at web.de
Thu Sep 5 09:30:56 EDT 2019
Antoon Pardon wrote:
> On 4/09/19 17:46, Peter Otten wrote:
>> Antoon Pardon wrote:
>>
>>> What I am trying to do is the following.
>>>
>>> class MyClass (...) :
>>> @register
>>> def MyFunction(...)
>>> ...
>>>
>>> What I would want is for the register decorator to somehow create/mutate
>>> class variable(s) of MyClass.
>>>
>>> Is that possible or do I have to rethink my approach?
>> If you are willing to delegate the actual work to the metaclass call:
>>
>> def register(f):
>> f.registered = True
>> return f
>>
>> def registered(name, bases, namespace):
>> namespace["my_cool_functions"] = [
>> n for n, v in namespace.items()
>> if getattr(v, "registered", False)
>> ]
>> return type(name, bases, namespace)
>>
>> class MyClass(metaclass=registered) :
>> @register
>> def foo(self):
>> pass
>> @register
>> def bar(self):
>> pass
>> def other(self):
>> pass
>>
>> print(MyClass.my_cool_functions)
>
> I have been playing with this idea and it looks promising. I was wondering
> about two points.
>
> 1) I guess I can add extra methods to my class through the metaclass by
> having something like the following in the registered function:
>
> def registered(name, bases, namespace):
> namespace["my_cool_functions"] = [
> n for n, v in namespace.items()
> if getattr(v, "registered", False)
> ]
> namespace["__getitem__"] = lambda self, index:
> self.my_cool_functions[index]
Methods are just functions as class attributes, so yes. Problems may arise
with __private attributes and super().
>
> 2) Is it possible to make MyClass automatically a subclass of an other
> class
> through the metaclass?
>
While you can modify `bases` before passing it on to `type` this starts to
get a bit messy. Maybe you need a real metaclass which unlike the registered
function above is shared by the subclasses...
import random
def register(f):
f.registered = True
return f
class Foo: pass
class Bar: pass
class RegisterMeta(type):
def __new__(cls, name, bases, namespace):
namespace["my_cool_functions"] = [
n for n, v in namespace.items()
if getattr(v, "registered", False)
]
return type.__new__(
cls, name,
bases + (random.choice([Foo, Bar]),),
namespace
)
class RegisterBase(metaclass=RegisterMeta):
def __getitem__(self, i):
return self.my_cool_functions[i]
class MyClass(RegisterBase):
@register
def foo(self):
pass
@register
def bar(self):
pass
def other(self):
pass
print(MyClass.my_cool_functions)
print(MyClass()[0])
print(MyClass.my_cool_functions is RegisterBase.my_cool_functions) # False
print(MyClass.__bases__, RegisterBase.__bases__)
...or something else entirely. Can you provide some context?
More information about the Python-list
mailing list