classmethod 装饰器:对装饰的绑定方法会变成类方法
为了了解 classmethod 装饰器到底有什么作用,我们继续使用之前举过的售卖苹果的例子:
class Goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discountapple = Goods()print(apple.price) # 4.0
这时我们如果想修改折扣为 6 折,可以进行下列修改:
# 修改折扣 0.6class 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_discountGoods.change_discount(0.6) # 可以直接使用方法名修改,无需先实例化apple2 = Goods()print(apple2.price) # 3.0
所以一般什么情况下,才使用 classmethod 装饰器呢?
我们来举一个实际应用中的例子:定义一个类Date
,内部有年、月、日等实例变量,Date.today()
可以返回一个存储当天年月日的对象。
import timeclass 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 objtime_obj = Date.today()print(time_obj.__dict__)
staticmethod 装饰器:被装饰的绑定方法会变成一个静态方法
class User: pass @staticmethod # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加了@staticmethod装饰器就可以了 def login(): print('login') # 在函数的内部不会用到self变量,也不会用到cls类User.login() # 除了前面要加上类名,其他使用方式与普通函数一致
小结
能定义到类的内容主要有:
__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)) # Trueobj() # 调用__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)) # 3print(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 = pantsb1 = 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 monthslst = [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__方法
联系客服