一种事物具备多种不同形态
水 固态 液态 气态
大黄蜂 汽车人 汽车
官方解释:多个不同类的对象可以响应同一个方法,产生不同的结果
首先强调多态不是一种特殊的语法,而是一种状态,特性(即多个不同对象可以响应同一个方法,产生不同的结果
多个对象有相同的使用方法
好处:对于使用者而言,大大的降低了使用难度
我们之前写的USB接口,下的鼠标、键盘就属于多态
实现多态的手段:鸭子类型(最简单),继承:接口;抽象类
''' 要管理 鸡 鸭 鹅 如何最方便的管理: 说同一句话,他们都能理解 他们拥有相同的方法 ''' # 下面这个也算鸭子?类型 class chicken: def bark(self): print('gege') def spawn(self): print('下鸡蛋') class duck: def bark(self): print('yaay') def spawn(self): print('下鸭蛋') class E: def bark(self): print('eeee') def spawn(self): print('下e蛋') chicken = chicken() # 创建对象 duck = duck() E = E() def manage(obj): obj.spawn() manage(chicken) # 下鸡蛋 manage(duck) # 下鸭蛋 manage(E) # 下e蛋判断一个对象是否是某个类的实例
参数1 要判断的对象(a)
参数2 要判断的类型(int)
def add_num(a,b): # if type(a) == int and type(b) == int: # return a+b # 这是我们原来的判定方法 if isinstance(a,int) and isinstance(b,int): return a+b # 这是使用isinstance的写法 print(add_num(5,15)) # 20 print(add_num(2,'6')) # None判断一个类是不个类的子类
参数1 子类
参数2 父类
class Animal: def eat(self): print('动物得吃东西') class Tree: def light(self): print('树要光合作用') class Pig(Animal): def eat(self): print("猪得吃东西") pig = Pig() t = Tree() ###############传统方法###################### def manage(isanimal): if type(isanimal) == Animal: # 如果这么写了连pig的类都管不了,因为Pig是继承于Animal的 # 而不是等于Animal,所以并不会执行下面的语句 isanimal.eat() else: print('不是动物') manage(pig) # 不是动物 # manage(t) # 报错说没有eat方法 # 所以就需要 subclass 判断是不是子类 ###############subclass方法################# def manage(obj): if issubclass(type(obj),Animal): obj.eat() else: print('不是动物') manage(pig) # 猪得吃东西 manage(t) # 不是动物 print(issubclass(Pig,object)) # True # 需要注意的是,所有的类都直接或者间接地继承object,所以这样判断没有意义双下方法会在某个时刻自动执行
传(self)这种是对象方法
__str__ 会在对象被转换为字符串时执行,转换的结果就是这个函数的返回值 使用场景:我们可以利用该函数来 自定义对象的打印格式 class Person: # 其实这里等价于 class Person(object): # 即继承了object这个总的父类 # 所以下面的__str__相当于被覆写了 def __str__(self): print('run') return 'abc' p = Person() print(p) # run # abc # 如果把print(p)改成下面这个 str(p) # run # 所以说明__str__ 会在对象被转换为字符串时执行 # 转换的结果就是这个函数的返回值 class Person: # 其实这里等价于 class Person(object): # 即继承了object这个总的父类 def __init__(self,name,age): self.name = name self.age = age p = Person('jack',18) print(p) # <__main__.Person object at 0x1052412b0> # 打印出来是对象的内存地址# 如果想要的是让他打印出可用信息,而不是内存地址,
就按照下面的写(其实就是用双下str方法换一下要return的值)
class Person: # 其实这里等价于 class Person(object): # 即继承了object这个总的父类 # 所以下面的__str__相当于被覆写了 def __init__(self,name,age): self.name = name self.age = age def __str__(self): return '这是一个person对象,name:%s,age:%s' %(self.name,self.age) p = Person('jack',18) print(p) # 这是一个person对象,name:jack,age:18执行时机: 手动删除对象时立马执行,或是程序运行结束时也会自动执行(做一个清理工作) 使用场景: 当你的对象在使用过程中,打开了不属于解释器的资源:例如文件,网络端口等
# del 析构函数 (__init__ 构造函数) # 执行时机:手动删除对象时立马执行,或是程序运行结束时也会自动执行(垃圾回收机制?) # 使用场景:当你的对象再使用过程中打开了不属于解释器的资源,例如文件,网络端口 import time class Person: def __init__(self, name, age): self.name = name self.age = age def __del__(self): # 重写object中的 __str__ print("del run...") return "del run" p = Person("jack", 20) # del p # 删除对象触发 __del__函数执行 # # del run... time.sleep(2) print("over") # over # del run... # 程序结束后会把名称空间清除掉,清除时触发了 __del__,打印出 del run...结束使用自动关闭文件资源案例
class FileTool: # 该类用于简化文件的读写操作 def __init__(self, path): self.file = open(path, 'rt', encoding='utf-8') def read(self): return self.file.read() # rt模式不推荐直接读字节(汉字、英文字节不同),可以一行一行读 # 执行这个函数可以确定一个函数,这个对象肯定不用了,所以就可以放心的关心文件了 def __del__(self): self.file.close() tool = FileTool("a.txt") print(tool.read()) # 文件属于操作系统,不受垃圾回收机制管理 # aaaaaaaaaaaa # 不知道什么不使用该对象,那就写在 __del__函数中,当其被删除时,指定关闭资源python是动态语言,可以在运行期间动态修改对象的属性,如何能存储更多属性呢? 需要开启更大的内存区域,将原始的属性赋值过去 问题:如果开启的容量太大(为了效率牺牲了空间),将造成内存的浪费 解决方案:在创建对象是告诉系统这个对象只有哪些属性,也就是固定了对象的属性数量,这样就可任意要多少开多少,减少空间浪费(使用__slots__)
import sys class Person: __slots__ = ['name'] # 加了以后再添加属性就不行了,限制属性 # def __init__(self, name, age): def __init__(self, name): self.name = name # self.age = age # 未在__slots__中声明,直接报错 AttributeError: 'Person' object has no attribute 'age' # p = Person("jck", 18) p = Person("jck") print(sys.getsizeof(p)) # 获取对象占用内存大小 # 56 ---> 48 ---> __slots__ 指定有哪些属性,从而节省了内存空间(没指定__slots__之前56,指定之后48) # print(p.__dict__) # 报错,可变字典也被省掉了(名称空间连开都不开了),AttributeError: 'Person' object has no attribute '__dict__'该属性是一个类属性,用于优化对象内存
优化的原理:将原本不固定的属性数量,变得固定了,这样的解释器就不会以这个对象创建名称空间(所以__dict__也没了),从而达到减少内存开销的效果
另外当类中出现了__slots__时将导致这个类的对象不再添加__slots__定义之外的属性
[ ] 的实现原理(getitem setitem delitem)
任何的符号,都会被解释器解释称特殊含义,例如 . [ ] ( )
getitem 当你用中括号去获取属性时 执行setitem 当你用中括号去设置属性时 执行delitem 当你用中括号去删除属性时 执行
''' 需求: 让一个对象支持 点语法来取值,也支持括号取值 ''' class MyDict(dict): def __getattr__(self, key): return self.get(key) # return self[key] # KeyError: 'name' def __setattr__(self, key, value): self[key] = value def __delattr__(self, item): del self[item] # 继承 dict 可以直接用字典的一些方式 a = MyDict() a['name'] = 'jack' print(a['name']) # jack # 使用 .语法(通过实现__getattr__ 、__setattr__、__delattr__来实现) a.name = 'sum' print(a.name, a['name']) # sum sum print(a['name']) # sum a.name = 'jackson' print(a.name) # jackson del a.name print(a.name) # None # 用的是 .get 所以不会报错> >= == != < <= 等比较运算符的的实现原理(运算符重载)(__gt__ __ge__ __eq__ __ne__ __lt__ __le__)
当我们在使用某个符号时,python解释器都会为这个符号定义一个含义,同时调用对应的处理函数,当我们需要自定义对象的比较规则时,就可以在子类中覆盖大于等于等的方法
案例
# 自定义对象的比较 # 对象直接无法直接比较大小 class Person: def __init__(self, name, height, age): self.name = name self.height = height self.age = age p1 = Person('jason', 185, 18) p2 = Person('tank', 179, 18) # print(p1 > p2) # TypeError: '>' not supported between instances of 'Person' and 'Person' class Student: def __init__(self, name, height, age): self.name = name self.height = height self.age = age # 自定义比较规则 def __gt__(self, other): print(self) print(other) print("__gt__") # 比身高 # if self.height > other.height: # return True return self.height > other.height # 没有返回值默认返回 None 即 False def __eq__(self, other): print("eq------") return self.name == other.name stu1 = Student("jack", 180, 28) stu2 = Student("rose", 165, 27) print(stu1 > stu2) # 直接报错,TypeError: '>' not supported between instances of 'Student' and 'Student' # <__main__.Student object at 0x000001992C7C8F60> # <__main__.Student object at 0x000001992C7C8F98> # __gt__ # True print(stu1 < stu2) # 大于和小于只要实现一个即可,符号如果不同解释器会自动交换两个对象的位置 # <__main__.Student object at 0x000001992C7C8F98> # <__main__.Student object at 0x000001992C7C8F60> # __gt__ # False print(stu1) # <__main__.Student object at 0x000001992C7C8F60> print(stu2) # <__main__.Student object at 0x000001992C7C8F98>原本自定义对象无法直接使用大于小于来进行比较,我们可以自定义运算符来实现,让自定义对象也支持比较符
上述代码中.other指的是另一个参与比较的对象
大于和小于只要实现一个即可,符号如果不同解释器会自动交换两个对象的位置
迭代器:是指具有__iter__和__next__的对象
我们可以为对象增加这两个方法来让对象变成迭代器
class MyIter: # num 传入,用来指定迭代次数 def __init__(self, num): self.num = num self.c = 0 def __iter__(self): return self def __next__(self): self.c += 1 if self.c <= self.num: return "hahha" raise StopIteration # 抛出异常 for i in MyIter(3): print(i) # hahha # hahha # hahha上下文:这个概念属于语言学科,指的是一段话的意义,要参考当前的场景,即上下文
在python中,上下文可以理解为一个代码区间,一个范围,例如with open 打开的文件仅在这个上下文中有效
__enter__:表示进入上下文(进入某个场景了)
__exit__:表示退出上下文(离开了某个场景了)
案例
class MyOpen: def __enter__(self): print("enter....") def __exit__(self, exc_type, exc_val, exc_tb): # exc --> exception print("exit.....") print(exc_type, exc_val, exc_tb) with MyOpen() as m: print("start...") # 1 + '123' # enter.... # exit..... # None None None实现了上面的两个方法就可以配合with语句用了,当执行with语句时,会先执行__enter__,当代码执行完毕后执行__exit__,或者代码遇到了异常会立即执行__exit__,并传入错误信息,包含错误的类型,错误的信息,错误的追踪信息
class MyOpen: def __enter__(self): print("enter....") def __exit__(self, exc_type, exc_val, exc_tb): # exc --> exception print("exit.....") print(exc_type, exc_val, exc_tb) return True # return True 可以让程序不报错 with MyOpen() as m: print("start...") 1 + '123' # TypeError: unsupported operand type(s) for +: 'int' and 'str' # enter.... # exit..... # None None None # 没有报错时打印这个 # <class 'TypeError'> unsupported operand type(s) for +: 'int' and 'str' <traceback object at 0x00000283F3EE0608> # 有错时打印这个,若__exit__ 返回为True则控制台不报错,否则控制台也会报错注意点
__enter__ 函数应该返回对象自己 __exit__ 函数可以有返回值,是一个bool类型,用于表示异常是否被处理,仅在上下文中出现异常时有用 如果为True 则意味着,异常已经被处理了 False 异常未被处理,程序将中断报错转载于:https://www.cnblogs.com/PowerTips/p/11264371.html
相关资源:php 7.29 WIN64 mysql