前言:
面向对象的三大特性:
1.封装
2.继承
3.多态(python中不存在的,而是鸭子类型)
在python中,一切皆对象,对象是某个东西。所以,顾名思义,类当然也是对象,连一个数字、字符串都是对象。
面向对象编程,是一种哲学,编程的哲学、编程的思维。很虚的,只是指导你如何去思考。
面向对象编程,就是将数据与函数绑定到一起,进行封装。
一、面向对象
先用现实生活中的一个例子引入:
见过月饼没?(没见过的出门左拐)
要做出一个月饼,首先得要做出月饼的模子,有了这个模子,就可以照着这个模子无限做月饼。
类:就相当于模子。
对象:就是做好的一个个的月饼。(python中,对象是可变的)
二、类、实例
(一).类(模子)
类有它自己的特征(属性)、行为(方法)。
拿“犬类”来理解:
狗的特征(属性):毛色、体重、血统……
狗的行为(方法):吃东西、奔跑、汪汪叫……
特征说白了就是属性,在编程中实则就是变量;行为就是方法,在编程中实则就是函数。
但是,方法和函数是有区别的:与类有着特定关联的函数,才被叫方法。
这里引入一个isinstance()这个内置函数:
class Dog: pass class Cat: pass d1 = Dog() # 实例化一个类。加括号,就像调用函数一样 d2 = Dog() c1 = Cat() print(d1 is d2) # False。看清楚了,这里是 is,身份运算符,判断id是不是一样的。d1、d2是两个不同的实例,当然ID不一样了 print(isinstance(d1, Dog)) # True print(isinstance(c1, Cat)) # True print(isinstance(c1, Dog)) # False print(isinstance(c1, (Dog, Cat))) # True,判断实例是不是属于某一个类,一个为真即为真
(二).实例
模子创建出来的一个具体的东西。月饼就是由模子做出来的,月饼就是个具体的东西。
每一个实例之间互不相干,可以实例化无限个(只要内存够用)。
class Cat: pass c1 = Cat() # 一定要加括号,就像调用函数一样
(三).举例理解
老虎、狮子、猎豹,都是猫科动物这一类的。是真实存在的,是猫科动物的具体实例的对象。
而类,则是猫科动物的模版,因为不存在一个叫猫科动物的动物。
三、属性封装
class Cat: var1 = "一只猫" print(Cat.var1) # 一只猫。直接通过 "类名.变量名" 来访问 Cat.var2 = "两只猫" # 只有python可以这么做 print(Cat.var2) # 两只猫 c1 = Cat() # 把Cat类实力化 print(c1.var1) # 一只猫 print(c1.var2) # 两只猫
查找变量,首先会去实例中找,没找到再去类中找。
(一)."属性操作"内置函数
getattr()/hasattr()/setattr()/delattr() 其实针对的是变量空间
这四个方法可以在程序运行时决定操作的属性。代码运行之后,决定赋什么属性、什么值。(不是写代码时就写死了,而是运行时可以决定)
此外,hasattr()还可以避免因为属性没有而导致报错。
(二).常用的特殊属性
(1).__name__
以后web开发中,写"路由"的时候会用到__name__,url配置。
路径、网络、函数的名字,一一对应。
返回的是一个字符串。是对象本身的名字,类是类的名字,函数是函数的名字。
class A: pass def bb(): pass print(A.__name__) # A print(type(A.__name__)) # <class 'str'> print(bb.__name__) # bb print(type(bb.__name__)) # <class 'str'>
(2).__class__
只要有实例,就能得到类。不是类的名称,是类自己本身的对象,而不是名字。
相当于再得到一个类对象。
class A: pass a = A() b = a.__class__ print(b) # <class '__main__.A'>
(3).__doc__
写代码要有写注释的习惯,不然过了1个星期,你自己都不知道你写的是啥东西了。
def jiec(n): """ 这是一个计算阶乘的递归函数 :param n: 对哪个数进行阶乘 :return: 阶乘的结果 """ if n == 1: # 出口 return 1 else: return jiec(n - 1) * n # 自己调用自己 print(jiec.__doc__) """ 这是一个计算阶乘的递归函数 :param n: 对哪个数进行阶乘 :return: 阶乘的结果 """ print(help(jiec)) """ Help on function jiec in module __main__: jiec(n) 这是一个计算阶乘的递归函数 :param n: 对哪个数进行阶乘 :return: 阶乘的结果 None """
可以把这个多行注释看作是"文档注释",每个类、方法只能有一个。
多行注释是存放在__doc__中,返回的是文档字符串,把多行注释返回了。
(4).__dict__
查看实例中封装的属性。类中的属性返回的是一个一大串底层的字典格式的对象。
四、self
在编写类的代码时,它是常客。
self它是一个关键字参数,首要参数(这里就记住:必须加),它是标记给哪个类,给每一个实例贴上标签。指的是对象本身。
如果不加这个self,那么有很多实例的时候,python解释器就分不清是哪一个实例了。python底层是用self来区分每一个实例。
调用时不需要写self这个参数,python底层已自动为我们实现了,python解释器会自动传递。
(一).引入魔法方法:__init__()
先解释一下什么叫魔法方法?被双下划线包裹起来的方法。在特定的情况被触发。就像陷阱,先布置好,一旦有踩到了就触发了这个陷阱。
阅读如下代码,例1:
class Account: def __init__(self, name, number, money): self.name = name self.number = number self.money = money a = Account("Tuple", "123456", 8888) # 实例化
__init__() 初始化的作用,实例化时自动调用(无需手动),初始化属性。用于对象一被创建时,就需要拥有属性的场合,让对象有一些指定的属性。
例如,一个人一出生就拥有血型、指纹……
"例1"中 self.name=name 这条在__init__()中的代码,就是让对象有了这个属性。self指对象本身。
注意:当 __init__() 的括号中有必备参数时,在实例化时,就必须传参数。
看着上述"例1"的代码,理解下图:(在编写类的代码时,如果不写self,那么实例化的时候,python根本就不知道是哪一个类传过去的)
(二).其他魔法方法
(1).__del__() 释放类时被调用。
python底层有自己的垃圾回收机制。不过当人为 del object 手动删除时,就触发了__del__()。
这里引入一个例子,此例向我们展示了python被称为高级语言的特性之一:"垃圾回收机制",
class A: def __del__(self): print("被销毁了") def func(): a = A() # a是在函数调用的时候实例化的 # 当函数调用结束的时候,里面的对象被销毁 func() # 调用完了都会被销毁 # 不销毁的话,当很多次调用的时候。a实例在内存中越来越多,最后导致内存溢出,你的电脑瘫痪 """ 运行结果: 被销毁了 为啥?又没去删除过对象,为什么被销毁了?看代码的注释去! """
(2).__str__() 向使用者提供尽可能简洁且有用的信息。针对使用者。print()的时候,就是触发了这个魔法方法。
(3).__repr__() 作用与__str__()很相似。只是这个魔法方法针对开发者,有时用来找BUG的
(4).str没有的时候,会去找repr
五、基础OOP的案例
(一).烤红薯
""" 小应用:烤红薯 属性: cookedlevel int : 0-3 表示生的;超过3 表示半生不熟;超过5 表示已经烤好了;超过8 表示烤焦了 cookedstring string : 描述红薯的生熟程度 coodiment : 调料 行为: cook() 把红薯烤一段时间 addcondiments() 给红薯添加配料 用到的魔法方法: __init__ 设置默认属性 __str__ 让print的结果更加好看 """ class SweetPotato: """这是烤红薯的类""" # 定义初始化方法 def __init__(self): self.cooklevel = 0 self.cook_string = "生的" self.condiments = [] # 定制print时显示的内容 def __str__(self): msg = self.cook_string + "红薯" if len(self.condiments) > 0: msg = msg + "(" for i in self.condiments: msg = msg + i + "," msg = msg.strip(",") # 去掉左右两边的逗号 msg = msg + ")" return msg # 烤红薯方法 def cook(self, time): self.cooklevel += time if self.cooklevel > 8: self.cook_string = "烤成灰了" elif self.cooklevel > 5: self.cook_string = "烤熟了" elif self.cooklevel > 3: self.cook_string = "半生不熟" else: self.cook_string = "生的" # 加调味品 def add_condiments(self, condiment): self.condiments.append(condiment) sp = SweetPotato() # 实例 print("有一个红薯还没有烤。", "level:", sp.cooklevel, sp.cook_string, "。调料:", sp.condiments) print("接下来开始烤红薯了") print("已经烤了4分钟") sp.cook(4) print(sp) print("我又烤了3分钟") sp.cook(3) print(sp) print("开始放调料了,我放了芥末") sp.add_condiments("芥末") print(sp) print("我口味比较重,我想放点老干妈") sp.add_condiments("老干妈") print(sp) print("我又烤了5分钟") sp.cook(5) print(sp) print("烤成灰了也要吃,加杯脉动") sp.add_condiments("脉动") print(sp) """ 运行结果: 有一个红薯还没有烤。 level: 0 生的 。调料: [] 接下来开始烤红薯了 已经烤了4分钟 半生不熟红薯 我又烤了3分钟 烤熟了红薯 开始放调料了,我放了芥末 烤熟了红薯(芥末) 我口味比较重,我想放点老干妈 烤熟了红薯(芥末,老干妈) 我又烤了5分钟 烤成灰了红薯(芥末,老干妈) 烤成灰了也要吃,加杯脉动 烤成灰了红薯(芥末,老干妈,脉动) """
(二).房间里摆放家具
""" 你有一个房间,往房间里摆放家具。 用OOP思想编写代码! """ class Room: def __init__(self, area): self.area = area # 房间的总面积 self.light = "on" # 默认让房间的灯是亮着的 self.contains_item = [] # 家具列表 def __str__(self): msg = "当前房间可用面积为:{},".format(str(self.area)) if len(self.contains_item) > 0: # 房间里有家具 msg = "{}容纳的物品有:{}".format(msg, "、".join([i.get_name() for i in self.contains_item])) # 预设方法 : get_name() 获取家具的名称 # self.contains_item中,放的是Bed类的实例,所以可以直接 ".函数名()" 来操作 # print(self.contains_item) # [<__main__.Bed object at 0x003E8430>,...] return msg def accommodate_item(self, item): # 往房间中添加家具 need_area = item.get_used_area() # 预设函数 : 已使用多少面积 if self.area > need_area: # 总面积大于家具的面积,可以往房间里添家具 self.contains_item.append(item) self.area -= need_area # 家具放进房间了,房间总面被占用了,相应减少可以使用的面积 print(""{}"已放进房间里了".format(item.get_name())) else: print("房间面积不够了,塞不进去了!") class Bed: """这里以 "床" 为例""" def __init__(self, area, name="床1"): self.area = area # 床有面积 self.name = name # 床有品牌 def __str__(self): return "床的面积为:{}".format(str(self.area)) def get_used_area(self): return self.area def get_name(self): return self.name room = Room(20) print(room) b1 = Bed(3) print(b1) room.accommodate_item(b1) # 往房间里添床了 # 传过去的是Bed类的实例 print(room) print() # 为了看得清楚 b2 = Bed(5, "席梦思") print(b2) room.accommodate_item(b2) # 再添一张床 print(room) """ 运行结果: 当前房间可用面积为:20, 床的面积为:3 "床1"已放进房间里了 当前房间可用面积为:17,容纳的物品有:床1 床的面积为:5 "席梦思"已放进房间里了 当前房间可用面积为:12,容纳的物品有:床1、席梦思 """
(三).文字版CS
""" 文字版 CS 1. 人类 属性 : 姓名 血量 持有的枪 方法 : 安子弹 安弹夹 拿枪(持有抢) 开枪 2. 子弹类 属性 : 杀伤力 方法 : 伤害敌人(让敌人掉血) 3. 弹夹类 属性 : 容量(子弹存储的最大值) 当前保存的子弹 方法 : 保存子弹(安装子弹的时候) 弹出子弹(开枪的时候) 4. 枪类 属性 : 弹夹(默认没有弹夹,需要安装) 方法 : 连接弹夹(保存弹夹) 射子弹 """ # 人类 class Ren: # 初始化方法 def __init__(self, name): self.name = name self.xue = 100 self.qiang = None # 魔术方法 def __str__(self): return self.name + "剩余血量为:" + str(self.xue) def anzidan(self, danjia, zidan): danjia.baocunzidan(zidan) def andanjia(self, qiang, danjia): qiang.lianjiedanjia(danjia) def naqiang(self, qiang): self.qiang = qiang def kaiqiang(self, diren): self.qiang.she(diren) def diaoxue(self, shashangli): self.xue -= shashangli # 弹夹类 class Danjia: def __init__(self, rongliang): self.rongliang = rongliang self.rongnaList = [] def __str__(self): return "弹夹当前的子弹数量为:" + str(len(self.rongnaList)) + "/" + str(self.rongliang) def baocunzidan(self, zidan): if len(self.rongnaList) < self.rongliang: self.rongnaList.append(zidan) def chuzidan(self): # 判断当前弹夹中是否还有子弹 if len(self.rongnaList) > 0: # 获取最后压入到单夹中的子弹 zidan = self.rongnaList[-1] # 每一次调用都会用掉一发子弹,所以要删除列表中的最后一个元素 self.rongnaList.pop() return zidan else: return None # 子弹类 class Zidan: def __init__(self, shashangli): self.shashangli = shashangli def shanghai(self, diren): diren.diaoxue(self.shashangli) # 枪类 class Qiang: def __init__(self): self.danjia = None def __str__(self): if self.danjia: return "M4A1当前有弹夹" else: return "M4A1当前没有弹夹" def lianjiedanjia(self, danjia): if not self.danjia: self.danjia = danjia def she(self, diren): zidan = self.danjia.chuzidan() if zidan: zidan.shanghai(diren) else: print("没有子弹了,需要更换弹夹") police = Ren("德国边防第九大队") danjia = Danjia(20) print(danjia) i = 0 while i < 20: zidan = Zidan(20) police.anzidan(danjia, zidan) i += 1 print(danjia) qiang = Qiang() print(qiang) police.andanjia(qiang, danjia) print(qiang) diren = Ren("凤凰战士") print(diren) police.naqiang(qiang) police.kaiqiang(diren) print(diren) print(danjia) police.kaiqiang(diren) print(diren) print(danjia) """ 运行结果: 弹夹当前的子弹数量为:0/20 弹夹当前的子弹数量为:20/20 M4A1当前没有弹夹 M4A1当前有弹夹 凤凰战士剩余血量为:100 凤凰战士剩余血量为:80 弹夹当前的子弹数量为:19/20 凤凰战士剩余血量为:60 弹夹当前的子弹数量为:18/20 """
六、继承
就是相同的类,取他们的共性。(也可以说是派生)。抽象出一个更抽象的类,放公共代码。
(一).设计思想:
单继承:从上往下,传统分类思想。
基于多继承的Mix-in的思路:拼积木,组装。就像拼四驱车,由马达、轴承等等组成一辆完整的四驱车。(代码量一多,拼积木容易把自己绕晕)。一般,Mix-in类是继承的终点。
(二).继承搜索的顺序
先找子类 -> 再找父类 -> 最后找object
注意:不是父类空间的复制。
寻找多继承的轨迹的方式:__mro__ 属性。
(三).super()
用途:当子类需要重写父类时,但还需要用到父类。super()必须加上。
父类中所有的一切,子类都可以直接拿来用。如果父类无法满足子类的需求,在不改变父类作用的前提下,在子类中重写父类方法时,super()得要加上。
因为重写了子类,父类中的属性、方法就会被覆盖了,不会再去找父类中的属性、方法了。所以需要super()手动去找一下。
super()了,后面的__init__()中,self也不用了。
python解释器会自动去找它的父类。super()是通过mro查找的。
父类中有多少参数,super().__init__()也得有同样参数。
super().__init__()拿来的是父类的属性,父类中有哪些属性,都必须要统统写进来。而子类自有的属性就在init中定义。然后传参的时候,就按子类定义的参数传实参就可以了。
(四).练习:
定义一个猫咪的父类,再从父类中分出不同种类的猫
""" 定义一个猫咪的父类, 再从父类中分出不同种类的猫 """ class Cat: """猫咪的父类""" def __init__(self, color="", weight=10, age=1): self.color = color # 颜色 self.weight = weight # 体重 self.age = age # 年龄 def __str__(self): return "{},体重:{},年龄:{}岁".format( self.color, str(self.weight), str(self.age)) class GarfieldCat(Cat): """加菲猫""" def __init__(self, color, weight, age, hungry): super().__init__(color, weight, age) self.hungry = hungry if self.hungry: self.hungry = "我是吃货" else: self.hungry = "我才不是吃货" def speak(self): print("我是加菲猫,我会说话!") def __str__(self): return "{},体重:{},年龄:{}岁,{}".format( self.color, str(self.weight), str(self.age), self.hungry) class Hellokitty(Cat): """Hello kitty""" def iam_cute(self): print("我是Hello kitty,我会卖萌") garfield = GarfieldCat("棕色", 30, 10, True) print(garfield) garfield.speak() print() # 换行,为了看得清楚 hk = Hellokitty("粉色", 20, 1) print(hk) hk.iam_cute() """ 运行结果: 棕色,体重:30,年龄:10岁,我是吃货 我是加菲猫,我会说话! 粉色,体重:20,年龄:1岁 我是Hello kitty,我会卖萌 """
(五).补充:python不具备多态,python是假多态。(python中是鸭子类型)
鸭子类型:如果走起路来像鸭子,或者叫起来像鸭子,那就是鸭子!比如那天我学嬴政穿着龙袍,那么认为我就是皇帝。
(六).广度优先遍历、深度优先遍历。广度:类似就近原则。深度:一条路找到底。类名.mro()就可以看到寻找的轨迹,既不是广度也不是深度,而是C3算法推导出来的。
广度的寻找轨迹:找BC,再去B里面的DE,最后是object
深度的寻找轨迹:B -> D -> E -> C
补充一个案例:(由于冲突导致的不能继承)
""" 用个生动形象的比喻 """ class A: # 爸爸 pass class B: # 妈妈 pass class C(A, B): # 儿子。先继承了爸爸的小JJ pass class D(B, A): # 女儿。先继承了妈妈的... pass # 一旦运行下面代码就会报错 class E(C, D): # 儿子和女儿能结婚吗? pass """ Traceback (most recent call last): File "D:/python_local/test1.py", line 21, in <module> class E(C, D): TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B """ """ C先继承了A 然后B, D先继承了B 然后A, 那么E的寻找轨迹就发生了冲突,到底先找A还是先去找B?然后就报错了。 C3算法找不到继承轨迹了。 """
七、装饰器
装饰器(名词):装饰某个东西。给一个现有的函数增加功能,可以反复使用,你装饰什么函数,那个函数就增加功能。
装饰(动词):指向了一个新的函数,除了调用原来的函数外,还会做点别的事。函数名还是原来的函数名,指向的却是新的函数,这个新的函数,会调用原来的函数,也可以做些别的事。
一个装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数。
(一).使用装饰器的场景
(1).提供一个验证功能的时候
(2).统计函数执行时间
(3).日志模块,自动打印日志
(4).执行函数前的预备处理
(5).函数执行后的一些清理功能
(二).开放封闭原则
写代码要遵循 "开放封闭" 的原则。简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展。
封闭:已实现的功能代码。
开放:对扩展开放。
(三).装饰器练习
(1).
一个简单的装饰器,"例1":
def i_label(fn): def add_i(): return "<i>{}</i>".format(fn()) return add_i def b_label(fn): def add_b(): return "<b>{}</b>".format(fn()) return add_b @i_label def sentence1(): return "第一句话" # print("第一句话") <i>None</i> # print()返回的是None @b_label def sentence2(): return "第二句话" @i_label @b_label def sentence3(): return "第三句话" print(sentence1()) print(sentence2()) print(sentence3()) """ 先会去执行b_label,执行完后,返回出一个新的函数,这个新函数包含了刚才代码执行后的结果。 然后再去执行i_label,i_label调用执行的是刚才生成的新函数了,新函数中有b_label执行后的结果,才能拼接在一起。然后返回。 """ """ 运行结果: <i>第一句话</i> <b>第二句话</b> <i><b>第三句话</b></i> """
(2).
带参数的装饰器,"例2":
""" 满足500元的商品可以打9折。 """ def discount(price): def exchange(x): # x是original_price if price(x) >= 500: # price()是调用entirely() return price(x) * 0.9 else: return price(x) return exchange @discount def entirely(original_price): return original_price print("合计:{}".format(entirely(600))) # 合计:540.0
(3).
大装饰器,"例3":
import time from functools import wraps """ 装饰器从里到外起作用的 """ def what_you_want(command): if command == "get_sum": def get_sum(func): @wraps(func) def sum_inner(*args, **kwargs): values = func(*args, **kwargs) print("打印两数之和:{}".format(sum(values))) return values return sum_inner return get_sum elif command == "get_cost_time": def get_cost_time(func): @wraps(func) def time_inner(*args, **kwargs): start_time = time.time() time.sleep(0.5) values = func(*args, **kwargs) end_time = time.time() print("耗时{}秒".format(str(end_time - start_time))) return values # 如果没有返回,get_sum这个装饰器中就会报错 return time_inner return get_cost_time """ 所有的装饰器装饰好了再运行。 此案例中,其中一个装饰器需要依赖那个values。 如果装饰完后没有得到values的返回值,就会报错。 """ @what_you_want("get_sum") # 如果没有得到values,这条语句必须写再下面语句的后面 @what_you_want("get_cost_time") def my_func(a, b): return a, b my_func(10, 20) """ 运行结果: 耗时0.5000286102294922秒 打印两数之和:30 """
由"例3"中可看出:装饰器可以叠加使用,若多个装饰器同时装饰一个函数,那么装饰器的调用顺序和语法糖的声明顺序相反。
(4).
阅读以下代码
from functools import wraps def is_login(func): @wraps(func) def _wrapper(*args, **kwargs): print(kwargs) if (kwargs["username"] == "abc") and (kwargs["password"] == "123"): return func(*args, **kwargs) else: return "unauth." return _wrapper @is_login def login(username=None, password=None): return "welcome to use." print(login())
运行报错了,报错如下:
为什么?
kwargs["username"]的判断需要值,只有在func(*args,**kwargs)的时候才会拿到值。也就是需要实际调用func(*args,**kwargs)的时候才会拿到参数。
如果调用login()的时候不给参数赋值,那么在装饰器中执行的时候就是空字典了。想要判断,就必须要给他值,不然就是空字典。
修改代码,并附上了注释,运行通过:
from functools import wraps def is_login(func): @wraps(func) def _wrapper(*args, **kwargs): print(kwargs) if (kwargs["username"] == "abc") and (kwargs["password"] == "123"): return func(*args, **kwargs) # 返回了func()的执行结果 else: return "unauth." return _wrapper # 一层一层,从里往外,返回出去 @is_login def login(username=None, password=None): return "welcome to use." # print(login()) """ kwargs["username"]的判断需要值,只有在func(*args,**kwargs)的时候才会拿到值。 也就是,需要实际调用func(*args,**kwargs)的时候才会拿到参数。 如果调用login()的时候不给参数赋值,那么在装饰器中执行的时候就是空字典了。 想要判断,就必须要给他值,不然就是空字典。 """ print(login(username="abc", password="123"))
(四).wraps的作用
装饰器在实现的时候,被装饰后的函数其实已经是另外一个函数了,函数名等函数属性会发生改变。
Python的functools包,提供了wraps这个装饰器来消除这样的副作用。最好在实现之前都加上functools的wrap,它能保留原有函数的名称和文档注释。
第一部分代码:
def decorate(func): def _wrapper(*args, **kwargs): print("test content.") return _wrapper @decorate def test(): """ this is a test function. """ return 1 print(test.__name__) # _wrapper print(test.__doc__) # None
没有使用wraps装饰器时,原函数的名称和文档注释都丢失了。
第二部分代码:
from functools import wraps def decorate(func): @wraps(func) def _wrapper(*args, **kwargs): print("test content.") return _wrapper @decorate def test(): """ this is a test function. """ return 1 print(test.__name__) print(test.__doc__) """ test this is a test function. """
使用了wraps装饰器,原函数的名称和文档注释都还在。
八、描述器
一个类中,只要有 __get__() 、 __set__() 、__delete__() 这三个魔法方法中的其中一个,那么就是描述器。
描述器管理一个"类属性"的访问、修改、删除。(上面已有写了)
基于描述器的装饰器,可以让一个方法,用起来像一个属性。
class Property: """这是一个装饰器,也是一个描述器""" def __init__(self, func=None): self.func = func def __get__(self, obj, objtype=None): if obj is None: return self # 直接通过类来访问时 if self.func is None: raise AttributeError("unreadable attribute") return self.func(obj) class MyName: """想要隐藏自己的昵称,用匿名来代替。如匿名评论""" def __init__(self, name): self.name = name self.isanonymous = False # 是否是匿名的 @Property # 可以让一个方法,用起来像属性 def show_name(self): if self.isanonymous: # 如果是匿名的 return "匿名的" else: return self.name # 不是匿名直接返回明文 my = MyName("zyb") my.isanonymous = True print(my.show_name) """ 运行结果: 匿名的 """
九、内置装饰器
(一).@property装饰器
就是把一个方法,变成属性。就是调用的时候不用加小括号了。
class A: def __init__(self, name, age): self.name = name self.age = age @property def foo(self): print(self.name) return "property foo" p = A("quanquan616", 30) print(p.foo) # 直接就是属性的形式了
(二).静态方法:@staticmethod,两个看起来都像普通函数。不需要self参数了。
class A: @staticmethod # 静态方法了 def meth(): print("aaa") # def meth(self): # print("aaa") A.meth() # 直接用类,就相当于看成普通函数 a = A() a.meth() # 实例调用的时候 --> A.meth(a) """ 如果我希望:实例调用和类调用都一样,都看成普通函数,怎么办?@staticmethod 静态方法:会让类和实例,看它都是一个普通函数 """ """ 运行结果: aaa aaa """
(三).类方法:@classmethod,不传实例,只传类。
class A: @classmethod def meth(cls): # 第一个参数是cls 表示类 print("aaa") A.meth() # 直接用类,就相当于看成普通函数 a = A() a.meth() # 实例调用的时候 --> A.meth(A) #类当作第一个参数传进去 """ 类方法:第一个传进去的不是实例,而是类。 大概场景: 以后做数据库接入的时候,有一个ORM,基于面向对象的。 在查询的时候,可以使用类方法,来简化你的代码。 """
(四).区别
(1).@staticmethod 不需要表示自身对象的self参数和自身类的cls参数,就跟使用普通的函数一样。但不加这个装饰器就不可以了!
(2).@classmethod 不需要self参数,但是第一个参数必须是cls,表示自身类。
(3).@classmethod的话,在类里的所有方法都可以调用的。