Python基础学习(26)classmethod/staticmethod 装饰器 部分内置魔术方法
一、今日内容大纲
- classmethod staticmethod 装饰器
- 部分内置魔术方法
二、classmethod staticmethod 装饰器
-
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__)
-
staticmethod 装饰器:被装饰的绑定方法会变成一个静态方法
class User: pass @staticmethod # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加了@staticmethod装饰器就可以了 def login(): print('login') # 在函数的内部不会用到self变量,也不会用到cls类 User.login() # 除了前面要加上类名,其他使用方式与普通函数一致
-
小结
能定义到类的内容主要有:
- 静态方法
- 绑定方法
- 类方法:利用 classmethod 装饰器装饰绑定方法实现
- 静态方法:利用 staticmethod 装饰器装饰绑定方法实现
- 属性/实例变量:可直接在
__init__
中定义或者利用 property 装饰器装饰绑定方法实现 - 静态变量
三、部分内置魔术方法
-
总览
魔术方法主要是指 Python 内置的
__func__
形式的方法,主要包括:__new__
__call__
__len__
__eq__
__str__
__repr__
__del__
__enter__
__exit__
-
__call__
方法:object_name()
调用此对象所属类的__call__
方法。class A: def __call__(self, *args, **kwargs): print('----------') obj = A() print(callable(obj)) # True obj() # 调用__call__方法 ----------
-
__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
-
__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) # 白衬衫 # 利用模块的方式实现单例模式是最便捷的方法
-
__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__方法