ZhangZhihui's Blog  

When we talk about decorating classes, we can either decorate individual methods or we can create a decorator to decorate the whole class.

class MyClass:
    def __init__(self, a):
        self.a = a

    @timer
    def method1(self, x, y):
        print('method1 executing')

    @timer
    def method2(self, x, y):
        print('method2 executing')

    @trace
    def method3(self, x, y):
        print('method3 executing')

Decorating methods is straightforward, it is like decorating functions only. For example, the decorators trace and timer that we have created in our lectures, can be applied to methods of a class to trace or time the method calls. While applying the decorators to methods of a class, we need to keep in mind that a method always receives the current instance as the first argument. The decorators trace and timer that we had created, will work properly with methods also because they are not doing anything special with the first argument but suppose you have a decorator that sanitizes the first argument of a function then you cannot simply apply that decorator to a method, because for methods, the current instance is the first argument, and the first argument that you send when you call the method is actually the second argument.

 

Now, let us see how to create a decorator function to decorate the class as a whole. This type of decorator function will take a class as the argument. It will either modify that class and return it, or it will create a new class and return it. Modifying the class in-place and returning the modified class is more convenient than creating a new class. So, we will see some examples where we will create a decorator that takes a class and returns the modified class.

The syntax for applying the decorator to a class will be the same. You can either use the automatic way or decorate the class manually.

@decorator
class MyClass:
    pass


class MyClass:
    pass
MyClass = decorator(MyClass)

 

First, let us create a very simple decorator, which, when applied to a class, adds a new attribute named author and a line to the docstring of the class.

def my_decorator(cls):
    if cls.__doc__ is None:
        cls.__doc__ = '\nThis is an important class\n'
    else:
        cls.__doc__ += '\nThis is an important class\n'

    cls.author = 'Ryan'
    return cls


@my_decorator
class Person:
    """This is the docstring of Person class"""

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        print(f'Hello, I am {self.name}')


print(Person.__doc__)
print(Person.__dict__)


class Car:
    def __init__(self, model, max_speed):
        self.model = model
        self.max_speed = max_speed

    def show(self):
        print(f'{self.model}, {self.max_speed}')


Car = my_decorator(Car)
print(Car.__doc__)  
print(Car.__dict__)

Output-

This is the docstring of Person class

This is an important class

{'__module__': '__main__' , …………… , 'author': 'Ryan'}

This is an important class

{'__module__': '__main__', ……………… , 'author': 'Ryan'}

Our decorator takes in a class as the argument, so we have named its parameter cls. If the docstring of the class is None, then we assign a string to the docstring; otherwise, we add the string to the docstring. After this we add a new attribute named author to the class. At last, we return the class from the decorator.

We have applied this decorator to the two classes named Person and Car. The Person class has been decorated using the automatic syntax, while the class Car has been decorated using the manual decoration syntax. The output clearly shows the effect of decoration.

 

In our next example, we have created a decorator that adds an attribute named time_of_creation to each instance of the class. Note that in the previous example, we added an attribute to the class object, so we had actually created a class variable. Now, we want to add an attribute to each instance object, so we will be creating an instance variable. This attribute, named time_of_creation, will store the time when the instance object is created. The decorator will also print a message whenever a new instance object is created.

def add_creation_time(cls):
  init = cls.__init__

  def new_init(self,*args, **kwargs):
    from time import ctime
    self.time_of_creation = ctime()
    print(f'A new object of type {cls.__name__ } created')
    init(self,*args, **kwargs)

  cls.__init__ = new_init
  return cls


@add_creation_time  
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        print(f'Hello, I am {self.name}')


@add_creation_time
class Car:
    def __init__(self, model, color):
        self.model = model
        self.color = color

    def show(self):
        print(f'{self.model}, {self.max_speed}')


bob = Person('Bob', 23)
tom = Person('Tom', 66)
x = Car('Audi R8', 'White')
y = Car('Jaguar XJ', 'Black')
print(bob.time_of_creation)
print(tom.time_of_creation)
print(x.time_of_creation)
print(y.time_of_creation)

Output-

A new object of type Person created

A new object of type Person created

A new object of type Car created

A new object of type Car created

Tue Aug 22 16:57:32 2023

Tue Aug 22 16:57:32 2023

Tue Aug 22 16:57:32 2023

Tue Aug 22 16:57:32 2023

Let us understand the code that is written inside the decorator. We have to print the message and add the attribute when an instance is created, so we will have to change the __init__ method. First, we save the __init__ in a separate variable named init. Then we define a function named new_init. The first argument is self and then we place args and kwargs.

After that, we imported the ctime function from the time module. We add a new attribute named time_of_creation. After that, we print the message, that a new object has been created. Next, we call this original __init__ that we have saved in the variable init. The extra work has been done before calling the original init. Then we assign the new_init method to the __init__ attribute of the class. And at last, we return the class object.

We have applied this decorator to the classes Person and Car. The output shows that when instance objects of these classes are created, a message is displayed. A new attribute named time_of_creation gets attached to each instance, and its value is the time when the instance object is created.

 

posted on 2024-07-31 15:28  ZhangZhihuiAAA  阅读(5)  评论(0编辑  收藏  举报