Python基础学习(26)classmethod/staticmethod 装饰器 部分内置魔术方法

Python基础学习(26)classmethod/staticmethod 装饰器 部分内置魔术方法

一、今日内容大纲

  • classmethod staticmethod 装饰器
  • 部分内置魔术方法

二、classmethod staticmethod 装饰器

  1. classmethod 装饰器:对装饰的绑定方法会变成类方法

    为了了解 classmethod 装饰器到底有什么作用,我们继续使用之前举过的售卖苹果的例子:

    class Goods:
        __discount = 0.8
        def __init__(self):
            self.__price = 5
            self.price = self.__price * self.__discount
    
    apple = Goods()
    print(apple.price)  # 4.0
    

    这时我们如果想修改折扣为 6 折,可以进行下列修改:

    # 修改折扣 0.6
    class Goods:
        __discount = 0.8
        def __init__(self):
            self.__price = 5
            self.price = self.__price * self.__discount
        def change_discount(self, new_discount):  # self参数没有利用
            Goods.__discount = new_discount
    # 虽然成功修改了,但是好像有点儿不和逻辑
    apple = Goods()
    apple.change_discount(0.6)
    apple2 = Goods()
    print(apple2.price)  # 3.0
    

    可以看到,虽然我们能够成功实现修改购物商场的全场折扣,但是内置方法中的 self 参数是完全没有利用的,且修改全场折扣要利用到个别对象的绑定方法,也就是说我们如果想要修改整个全场折扣,还要首先实例化一个对象,再依靠对象来修改折扣。这样显然是不符合正常逻辑的,所以我们使用 classmethod 装饰器来将此方法定义为一个类方法:只可以被类调用(实际使用其实也可以被对象调用,但是一般不可以这么使用):

    # 定义了一个方法,默认传self,但这个self没被使用,
    # 这时我们使用classmethod装饰器
    class Goods:
        __discount = 0.8
        def __init__(self):
            self.__price = 5
            self.price = self.__price * self.__discount
        @classmethod
        def change_discount(cls, new_discount):  # 将self换成类名传入
            cls.__discount = new_discount
    
    Goods.change_discount(0.6)  # 可以直接使用方法名修改,无需先实例化
    apple2 = Goods()
    print(apple2.price)  # 3.0
    

    所以一般什么情况下,才使用 classmethod 装饰器呢?

    • 定义了一个方法,默认传 self,但是这个 self 没有被使用;
    • 这个方法里用到了当前的类名,或者你准备使用这个类的内存空间中的名字的时候;

    我们来举一个实际应用中的例子:定义一个类Date,内部有年、月、日等实例变量,Date.today()可以返回一个存储当天年月日的对象。

    import time
    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
        @classmethod
        def today(cls):
            obj = cls(time.localtime().tm_year, time.localtime().tm_mon, time.localtime().tm_mday)
            return obj
    
    time_obj = Date.today()
    print(time_obj.__dict__)
    
  2. staticmethod 装饰器:被装饰的绑定方法会变成一个静态方法

    class User:
        pass
        @staticmethod  # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加了@staticmethod装饰器就可以了
        def login():
            print('login')
            # 在函数的内部不会用到self变量,也不会用到cls类
    
    User.login()  # 除了前面要加上类名,其他使用方式与普通函数一致
    
  3. 小结

    能定义到类的内容主要有:

    • 静态方法
    • 绑定方法
    • 类方法:利用 classmethod 装饰器装饰绑定方法实现
    • 静态方法:利用 staticmethod 装饰器装饰绑定方法实现
    • 属性/实例变量:可直接在__init__中定义或者利用 property 装饰器装饰绑定方法实现
    • 静态变量

三、部分内置魔术方法

  1. 总览

    魔术方法主要是指 Python 内置的__func__形式的方法,主要包括:

    • __new__
    • __call__
    • __len__
    • __eq__
    • __str__
    • __repr__
    • __del__
    • __enter__
    • __exit__
  2. __call__方法:object_name()调用此对象所属类的__call__方法。

    class A:
        def __call__(self, *args, **kwargs):
            print('----------')
    
    obj = A()
    print(callable(obj))  # True
    obj()  # 调用__call__方法 ----------
    
  3. __len__方法:len(object_name)调用此对象所属类的__call__方法。

    class Cls:
        def __init__(self, name):
            self.name = name
            self.students = []
        def __len__(self):
            return len(self.students)
    py22 = Cls('py22')
    py22.students.append('duxiangxi')
    py22.students.append('taibai')
    py22.students.append('dazhuang')
    print(len(py22.students))  # 3
    # 这两者是等价的
    print(len(py22))  # 3
    print(py22.__len__())  # 3
    
  4. __new__方法:__new__方法就是在实例化之前所完成的操作,实例化的过程会先执行__new__方法,再执行__init__方法,实例化过程返回的对象也就是__new__返回的对象。

    # __new__
    class A:
        def __new__(cls, *args, **kwargs):
            o = super().__new__(cls)
            # o = object.__new__(cls)  # 因为父类是object也可以这么写
            print('new', o)
            return o
    
        def __init__(self):
            print('init', self)
    
    
    A()
    
    
    # new <__main__.A object at 0x0000016947968B38>
    # init <__main__.A object at 0x0000016947968B38>
    
    # 实例化的时候
    # 先创建一块对象空间,有一个指针能指向类 -> 由__new__完成
    # 调用__init__
    
    # __new__是一个构造方法
    
    
    # 为什么要自己实现一个__new__呢?
    
    
    # 设计模式 --> 单例模式
    # 一个类 从头到尾 只会创建一次self的空间
    class Baby:
        __instance = None
    
        def __new__(cls, *args, **kwargs):
            if cls.__instance is None:
                cls.__instance = super().__new__(cls)
            return cls.__instance
    
        def __init__(self, cloth, pants):
            self.cloth = cloth
            self.pants = pants
    
    
    b1 = Baby('红毛衣', '绿皮裤')
    b2 = Baby('白衬衫', '黑豹纹')
    print(b1)  # <__main__.Baby object at 0x0000022322CFDB00>
    print(b2)  # <__main__.Baby object at 0x0000022322CFDB00>
    print(b1.cloth)  # 白衬衫
    print(b2.cloth)  # 白衬衫
    
    
    # 利用模块的方式实现单例模式是最便捷的方法
    
  5. __str____repr__方法:在打印一个对象或者len(object_name)的时候,调用对象内部的__str__方法,如果对象内部不存在__str__方法则会调用备用的__repr__方法。

    # 2.__str__  __repr__
    class Course:
        def __init__(self, name, price, period):
            self.name = name
            self.price = price
            self.period = period
    
        def __str__(self):  # 它只能返回str数据类型
            return ','.join([self.name, str(self.price), self.period])
    
    
    python = Course('python', 21800, '6 months')
    linux = Course('linux', 19800, '3 months')
    mysql = Course('mysql', 12800, '4 months')
    go = Course('python', 21800, '4 months')
    print(go)  # python,21800,4 months
    lst = [python, linux, mysql, go]
    for course in lst:
        print(course)
    # for index, c in enumerate(lst, 1):
    #     print(index, c.name, c.price, c.period)
    # num = int(input('>>>'))
    # course = lst[num-1]
    # print(f'you choose {course.name} {course.price}.')
    # 在打印一个对象的时候,调用__str__方法
    # 在str一个对象的时候,调用__str__方法
    
    # 当我们打印一个对象的时候, 用%s进行字符串批结,或者str(对象) 总是调用这个对象的__str__方法
    # 如果找不到__str__就调用__repr__方法
    
    # __repr__不仅仅是__str__的替代品,还有自己的功能
    # 用%r进行字符串拼接 或者用repr(对象)的时候总是调用这个对象的__repr__方法
    
上一篇:python的classmethod修饰符


下一篇:Aop进行日志处理