当前位置 博文首页 > 小杨-先生:类的两个装饰器classmethod、staticethod和内置魔术

    小杨-先生:类的两个装饰器classmethod、staticethod和内置魔术

    作者:小杨-先生 时间:2021-05-14 18:22

    一、两个装饰器@classmethod、@staticmethod

    @classmethod:把类中的绑定方法变成一个类方法,cls 就等于类名

    有什么用?

    1、在方法中任然可以引用类中的静态变量

    2、可以不用实例化对象,就直接用类名在外部调用这个方法

    什么时候用?

    1、定义了一个方法,默认传 self ,但这个 self 没有被使用。

    2、并且你在这个方法里用到了当前类名,或者你准备使用这个类的内存空间中的名字的时候

    # 商品打折:
    class Goods:
        __discount = 0.8
    ?
        def __init__(self, original_price):
            self.original_price = original_price
            self.price = self.original_price * self.__discount
    ?
        @classmethod        # 把一个对象的绑定方法改成一个类方法
        def change_discount(cls, count):
            cls.__discount = count  # 相当于Goods.__discount = count
    ?
    ?
    Goods.change_discount(0.6)      # 类方法可以通过类名调用
    ?
    # 实例化一个华为手机对象
    huawei = Goods(20)
    print(int(huawei.price))
    ?
    huawei.change_discount(0.4)     # 类方法可以通过对象调用
    ?
    # 实例化一个苹果手机对象
    apple = Goods(20)
    print(int(apple.price))
    ?
    # 输出
    12
    8

    扩展:

    在掉用类的函数的时候就实例化了一个对象

    import time
    ?
    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    ?
        @classmethod
        def today(cls):
            structure = time.localtime()
            # 在方法中创建一个对象
            obj = cls(structure.tm_year, structure.tm_mon, structure.tm_mday)
            return obj
    ?
    ?
    # 当调用类中的today方法就会返回一个对象(对象属性不用自己填,today方法已经填好了)
    data对象 = Date.today()
    print(data对象.year)
    print(data对象.month)
    print(data对象.day)
    ?
    # 输出
    2021
    5
    4

    @staticmethod:被装饰的方法会成为一个静态方法

    本身是一个普通函数,被挪到类的内部执行,那么直接给这个函数添加 @staticmethod 装饰器就可以了

    在函数的内部即用不到 self 变量,也用不到 cls 类

    class User:
        pass
    ?
        @staticmethod
        def login(user):
            print(f'{user},登录成功')
    ?
    User.login('Alen')   # 类可以调佣
    Bob = User()
    Bob.login('Bob')     # 对象可以调佣
    # 输出
    Alen,登录成功
    Bob,登录成功

    总结:能定义到类中的内容。

    1、静态变量:是个所有的对象共享的变量 ——由对象、类调用

    2、实例变量:是对象的属性变量 ——由对象调用

    3、绑定方法:是个自带self参数的函数 ——由对象调用

    4、类方法:是个自带cls参数的函数 ——由对象、类调用

    5、property属性:是个伪装属性的方法 ——由对象调用,但不加括号

    二、内置魔术方法

    __call__方法:对象 + ( ) 调用这个类中的__call__方法

    class User:
        pass
    ?
        @staticmethod
        def login(user):
            print(f'{user},登录成功')
    ?
    User.login('Alen')   # 类可以调佣
    Bob = User()
    Bob.login('Bob')     # 对象可以调佣
    # 输出
    Alen,登录成功
    Bob,登录成功

    __len__方法:len (对象) 需要实现这个类中的__len__方法

    class A:
        def __init__(self):
            self.lis = [1, 2, 3, 4, 5]
    ?
        def len(self):
            return len(self.lis)
    ?
        def __len__(self):
            return len(self.lis)
    ?
    ?
    a = A()
    print(len(a))       # 可以直接len(a)就可以查看lis里面值的个数
    print(a.len())      # 不然就要使用类中的len方法才可以查看
    ?
    # 输出
    5
    5
    ?
    # -------------------------------------------------------------------------------------
    # 扩展:也可以使用自定义的函数
    ?
    class A:
        def __init__(self, count):
            self.count = count
    ?
        def __square__(self):
            value = self.count ** 2
            return value
    ?
    def square(count):
        return A.__square__(count)
    ?
    a = A(5)
    ?
    print(square(a))
    ?
    # 输出
    25

    __new__方法:实例化的时候会先执行__new__方法用来创建一个对象需要的空间

    class A:
        def __new__(cls, *args, **kwargs):
            o = super().__new__(cls)
            print(o)                    # 内存地址和 self 的内存地址一样
            print('执行了__new__方法')
            return o
    ?
        def __init__(self):
            print(self)
            print('执行了__init__方法')
    ?
    A()     # 实例化对象
    ?
    # 输出
    <__main__.A object at 0x014E0970>
    执行了__new__方法
    <__main__.A object at 0x014E0970>
    执行了__init__方法

    __new__方法的设计模式:————>单例模式

    一个类,从头到尾,只会创建一次 self 的空间

    例如:我有一辆车(有且只有一辆),小红要拿去用,我就给她了,她拿去随便怎么改装,换个红色车漆,换蓝色车轮都可以。但是小明又过来找我要用车,那我就去把我的车给拿回来在给小明,也随小明随便改装。

    class Car:
        __attribute = None
    ?
        def __new__(cls, *args, **kwargs):
            if cls.__attribute is None:         # 当第一次开空间的时候__attribute肯定是空的
                # 所以就会开一个新的空间。并把__attribute给赋值,那么以后的都不是第一次了。
                cls.__attribute = super().__new__(cls)  
            return cls.__attribute                      # 把新空间返回给self
    ?
        def __init__(self, car_paint, cartwheel_colour):
            self.car_paint = car_paint
            self.cartwheel_colour = cartwheel_colour
    ?
    ?
    xiao_hong = Car('红色的车漆', '蓝色的车轮')
    print('小红的内存地址:', xiao_hong)    # 他们的内存地址都是一样的
    print(xiao_hong.car_paint)          # 红色的车漆————>小红刚拿到的时候还是红色的
    xiao_ming = Car('白色的车漆', '黑色的车轮')
    print('小名的内存地址:', xiao_ming)
    print(xiao_hong.car_paint)          # 白色的车漆————>小明在拿过来,把颜色给改成白的了
    print(xiao_ming.car_paint)          # 白色的车漆————>小红的也变了,应为他们都是同一个车
    ?
    # 输出
    小红的内存地址: <__main__.Car object at 0x01A70A60>
    红色的车漆
    小名的内存地址: <__main__.Car object at 0x01A70A60>
    白色的车漆
    白色的车漆

    __str____repr__方法:

    str方法:帮助我们在打印或展示对象的时候更加直观的显示对象内容

    1、在打印一个对象的时候,调用__str__方法

    2、在 %s 拼接一个对象的时候,调用__str__方法

    3、在str一个对象的时候,调用__str__方法

    当打印一个对象的时候,希望这个对象显示的值是什么,那么你就必须在这个对象内部实现一个__str__ ,这样的话你就能做到,你在打印这个对象的时候你就能查看到他的内容是多少(查看的内容由你自己在 str 中定义),不然就只能答应一堆内存地址。

    repr方法:是 str 的备胎(有str的时候打印str,没有就打印rper),同时 %r 和 repr 有合作关系

    class User:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    ?
        def __str__(self):
            return self.name
    ?
        def __repr__(self):
            return self.age
    ?
    ?
    Bob = User('鲍勃', '18')
    ?
    print(Bob)  # 现在就可以直接打印对象了,——不设置str方法,打印的就是是一堆内存地址,优先打印 str 方法
    ?
    print('我的名字是%s' % Bob)  # 在 %s 拼接一个对象的时候,调用__str__方法
    print('我的年龄是%r' % Bob)  # 在 %r 拼接一个对象的时候,调用__repr__方法
    ?
    # 输出
    鲍勃
    我的名字是鲍勃
    我的年龄是18
    bk