I want to decorate all the methods of my class. I have written a sample small decorator for illustration purpose here.
Decorator:
def debug(func):
msg = func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
print(msg)
return func(*args, **kwargs)
return wrapper
def debugmethods(cls):
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, debug(val))
return cls
Now I want to decorate all the methods of my class. One simple way is to use @debugmethods annotation on top of my class but I am trying to understand two other different approaches for doing so.
a) Overriding __new__
class Spam:
def __new__(cls, *args, **kwargs):
clsobj = super().__new__(cls)
clsobj = debugmethods(clsobj)
return clsobj
def __init__(self):
pass
def foo(self):
pass
def bar(self):
pass
spam = Spam()
spam.foo()
b) Writing metaclass
class debugmeta(type):
def __new__(cls, clsname, bases, clsdict):
clsobj = super().__new__(cls, clsname, bases, clsdict)
clsobj = debugmethods(clsobj)
return clsobj
class Spam(metaclass = debugmeta):
def foo(self):
pass
def bar(self):
pass
spam = Spam()
spam.foo()
I am not sure
__new__
" doesn't work ?__new__
is different in metaclass? Can someone help me understand what am I missing here.
To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function. Here, when we decorate, multiply_together with integer_check, the integer function gets called.
In Python, decorators can be either functions or classes. In both cases, decorating adds functionality to existing functions.
A class decorator is one that decorates a python class object. It works just the same as in the case of a function, except that the decorated object is a class. You can guess that the decorator you are implementing should return a reference to a higher-order class, then.
In Python, the @classmethod decorator is used to declare a method in the class as a class method that can be called using ClassName. MethodName() . The class method can also be called using an object of the class.
To decorate a method in a class, first use the ‘@’ symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function.
Decorators can be implemented in a number of different ways. One useful use-case for decorators involves using them with methods defined in a class. Decorating methods in the classes we create can extend the functionality of the defined method. For example, we could implement a data integrity check, or write the output of the method call to a file.
Decorating Methods defined in a Class With Python 1 Decorating Methods. Decorators provide a convenient and elegant way to add functionality to functions and methods in Pyt... 2 Using the Same Decorator for Multiple Methods. To increase both the usability and the utility of decorators, we can... 3 Summary. More ...
You appear to be confused between __new__
and metaclasses. __new__
is called to create a new object (an instance from a class, a class from a metaclass), it is not a 'class created' hook.
The normal pattern is:
Foo(...)
is translated to type(Foo).__call__(Foo, ...)
, see special method lookups for why that is. The type()
of a class is it's metaclass.The standard type.__call__
implementation used when Foo
is a custom Python class will call __new__
to create a new instance, then call the __init__
method on that instance if the result is indeed an instance of the Foo
class:
def __call__(cls, *args, **kwargs): # Foo(...) -> cls=Foo
instance = cls.__new__(cls, *args, **kwargs) # so Foo.__new__(Foo, ...)
if isinstance(instance, cls):
instance.__init__(*args, **kwargs) # Foo.__init__(instance, ...)
return instance
So Foo.__new__
is not called when the Foo
class itself is created, only when instances of Foo
are created.
You don't usually need to use __new__
in classes, because __init__
suffices to initialise the attributes of instances. But for immutable types, like int
or tuple
, you can only use __new__
to prepare the new instance state, as you can't alter the attributes of an immutable object once it is created. __new__
is also helpful when you want change what kinds of instances ClassObj()
produce (such as creating singletons or producing specialised subclasses instead).
The same __call__
-> __new__
and maybe __init__
process applies to metaclasses. A class Foo: ...
statement is implemented by calling the metaclass to create a class object, passing in 3 arguments: the class name, the class bases, and the class body, as a dictionary usually. With class Spam(metaclass = debugmeta): ...
, that means debugmeta('Spam', (), {...})
is called, which means debugmeta.__new__(debugmeta, 'Spam', (), {...})
is called.
Your first attempt a, setting Spam.__new__
doesn't work, because you are not creating a class object there. Instead, super().__new__(cls)
creates an empty Spam()
instance with no attributes, so vars()
returns an empty dictionary and debugmethods()
ends up doing nothing.
If you want to hook into class creation, then you want a metaclass.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With