Python第六章 面向对象
第六章 面向对象1.面向对象初了解? 面向对象的优点: ? 1.对相似功能的函数,同一个业务下的函数进行归类,分类 ? 2.类是一个公共的模板,对象就是从具体的模板中实例化出来的,得到对象就得到一切 类:具有相同属性和功能的一类事物 对象:某个类的具体表现 2.面向对象的结构class Human:#类名不要用下划线 #第一部分:静态属性 mind = '有思想' #类的属性 (静态属性,静态字段) #第二部分:动态方法 def eat(self):#方法 print('人类都需要吃饭') 3.从类名的角度研究类1.类名查看类中所有的内容print(Human.__dict__) #结果: #{'__module__': '__main__','mind': '思想','eat': <function Human.eat at 0x000001AD41659D08>,'__dict__': <attribute '__dict__' of 'Human' objects>,'__weakref__': <attribute '__weakref__' of 'Human' objects>,'__doc__': None} 2.类名操作类中的静态属性 (万能的点)? 1.增: Human.body = '有躯体' print(Human.__dict__) ? 2.删:del Human.mind del Huamn.mind ? 3.改:Human.mind = ‘live脑残‘ Human.mind = 'live脑残' ? 4.查:print(Human.mind) print(Human.mind) 3.类名调用类中的方法? 一般类中的(除了静态方法和类方法)方法不会通过类名调用 Human.eat(1)#self,需要传参,对象不需要,默认传对象名 4.从对象的角度研究类1.实例化对象:obj = Human()#实例化过程 #得到一个返回值,返回值就是对象,实例 2.实例化一个对象发生三件事:? 1.开辟一个对象空间。 ? 2.自动执行object中的__new__()方法,在内存中开辟一个对象空间. ? 3.运行__init__方法内的代码,给对象空间封装属性 class Human: mind = '有思想' def __init__(self,name,age): self.name = name self.age = age def eat(self): print('人类都需要吃饭') obj = Human()#将obj传给了self,自动执行__init__()函数 print(obj)#结果:<__main__.Human object at 0x00000210B6A48E80> #传参: obj = Human('qq',18) print(obj.name,obj.age) ''' 结果:qq 18 ''' print(obj.__dict__)#对象的属性,字典的形式 ''' 结果:{'name': 'qq','age': 18} ''' 3.对象操作对象空间的属性1.对象查看对象空间的所有属性:? 1.print(obj.__dict__): obj = Human('qq',18) print(obj.__dict__) 2.对象操作对象空间的属性? 1.增:obj.sex = ‘laddy_boy‘ ? 2.删:del obj.age ? 3.改:obj.age = 58 ? 4.查:print(obj.sex) 4.对象查看类中的属性? 对象不允许修改类中的属性,只允许查看。 obj = Human('qq',18) print(obj.mind)#通过对象查看类中的属性 obj.mind = '无脑' print(obj.mind) print(Human.mind) ''' 类中的属性并没有更改,而是对象中新增了mind属性 ''' 5.对象调用类中的方法class Human: mind = '有思想' def __init__(self,age): self.name = name self.age = age def eat(self): print(f'{self.name}都需要吃饭') def work(self): print(f'{self.name}都会工作') obj = Human('戴维',23) obj.work() obj.eat() 6.一个类可以实例化多个对象obj1 = Human('戴维',20) obj2 = Human('杰西',22) obj3 = Human('肉丝',18) 5.类的空间问题? 1.添加对象的属性 #1.在类的__init__中添加, #2.在类的方法中添加 def eat(self,argv): self.sex = '女' #3.在类的外部添加 sun = Human('孙','18') sun.weight = 120 ? 2.添加类的属性 #1.类的内部,直接定义 #2.类的外部: Human.body = '有头有脸' ? 3.对象空间与类空间的关系 当对象空间与类空间有相同的的名字,对象. 要先从对象空间查找 查询顺序: 对象.名字:对象空间 _类对象指针_ -->类空间 -- >父类空间 类名.名字:类空间 --> 父类空间 6.类与类之间的关系1.依赖关系:? 有主从之分,将一个类的类名或者对象传入另一个类的方法中 class Elephant:#主 def __init__(self,name): self.name = name def open(self,obj): print(f'{self.name}打开冰箱') obj.be_open() class Fire:#从 def __init__(self,name): self.name = name def be_open(self): print(f'{self.name}冰箱被打开了') qiqi = Elephant('琪琪') haier = Fire('海尔') qiqi.open(haier) ''' 结果: 琪琪打开冰箱 海尔冰箱被打开了 ''' 2.组合关系:? 给一个类的对象封装一个属 性,此属性为另一个类的对象(类名) class Game: def __init__(self,ad,hp): self.name = name self.ad = ad self.hp = hp def attrck(self,p): ''' 2. #p.hp = p.hp - self.ad - self.weap.a d #print(f'{self.name}使用{self.weap.name}攻击{p.name},掉了{self.ad+self.weap.ad},还剩{p.hp}') ''' def equipment(self,weap): self.weap = weap class Weapon: def __init__(self,ad): self.name = name self.ad = ad def weappon(self,p1,p2): #1. p2.hp = p2.hp - p1.ad - self.ad #1. print(f'{p1.name}使用{self.name}攻击{p2.name},{p2.name}掉了{p1.ad+self.ad}血量,{p2.name}还剩{p2.hp}') gailun = Game('盖伦',10,100) zhaoxin = Game('赵信',20,80) baojian = Weapon('大宝剑',20) changqiang = Weapon('长枪',15) #1. gailun.equipment(baojian) # 1.gailun.weap.weappon(gailun,zhaoxin) # 1.zhaoxin.equipment(changqiang) # 1.zhaoxin.weap.weappon(zhaoxin,gailun) ''' 2. gailun.equipment(baojian) gailun.attrck(zhaoxin) gailun.attrck(zhaoxin) ''' 7.继承1.初识继承:? 专业角度:如果B类继承A类,B类就称为子类、派生类。A类就称为父类、基类、超类。 ? 面向对象三大特性:封装、继承、多态 最顶级父类就是object 2.继承的优点:? 1.减少重复代码 ? 2.增加类之间的耦合性 ? 3.使代码更加清晰,流畅 3.继承:单继承,多继承1.单继承:? 查询顺序单项不可逆:子类可使用父类的属性方法,父类不可使用子类的属性方法 ? 1.1 类名执行父类属性方法,对象执行父类属性方法 class Animal: def __init__(self,sex,age): self.name = name self.sex = sex self.age = age class Human(Animal): pass class Dog(Animal): pass class Cat(): pass #Animal:父类 #Human,Dog:子类 person = Human('C罗','男',18) #cat1 = Cat()#默认为继承object 1.2 既执行子类的方法,又执行父类的方法 方案一:不依赖于继承,使用函数的调用 方案二:依赖于继承。super(), 执行父类的方法。称为重构 class Animal: def __init__(self,age): self.name = name self.sex = sex self.age = age pass def eat(self): print('动物都需要吃饭') class Human(Animal): def __init__(self,age,hobdy): #方案一 #Animal.__init__(self,age)#需要传入人类对象,姓名,性别,年龄 #方案二: #super(Huamn,self).__init__(name,age)#完整写法 super().__init__(name,age)#简写 self.hobdy = hobdy pass def eat(self): super().eat() print(f'{self.name}需要吃饭') human1 = Human('涛涛','雌','48','唱跳') print(human1.__dict__) human1.eat() ''' 结果: {'name': '涛涛','sex': '雌','age': '48','hobdy': '唱跳'} 动物都需要吃饭 涛涛需要吃饭 ''' 2.多继承:python中类分为两种: python2.x:python 2.2之前都是经典类,python2.2之后,经典类与新式类共存 python3.x:全是新式类 ? 1.经典类:不继承object类,遵循深度优先原则:从左至右。 ? 2.新式类:继承object类,遵循mro(C3)算法 关于mro算法:
class B(A): pass mro(B) = mro(B(A)) = [B] + merge(mro(A) + [A]) = [B,A]
表头:列表的第一个元素 表尾:列表中除表头以外的所有元素(可以为空) +操作:[A] + [B] = [A,B]
如计算merge( [E,O],[C,E,F,[C] ) 有三个列表 : ① ② ③ 1 merge不为空,取出第一个列表列表①的表头E,进行判断 各个列表的表尾分别是[O],[E,O],E在这些表尾的集合中,因而跳过当前当前列表 2 取出列表②的表头C,进行判断 C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除 merge( [E,[C]) = [C] + merge( [E,O] ) 3 进行下一次新的merge操作 ......
class O: name = 'qq' class D(O): pass class E(O): name = 'wx' # pass class F(O): name = 'taobao' class B(D,E): pass class C(E,F): pass class A(B,C): pass obj = A() print(obj.name) print(A.mro())#直接得出mro算法求出的顺序 """ mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C])#多继承 mro(B(D,E)) = [B] + merge(mro(D),mro(E),[D,E])#单继承:mro(D(O)) = [D,O] = [B] + merge([D,E])#拿出并删除D = [B,D] + merge([O],[E]) = [B,D,E] + merge([O],[O]) = [B,O] mro(C(E,F)) = [C] + merge(mro(E),mro(F),F]) = [C] + merge([E,[F,F]) = [C,[F]) = [C,F] + merge([O],[O]) = [C,O] mro(A(B,C)) = [A] + merge([B,C]) = [A,B] + merge([D,[C]) = [A,B,D] + merge([E,C] +merge([O],O]) = [A,C,O] 所以:mro()算法得出的顺序为:A-->B-->D-->C-->E-->F-->O """ 8.封装、多态和鸭子类型? 1.封装:把数据代码放到一个空间,并且可以使用(函数) ? 2.多态:一个事物可呈现多种形态 python默认支持多态,一个变量可以指向多种数据类型 鸭子类型:python崇尚鸭子类型(编程思路),看起来像,就是鸭子。对相同的功能定义相同的名字。 鸭子的优点: ? 1.好记 ? 2.虽然A,B两个类没有关系,但是统一两个类中相似方法的方法名,在某种意义上统一标准 class A: def login(self): print('登录') class B: def login(self): print('登录') ''' A,B互为鸭子。 ''' 9.类的约束方法一:python语言惯于使用的一种约束方式,在父类主动抛出错误 class Payment: def pay(self): raise Exception('你的子类未定义pay方法')#主动报错,起到约束作用 class QQ_pay(Payment): def pay(self,money): print(f"QQ成功支付{money}元") class Ali_pay(Payment): def pay(self,money): print(f"支付宝成功支付{money}元") class Wechat(Payment): def fukuan(self,money): print(f"微信成功支付{money}元") def pay(obj,money):#归一化设计:统一接口 obj.pay(money) pass #使用Wechat实例化对象并调用pay函数,就会主动报错。 方法二:借鉴于java语言,定义抽象类的概念,做到真正的强制约束 from abc import ABCMeta,abstractmethod #抽象类,接口类:强制制定规则 class Paymet(metaclass=ABCMeta):#指定元素 @abstractmethod def pay(self,money): pass class QQ_pay(Paymet): def pay(self,money): print(f"QQ成功支付{money}元") class Wechat(Paymet): def fukuan(self,money): print(f"微信成功支付{money}元") def pay(obj,money): obj.pay(money) obj = Wechat() ''' 在实例化对象时就会报错 obj = Wechat() TypeError: Can't instantiate abstract class Wechat with abstract methods pay ''' 10.super的深度剖析严格按照对象从属于类的mro的执行顺序执行,查询下一个类 class A: def f1(self): print('in A') class Foo(A): def f1(self): super().f1() print('in Foo') class Bar(A): def f1(self): print('in Bar') # 1 class Info(Foo,Bar): def f1(self): super(Foo,self).f1() print('in Info f1') # 2 # [Info --> Foo --> Bar --> A] obj = Info() obj.f1() 11.类的关于1.类的私有成员:? 在类的内部可以访问,外部不可访问,也不可在类的派生类中使用。 ? 当遇到重要的数据,功能,(只允许本类使用的)使用私有成员 类的私有成员:类的私有属性、类的私有方法、对象私有属性 class A: name = '涛涛' __name = '套套' def func(self): print(self.name) print(self.__name) obj = A() obj.func() #可以访问 obj.__name#不可访问,会报错 #如何访问私有属性: print(A.__dict__) #{'__module__': '__main__','name': '涛涛','_A__name': '套套','func': <function A.func at 0x00000237FC0F9D08>,'__dict__': <attribute '__dict__' of 'A' objects>,'__weakref__': <attribute '__weakref__' of 'A' objects>,'__doc__': None}##会发现私有属性都被加上了'_类名+私有名' #所以调用 print(A._A__name) 2.实例方法、类方法1.实例方法:默认参数为self的方法 2.类方法: ? 一般就是通过类名去调用的方法,并且自动将类名地址传给cls,但是如果通过对象调用也可以,但传的地址还是类名地址 类方法作用: ? 1.得到类名可以实例化对象。 ? 2.可以操作类的属性 class A: @classmethod def cls_func(cls): print(f'cls'--->{cls}) #实例化对象 obj = cls() print(obj) print(A) A.cls_func() obj1 = A() obj.cls_func() 3.静态方法:? 静态方法不依赖对象与类。静态方法就是函数 作用:保证代码的规范性,合理的划分,提高代码后续维护性。 class A: @staticmethod def static_func(): print('静态方法') 4.属性:将执行一个函数需要函数名()变成直接函数名,将动态方法伪装成一个属性,虽然代码级别上没有提升,但是使其看起来更合理(本质还是方法) class Bmi: def __init__(self,height,weight): self.name = name self.height = height self.weight = weight @property def bmi(self): self.BMI = self.weight / self.height**2 print(self.BMI) p1 = Bmi('w',1.75,74) p1.bmi property是一个组合: ? 应用场景:1.面试考调用流程 ? 2.工作中如果遇到一些类似属性的方法名,可以让其伪装成属性 设置属性的两种方式:后两个return是无效的,得不到。 1.利用装饰器设置属性 class Foo: @property def bmi(self): print('get的时候运行我') @bmi.setter def bmi(self,value): print('set的时候运行我') print(value)#return得不到 @bmi.deleter def bmi(self): print('delete时运行我') obj = Foo() obj.bmi obj.bmi = 666#不是改变值,而是调用@bmi.setter装饰下的函数 del obj.bmi 2.利用实例化对象的方式设置属性 class Foo: def get_A(self): print('get的时候运行我') def set_A(self,value): print('set的时候运行我') print(value) def delete_A(self): print('delete的时候运行我') A = property(get_A,set_A,delete_A)#内置三个参数,与get,set,delet一一对应 f1 = Foo() f1.A f1.A = '666' del f1.A 5.内置函数:isinstance,issubclassisinstance:判断的是对象与类的关系True/False isinstance(a,b):判断对象a是否是b类或者b类的派生类实例化的对象 class A: pass class B(A): pass class C: pass obj = B() print(isinstance(obj,B)) print(isinstance(obj,A)) print(isinstance(obj,C)) """ 结果: True True False """ issubclass:类与类的关系 True/False issubclass(a,b):判断a类是否是b类的子孙类 class A: pass class B(A): pass class C: pass print(issubclass(B,A)) print(issubclass(C,A)) """ 结果: True False """ 12.元类typetype:获取对象从属于的类 python中一切皆对象,类在某种意义上也是一个对象,python中自己定义的类以及大部分内置类,都是有type元类(构建类)实例化来的 type与object的关系: print(type(object)) #object类是type类的一个实例 print(issubclass(type,object)) #type类是object类的子孙类 13.反射? 程序对自己内部代码的一种自省方式 反射是什么:通过字符串去操作对象的方式 反射是一个组合:hasattr、getattr、setattr、delattr(四个内置函数) 1.实例化对象的角度:class Human: country = '中国' def __init__(self,age): self.name = name self.age = age def func(self): print('is func') obj = Human('涛涛',80) #hasattr、getattr #hasattr返回True/False,getattr获取对象,没有会报错,可设置默认返回值 print(hasattr(obj,'name')) f = getattr(obj,'func') f() #setattr,delattr用的少 #setattr:添加属性,delattr删除属性 setattr(obj,'sex','男') print(obj.__dict__) delattr(obj,'name') print(obj.__dict__) 2.从类的角度:class Human: country = '中国' def __init__(self,age): self.name = name self.age = age def func(self): print('is func') if hasattr(A,'country'): print(getattr(A,'country')) 3.从其他模块:import other #找到other.py对象的A类,实例化对象 print(getattr(other,'A')) obj = getattr(other,'A')('涛涛') #对实例化对象反射取值 print(getattr(obj,'name')) 4.从当前模块:import sys print(sys.modules[__name__])#获取当前文件对象名 def func1(): print('in 本模块这个对象') pass getattr(sys.modules[__name__],'func1')() #当本文件有很多函数(名字递增),反射调用所有函数: func_list = [f'func{i}' for i in range(1,5)] for func in func_list: getattr(sys.modules[__name__],func)() 14.函数与方法的区别? 1.总结:函数都是显性传参,方法都有隐形传参 python 中一切皆对象,类在某种意义上也是一个对象,python中自己定义的类,以及大部分内置类,都是由type元类(构建类)实例化得来的. python 中一切皆对象,函数在某种意义上也是一个对象,函数这个对象是从FunctionType这个类实例化出来的. python 中一切皆对象,方法在某种意义上也是一个对象,方法这个对象是从MethodType这个类实例化出来的. 1.通过打印函数名的方式区分什么是方法,什么是函数def func1(): pass class A: def func2(): pass pass print(func1) print(A.func2) """ 通过类名调用的类中的方法叫做函数,结果: <function func1 at 0x0000022B99002F28> <function A.func2 at 0x0000022B9ACDABF8> """ obj = A() print(obj.func2) """ 通过对象调用的方法叫方法,结果: <bound method A.func2 of <__main__.A object at 0x0000022B9ACD9198>> """ 2.借助模块判断是方法还是函数from types import FunctionType from types import MethodType def func(): pass class A: def func(self): pass obj = A() #FunctionType:判断是否为函数 #MethodType:判断是否为方法 print(isinstance(func,FunctionType)) # True print(isinstance(A.func,FunctionType)) # True print(isinstance(obj.func,FunctionType)) # False print(isinstance(obj.func,MethodType)) # True 15.特殊双下方法? 双下方法:原来是开发python的程序员在源码中使用的。 双下方法:你不知道你干了什么就触发了双下方法 双下方法要慎用。 1.__len__():? 必须有返回值且返回值必须为int型 #计算实例化对象的属性个数 class B: def __init__(self,age): self.name = name self.age = age def __len__(self):#返回对象的属性个数 return len(self.__dict__) pass b = B('taotao',23) print(b.len(b)) 2.__hash__():? 获取哈希值,必须有返回值且返回值必须是可哈希类型 class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b)) pass a =A() print(hash(a)) 3.__str__()、__repr__():? 打印对象时触发,二者同时存在时,只执行__str__()。(str对象或repr对象也会触发)-->必须有返回值且返回值必须为字符串 class A: def __init__(self): pass def __str__(self): return '哈哈' def __str__(self): return '嘿嘿' pass a = A() print(a) print(str(a)) print('%s'%a) print(repr(a)) 4.__call__():****? 当对象后面加括号或类()()触发执行从属于类(父类)的__call__方法。 class Foo: def __init__(self): pass def __call__(self,*args,**kwargs): print('__call__') obj = Foo() obj() 5.__eq__():? 判断是否相等 class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b) 6.__del__():? 析构方法,当对象在内存中被释放时,自动触发执行。 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。 class A: def __del__(self): print(666) obj = A() del obj 7.__new__():***? new一个对象,构造方法 类名()
1.__new__方法: class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls,**kwargs): print('in new function') return object.__new__(A)#object类中的方法 pass #对象时object类中的__new__方法产生了一个对象。 a = A() #先执行__new__,在执行__init__,__init__中不能有return 2.python中的设计模式: 单例模式 ? 一个类只允许实例化一个对象 优点:节省内存 #单例模型 class A: __instance = None def __init__(self,name): self.name = name def __new__(cls,**kwargs): if not cls.__instance: cls.__instance = obj.__new__(cls) return cls.__instance pass obj = A('alex') print(obj) obj1 = A('taotao') print(obj1) 8.__item__():系列? __getitem__ ()、__setitem__()、__delitem__():对象做类似于字典的(增删改查)触发item系列 __delattr__():对象. 属性 触发此方法 class Foo: def __init__(self,name): self.name = name def __getitem__(self,item):#查 print(item) return self.__dict__[item] def __setitem__(self,key,value):#增、改 self.__dict__[key] = value print(self.__dict__) def __delitem__(self,key):#删 super().__delattr__(key) print(self.__dict__) #删对象.属性触发 def __delattr__(self,item): super().__delattr__(item) print(self.__dict__) print(item) f1 = Foo('sb') print(f1['name']) f1['ac'] = 2 del f1['ac'] print(f1.__dict__) # del f1.ac # print(f1.name) 9.__enter__()、__exit__ () with,上下文管理实例化对象的第二种方式:必须基于enter和exit这两个方法 class A: def __init__(self,name): self.name = name def __enter__(self):#开启上下文管理器对象时触发此方法 self.name = self.name + '侬好' #第一步 print(111) return self #!!!必须写,将实例化的对象(self)返回 def __exit__(self,exc_type,exc_val,exc_tb):# 执行完上下文管理器对象f1时触发此方法 print(333)#第三步 self.name = self.name + '好个鸡儿' with A('sb') as obj: print(222) print(obj.name)#第二步 print(obj.name)#第四步 """结果: 111 222 sb侬好 333 sb侬好好个鸡儿""" 10.__iter__():将对象变成可迭代对象class A: def __init__(self,name): self.name = name def __iter__(self): for i in range(10): yield i def __next__(self): pass obj = A('sb') # print('__iter__'in dir(obj)) for i in obj: print(i) print(obj.name) o = iter(obj.name) print(next(o)) 16.异常处理:异常处理的好处:
1.错误分类:语法错误,逻辑错误 如果你对错误信息不关心,只是想要排除错误让程序继续运行. 用万能异常 #万能异常:处理所有python识别的异常 try: pass except Exception as e: print(e) pass #单,多分支异常结构:你对错误信息要进行明确的分流,让你的程序多元化开发.
? except 必须依赖try,else必须依赖except和try,finally只依赖try finally:在异常出现之前,执行finally中的语句 try: pass except: pass else: print('如果没有异常则执行这里') finally: print(666)
with open('test1.txt','r+',encoding = 'utf-8') as f: try: for i in f1: print(i) finally: f1.close()#防止出现异常,文件不能关闭。
def func(): try: print(111) return 333 finally: print(222) pass pass print(func()) """结果: 111 222 333""" #在break之前执行 while 1: try: print(111) break finally: print(222)
raise ValueErro('出现了value错误')
#assert 条件 name = 'sb' n1 = input('name:') assert name == n1 print(111) #如果name 不等于 n1,会直接报错
python中提供了很多错误类型,但不是全部的错误 class SbError(BaseException):#自定义的错误必须继承于BaseException def __init__(self,msg): self.msg = msg def __str__(self): return self.msg try: raise SbError('你犯错了') except SbError as e: print(e) """结果:你犯错了""" (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |