面向对象介绍
面向过程:核心是"过程"二字过程的终极奥义就是将程序流程化过程是"流水线",用来分步骤解决问题的面向对象:核心是"对象"二字对象的终极奥义就是将程序"整合"对象是"容器",用来盛放数据与功能的类也是"容器",该容器用来存放同类对象共有的数据与功能python这门语言到底提供了什么语法来允许我们将数据与功能很好地整合好一起呢???# 程序=数据+功能# 学生的容器=学生的数据+学生的功能# 课程的容器=课程的数据+课程的功能# 粉扑、眼影、各种颜料=》原材料=》数据# 眉笔、小刷子 =》工具 =》功能
# 学生的功能def tell_stu_info(stu_obj):print('学生信息:名字:%s 年龄:%s 性别:%s' %(stu_obj['stu_name'],stu_obj['stu_age'],stu_obj['stu_gender']))def set_info(stu_obj,x,y,z):stu_obj['stu_name']=xstu_obj['stu_age']=ystu_obj['stu_gender']=zstu_obj={'stu_school':'oldboy','stu_name':'egon','stu_age':18,'stu_gender':'male','tell_stu_info': tell_stu_info,'set_info':set_info}stu1_obj={'stu_school':'oldboy','stu_name':'lili','stu_age':19,'stu_gender':'female','tell_stu_info': tell_stu_info,'set_info':set_info}# 课程的数据course_name='python'course_period='6mons'course_score=10# 课程的功能def tell_coure_info():print('课程信息:名字:%s 周期:%s 学分:%s' %(course_name,course_period,course_score))
实现面向对象
定义类
# 类是对象相似数据与功能的集合体# 所以类体中最常见的是变量与函数的定义,但是类体其实是可以包含任意其他代码的# 注意:类体代码是在类定义阶段就会立即执行,会产生类的名称空间class Student:# 1、变量的定义stu_school='oldboy'# 2、功能的定义def tell_stu_info(stu_obj):print('学生信息:名字:%s 年龄:%s 性别:%s' %(stu_obj['stu_name'],stu_obj['stu_age'],stu_obj['stu_gender']))def set_info(stu_obj,x,y,z):stu_obj['stu_name']=xstu_obj['stu_age']=ystu_obj['stu_gender']=z# print('========>')# print(Student.__dict__)
类属性
# 1、访问数据属性# print(Student.stu_school) # Student.__dict__['stu_school'] 返回的是类属性的内存地址# 2、访问函数属性# print(Student.set_info) # Student.__dict__['set_info'] 返回的是类函数的内存地址# Student.x=1111 #Student.__dict__['x]=111 #为类添加属性# print(Student.__dict__) #查看类属性
init方法推导
# 二:再调用类产生对象stu1_obj=Student()stu2_obj=Student()stu3_obj=Student()# print(stu1_obj.__dict__) #新创建的对象dict中的属性都为空# print(stu2_obj.__dict__)# print(stu3_obj.__dict__)# 为对象定制自己独有的属性# 问题1:代码重复# 问题2:属性的查找顺序# stu1_obj.stu_name='egon' # stu1_obj.__dict__['stu_name']='egon' #使用__init__方法,将这种赋值的方式改为传参# stu1_obj.stu_age=18 # stu1_obj.__dict__['stu_age']=18# stu1_obj.stu_gender='male' # stu1_obj.__dict__['stu_gender']='male'# stu1_obj.xxxx='123'# # print(stu1_obj.__dict__) #返回 {'stu_name': 'egon', 'stu_age': 18, 'stu_gender': 'male', 'xxxx': '123'}# stu2_obj.stu_name='lili'# stu2_obj.stu_age=19# stu2_obj.stu_gender='female'# # print(stu2_obj.__dict__)## stu3_obj.stu_name='jack'# stu3_obj.stu_age=20# stu3_obj.stu_gender='male'# # print(stu2_obj.__dict__)# 解决问题一:# 解决方案一:# def init(obj,x,y,z):# obj.stu_name=x# obj.stu_age=y# obj.stu_gender=z## init(stu1_obj,'egon',18,'male')# init(stu2_obj,'lili',19,'female')# init(stu2_obj,'jack',20,'male')# 解决方案二:# 一:先定义类class Student:# 1、变量的定义stu_school='oldboy'# 空对象,'egon',18,'male'def __init__(obj,x,y,z):obj.stu_name=x # 空对象.stu_name='egon'obj.stu_age=y # 空对象.stu_age=18obj.stu_gender=z # 空对象.stu_gender='male'# return None 必须返回none,否则报错# 2、功能的定义def tell_stu_info(stu_obj):print('学生信息:名字:%s 年龄:%s 性别:%s' %(stu_obj['stu_name'],stu_obj['stu_age'],stu_obj['stu_gender']))def set_info(stu_obj,x,y,z):stu_obj['stu_name']=xstu_obj['stu_age']=ystu_obj['stu_gender']=z# print('========>')# 二:再调用类产生对象# 调用类的过程又称之为实例化,发生了三件事# 1、先产生一个空对象# 2、python会自动调用类中的__init__方法然将空对象已经调用类时括号内传入的参数一同传给__init__方法# 3、返回初始完的对象stu1_obj=Student('egon',18,'male') # Student.__init__(空对象,'egon',18,'male')# stu2_obj=Student('lili',19,'female')# stu3_obj=Student('jack',20,'male')# print(stu1_obj.__dict__)# print(stu2_obj.__dict__)# print(stu3_obj.__dict__)# print(Student.__dict__) #此时再打印student属性还是和开始时一样,没有根据其创建的实例信息
init方法总结
# 总结__init__方法# 1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据# 2、__init__内应该存放是为对象初始化属性的功能,但是是可以存放任意其他代码,想要在# 类调用时就立刻执行的代码都可以放到该方法内# 3、__init__方法必须返回None# 该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
类的公有属性
class Student:# 1、变量的定义stu_school='oldboy'count=0# 空对象,'egon',18,'male'def __init__(self,x,y,z):Student.count += 1 #每创建一个实例就会+1,可以反映创建了对少个实例。self.stu_name=x # 空对象.stu_name='egon'self.stu_age=y # 空对象.stu_age=18self.stu_gender=z # 空对象.stu_gender='male'# return None# 2、功能的定义def tell_stu_info(self):print('学生信息:名字:%s 年龄:%s 性别:%s' %(self.stu_name,self.stu_age,self.stu_gender))def set_info(self,x,y,z):self.stu_name=xself.stu_age=yself.stu_gender=zdef choose(self,x):print('正在选课')self.course=xstu1_obj=Student('egon',18,'male') # Student.__init__(空对象,'egon',18,'male')stu2_obj=Student('lili',19,'female')stu3_obj=Student('jack',20,'male')# print(stu1_obj.count) #都是3# print(stu2_obj.count)# print(stu3_obj.count)
推导类的绑定方法
# 类中存放的是对象共有的数据与功能# 一:类可以访问:# 1、类的数据属性# print(Student.stu_school)# 2、类的函数属性# print(Student.tell_stu_info)# print(Student.set_info)# 二:但其实未实例化的类中的东西是给对象用的。对于变量属性是多个实例公用的。函数是绑定方法,内存地址不同# 1、类的数据属性是共享给所有对象用的,大家访问的地址都一样# print(id(Student.stu_school))# print(id(stu1_obj.stu_school))# print(id(stu2_obj.stu_school))# print(id(stu3_obj.stu_school))# 2种方法修改类的公有变量,函数是绑定方法,内存地址各不相同# Student.stu_school='OLDBOY'# stu1_obj.stu_school='OLDBOY' #这样也会修改变量属性# print(stu1_obj.stu_school)
类实例方法的2种调用方式
# 2、类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同# 类调用自己的函数属性必须严格按照函数的用法来# print(Student.tell_stu_info)# print(Student.set_info)# Student.tell_stu_info(stu1_obj)# Student.tell_stu_info(stu2_obj)# Student.tell_stu_info(stu3_obj)# Student.set_info(stu1_obj,'EGON',19,'MALE')# Student.tell_stu_info(stu1_obj)# 绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当做第一个参数自动传入# print(Student.tell_stu_info) # Student.tell_stu_info(stu1_obj)# print(stu1_obj.tell_stu_info)# print(stu2_obj.tell_stu_info)# print(stu3_obj.tell_stu_info)# stu1_obj.tell_stu_info() #tell_stu_info(stu1_obj)# stu2_obj.tell_stu_info() #tell_stu_info(stu2_obj)# stu3_obj.tell_stu_info() #tell_stu_info(stu3_obj)## stu1_obj.choose('python全栈开发')# print(stu1_obj.course)## stu2_obj.choose('linux运维')# print(stu2_obj.course)## stu3_obj.choose('高级架构师')# print(stu3_obj.course)# 调用类方法的区别# 由实例调用与类传实例调用区别,实例调用的第一个参数自动传入自己本身这个对象。类传实例调用需要传类实例
list列表的绑定方法与类
# list是一个类l1=['aa','bb','cc'] # l=list([1,2,3]),类的实例化,实例化出l1l2=[111,222,333] # l=list([1,2,3]),实例化出l2,list是一个类# 内存地址不相同# print(l1.append)# print(list.append)# l1.append('dd')# l2.append('dd')# print(l1)# print(l2)# list.append(l1,'dd') #与l1.append('dd')含义相同# list.append(l2,'dd') #与l2.append('dd')含义相同# print(l1)# print(l2)
练习作业
选课系统项目中涉及到诸多数据与功能,要求引入面向对象的思想对其进行高度整合# 1、学校数据与功能整合# 2、课程数据与功能进行整合# 3、学生数据与功能进行整合# 4、讲师数据与功能进行整合# 5、班级数据与功能进行整合ps:不会写的同学,可以先用普通的方式,先把数据与功能都给写好,再考虑基于面向对象的思想进行整合数据部分:校区的名字:如"老男孩上海校区"校区的地址:如"上海虹桥"班级名字班级所在校区学生的学校学生的姓名学生的年龄学号学生的性别课程名字课程周期课程价格老师的名字老师的年龄老师的薪资老师的等级功能部分:校区创建完毕后,可以为每个校区创建班级班级创建完毕后,可以为每个班级创建课程学生创建完毕后,学生可以选择班级老师创建完毕后,可以为学生打分
代码
# 整合->解耦合->扩展性增强class School:school_name = 'OLDBOY'def __init__(self, nickname, addr):self.nickname = nicknameself.addr = addrself.classes = []def related_class(self, class_obj):# self.classes.append(班级名字)# self.classes.append(class_name)self.classes.append(class_obj)def tell_class(self): # 改# 打印的班级的名字print(self.nickname.center(60,'='))# 打印班级开设的课程信息for class_obj in self.classes:class_obj.tell_course()# # 一:学校# #1、创建校区school_obj1=School('老男孩魔都校区','上海')school_obj2=School('老男孩帝都校区','北京')## #2、为学校开设班级# # 上海校区开了:脱产14期,上海校区开了脱产15期# school_obj1.related_class("脱产14期")# school_obj1.related_class("脱产15期")## # 北京校区开了:脱产29期# school_obj2.related_class("脱产29期")## #3、查看每个校区开设的班级# school_obj1.tell_class()# school_obj2.tell_class()class Class:def __init__(self, name):self.name = nameself.course = Nonedef related_course(self, course_obj):# self.course = course_nameself.course = course_objdef tell_course(self):print('%s' % self.name,end=" ")self.course.tell_info() # 打印课程的详细信息# 二:班级# 1、创建班级class_obj1 = Class('脱产14期')class_obj2 = Class('脱产15期')class_obj3 = Class('脱产29期')# 2、为班级关联一个课程# class_obj1.related_course('python全栈开发')# class_obj2.related_course('linux运维')# class_obj3.related_course('python全栈开发')# 3、查看班级开设的课程信息# class_obj1.tell_course()# class_obj2.tell_course()# class_obj3.tell_course()# 4、为学校开设班级# 上海校区开了:脱产14期,上海校区开了脱产15期school_obj1.related_class(class_obj1) #对象也可以存到列表中school_obj1.related_class(class_obj2)# 北京校区开了:脱产29期school_obj2.related_class(class_obj3)# school_obj1.tell_class()# school_obj2.tell_class()class Course:def __init__(self,name,period,price):self.name=nameself.period=periodself.price=pricedef tell_info(self):print('<课程名:%s 周期:%s 价钱:%s>' %(self.name,self.period,self.price))# 三:课程# 1、创建课程course_obj1=Course('python全栈开发','6mons',20000)course_obj2=Course('linux运维','5mons',18000)# 2、查看课程的详细信息# course_obj1.tell_info()# course_obj2.tell_info()# 3、为班级关联课程对象class_obj1.related_course(course_obj1)class_obj2.related_course(course_obj2)class_obj3.related_course(course_obj1)# class_obj1.tell_course()# class_obj2.tell_course()# class_obj3.tell_course()school_obj1.tell_class()school_obj2.tell_class()class Student:pass
封装__属性方法
# 一:封装介绍# 封装是面向对象三大特性最核心的一个特性# 封装<->整合# 二、将封装的属性进行隐藏操作# 1、如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果# 该隐藏需要注意的问题:# I:在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如Foo._A__N,# 所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。class Foo:__x = 1 # _Foo__xdef __f1(self): # _Foo__f1print('from test')# print(Foo.__dict__)# # print(Foo._Foo__x)# # print(Foo._Foo__f1)# II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形class Foo:__x = 1 # _Foo__x = 1def __f1(self): # _Foo__f1print('from test')def f2(self):print(self.__x) # print(self._Foo__x)print(self.__f1) # print(self._Foo__f1)# print(Foo.__x)# print(Foo.__f1)# obj=Foo()# obj.f2()# III: 这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形class Foo:__x = 1 # _Foo__x = 1def __f1(self): # _Foo__f1print('from test')def f2(self):print(self.__x) # print(self._Foo__x)print(self.__f1) # print(self._Foo__f1)Foo.__y=3print(Foo.__dict__)print(Foo.__y)class Foo:__x = 1 # _Foo__x = 1def __init__(self,name,age):self.__name=nameself.__age=ageobj=Foo('egon',18)# print(obj.__dict__)# print(obj.name,obj.age)
为何隐藏
# 2、为何要隐藏?# I、隐藏数据属性"将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口(方法)来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制:# 设计者:egonclass People:def __init__(self, name):self.__name = namedef get_name(self):# 通过该接口就可以间接地访问到名字属性# print('小垃圾,不让看')print(self.__name)def set_name(self,val):if type(val) is not str:print('小垃圾,必须传字符串类型')returnself.__name=val# 使用者:王鹏obj = People('egon')# print(obj.name) # 无法直接用名字属性# obj.set_name('EGON')obj.set_name(123123123)obj.get_name()# II、隐藏函数/方法属性:目的的是为了隔离复杂度
property装饰器-方法伪造属性
# 装饰器是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加# 新功能的可调用对象# print(property)# property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性"""成人的BMI数值:过轻:低于18.5正常:18.5-23.9过重:24-27肥胖:28-32非常肥胖, 高于32体质指数(BMI)=体重(kg)÷身高^2(m)EX:70kg÷(1.75×1.75)=22.86"""
@property、@name.setter、@name.deleter
# 案例一:class People:def __init__(self, name, weight, height):self.name = nameself.weight = weightself.height = height# # 定义函数的原因1:# # 1、从bmi的公式上看,bmi应该是触发功能计算得到的# # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值# # 说白了,每次都是需要临时计算得到的## # 但是bmi听起来更像是一个数据属性,而非功能@propertydef bmi(self):return self.weight / (self.height ** 2)# obj1 = People('egon', 70, 1.83)# print(obj1.bmi())# obj1.height=1.86# print(obj1.bmi())# print(obj1.bmi)# # 案例二:class People:def __init__(self, name):self.__name = namedef get_name(self):return self.__namedef set_name(self, val):if type(val) is not str:print('必须传入str类型')returnself.__name = valdef del_name(self):print('不让删除')# del self.__namename=property(get_name,set_name,del_name)obj1=People('egon')# print(obj1.get_name())# obj1.set_name('EGON')# print(obj1.get_name())# obj1.del_name()#### # 人正常的思维逻辑# print(obj1.name) ## # obj1.name=18# # del obj1.name# 案例三: #装饰的3个方法名称都相同:nameclass People:def __init__(self, name):self.__name = name@propertydef name(self): # obj1.namereturn self.__name@name.setterdef name(self, val): # obj1.name='EGON'if type(val) is not str: #增加了判断,修改是否合法print('必须传入str类型')returnself.__name = val@name.deleterdef name(self): # del obj1.name #阻止删除print('不让删除')# del self.__nameobj1=People('egon')# 人正常的思维逻辑print(obj1.name) ## obj1.name=18# del obj1.name
继承
# 1、什么是继承# 2、为何要用继承:用来解决类与类之间代码冗余问题# I:继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性# II:需要注意的是:python支持多继承# 在Python中,新建的类可以继承一个或多个父类
新式类与经典类
新式类与经典类在多继承查找父类时,新式类广度优先,经典类深度优先
class Parent1(object):x=1111class Parent2(object):passclass Sub1(Parent1): # 单继承passclass Sub2(Parent1,Parent2): # 多继承passprint(Sub1.__bases__)print(Sub2.__bases__)# print(Sub1.x)# ps1: 在python2中有经典类与新式类之分# 新式类:继承了object类的子类,以及该子类的子类子子类。。。# 经典:没有继承object类的子类,以及该子类的子类子子类。。。# ps2:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类print(Parent1.__bases__) #__bases__内置方法,查看该类的父类print(Parent2.__bases__)
实现继承
# 3、如何实现继承# # 示范1:类与类之间存在冗余问题class Student:school='OLDBOY'def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sexdef choose_course(self):print('学生%s 正在选课' %self.name)class Teacher:school='OLDBOY'def __init__(self,name,age,sex,salary,level):self.name=nameself.age=ageself.sex=sexself.salary=salaryself.level=leveldef score(self):print('老师 %s 正在给学生打分' %self.name)# 示范2:基于继承解决类与类之间的冗余问题class OldboyPeople: #定义基类school = 'OLDBOY'def __init__(self, name, age, sex):self.name = nameself.age = ageself.sex = sex# student继承class Student(OldboyPeople): #继承基类,可以不使用init方法def choose_course(self):print('学生%s 正在选课' % self.name)# stu_obj = Student('lili', 18, 'female') #必须传入父类的init参数# print(stu_obj.__dict__)# print(stu_obj.school)# stu_obj.choose_course()# teacher继承class Teacher(OldboyPeople):# 老师的空对象,'egon',18,'male',3000,10def __init__(self, name, age, sex, salary, level): #将父类的参数也传进去;子类重写init方法,必须调用一次父类init方法# 指名道姓地跟父类OldboyPeople去要__init__OldboyPeople.__init__(self,name,age, sex) #调用父类的init方法self.salary = salaryself.level = leveldef score(self):print('老师 %s 正在给学生打分' % self.name)tea_obj=Teacher('egon',18,'male',3000,10)# print(tea_obj.__dict__)# print(tea_obj.school)tea_obj.score()
多继承的菱形问题-mro
# 一:菱形问题介绍与MRO# mro列表打印,类名.mro(),mro是一个父类读取顺序列表class A(object):# def test(self):# print('from A')passclass B(A):def test(self):print('from B')passclass C(A):# def test(self):# print('from C')passclass D(C,B):# def test(self):# print('from D')pass## print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表## # obj = D()# # obj.test()## # print(D.test)## # print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表# # c=C()# # c.test()## # 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro# # 二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:# # 都是一个分支一个分支地找下去,然后最后找objectclass E:# def test(self):# print('from E')passclass F:def test(self):print('from F')class B(E):# def test(self):# print('from B')passclass C(F):# def test(self):# print('from C')passclass D:def test(self):print('from D')class A(B, C, D):# def test(self):# print('from A')pass# 新式类# print(A.mro()) # A->B->E->C->F->D->objectobj = A()obj.test() # 结果为:from F# 三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:# 经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)# 新式类:广度优先,会在检索最后一条分支的时候检索大脑袋class G: # 在python2中,未继承object的类及其子类,都是经典类# def test(self):# print('from G')passclass E(G):# def test(self):# print('from E')passclass F(G):def test(self):print('from F')class B(E):# def test(self):# print('from B')passclass C(F):def test(self):print('from C')class D(G):def test(self):print('from D')class A(B,C,D):# def test(self):# print('from A')pass# 新式类# print(A.mro()) # A->B->E->C->F->D->G->object(从左到右,每个参数纵向向上查找,不达到最深的一个父类,再往右)# 经典类:A->B->E->G->C->F->Dobj = A()obj.test() ## 总结:# 多继承到底要不用???# 要用,但是规避几点问题# 1、继承结构尽量不要过于复杂# 2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系
多继承mixins机制
# 多继承的正确打开方式:mixins机制# mixins机制核心:就是在多继承背景下尽可能地提升多继承的可读性# ps:让多继承满足人的思维习惯=》什么"是"什么class Vehicle: #交通工具类passclass FlyableMixin: #可以飞,作为所有飞机的mixin父类def fly(self):passclass CivilAircraft(FlyableMixin,Vehicle): # 民航飞机,mixin父类写在继承最左边passclass Helicopter(FlyableMixin,Vehicle): # 直升飞机passclass Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了passimport socketserver# 补充:通常Mixin结果的类放在左边
调用其他类的功能、super
# 在子类派生的新方法中如何重用父类的功能# 方式一:指名道姓调用某一个类下的函数=》不依赖于继承关系class OldboyPeople:def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sexdef f1(self):print('%s say hello' %self.name)class Teacher(OldboyPeople):def __init__(self,name,age,sex,level,salary):OldboyPeople.__init__(self,name,age,sex)self.level = levelself.salary=salarytea_obj=Teacher('egon',18,'male',10,3000)print(tea_obj.__dict__)# 方式二:super()调用父类提供给自己的方法=》严格依赖继承关系# 调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性class OldboyPeople:def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sexdef f1(self):print('%s say hello' %self.name)class Teacher(OldboyPeople):def __init__(self,name,age,sex,level,salary):# super(Teacher,self).__init__(name,age,sex) #python2super().__init__(name,age,sex) # 调用的是方法,自动传入对象self.level = levelself.salary=salary# print(Teacher.mro()) #打印[<class '__main__.Teacher'>, <class '__main__.OldboyPeople'>, <class 'object'>]tea_obj=Teacher('egon',18,'male',10,3000)print(tea_obj.__dict__)# super()案例class A:def test(self):print('from A')super().test()class B:def test(self):print('from B')class C(A,B):passobj=C()obj.test()print(C.mro())class A:def test(self):print('from A')super().test1() #报错,没有将C类当参数传进来,所以调用不了class B:def test(self):print('from B')class C(A,B):def test1(self):print('from C')obj=C()obj.test()print(C.mro())
多态
子类调用父类相同的方法
# 1、什么多态:同一事物有多种形态class Animal:passclass People(Animal):passclass Dog(Animal):passclass Pig(Animal):pass# 2、为何要有多态=》多态会带来什么样的特性,多态性# 多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象class Animal: # 统一所有子类的方法def say(self):print('动物基本的发声频率。。。',end=' ')class People(Animal):def say(self):super().say() #使用父类的方法,不写super调用父类的say,则相当于重写了syaprint('嘤嘤嘤嘤嘤嘤嘤')class Dog(Animal):def say(self):super().say()print('汪汪汪')class Pig(Animal):def say(self):super().say()print('哼哼哼')obj1=People()obj2=Dog()obj3=Pig()obj1.say()obj2.say()obj3.say()# 定义统一的接口,接收传入的动物对象# 此处多态的接口是使用一个方法来调用所有类似对象的相同方法# 封装所说的接口,是使用一个接口函数来调用其他多个函数def animal_say(animal):animal.say()animal_say(obj1)animal_say(obj2)animal_say(obj3)
绑定方法与非绑定方法
绑定方法有:
- 绑定给对象的方法即:类在实例化时self作为一个参数自动传入=====self
- 绑定给类的方法,类作为一个参数自动传入,:调用类的方法来实例化类(调用init方法,并传入固定参数)=====@classmethod
非绑定方法:
- 静态方法staticmethod:在类声明中的函数中,返回其他对象的方法。不需要传入第一个参数。
# 一:绑定方法:特殊之处在于将调用者本身当做第一个参数自动传入# 1、绑定给对象的方法:调用者是对象,自动传入的是对象# 2、绑定给类的方法:调用者类,自动传入的是类import settingsclass Mysql:def __init__(self,ip,port):self.ip=ipself.port=portdef func(self):print('%s:%s' %(self.ip,self.port))@classmethod # 将下面的函数装饰成绑定给类的方法def from_conf(cls):print(cls)return cls(settings.IP, settings.PORT)#导入的settings.py文件内容,IP='127.0.0.1',PORT=3306obj1=Mysql('1.1.1.1',3306)obj2=Mysql.from_conf()print(obj2.__dict__)# 二:非绑定方法-》静态方法:# 没有绑定给任何人:调用者可以是类、对象,没有自动传参的效果class Mysql:def __init__(self,ip,port):self.nid=self.create_id()self.ip=ipself.port=port@staticmethod # 将下述函数装饰成一个静态方法def create_id():import uuidreturn uuid.uuid4()@classmethoddef f1(cls):passdef f2(self):passobj1=Mysql('1.1.1.1',3306)# print(Mysql.create_id)# print(obj1.create_id)# Mysql.create_id(1,2,3)# obj1.create_id(4,5,6)print(Mysql.create_id)print(Mysql.f1)print(obj1.f2)
内置函数

print(abs(-1))print(all([1,'aaa','1']))print(all([]))print(any([0,None,1]))print(any([]))print(bin(11))print(oct(11))print(hex(11))print(bool(''))def func():passclass Foo:passprint(callable(Foo)) # 方print(chr(65))print(ord('A'))# 不可变集合s=frozenset({1,2,3}) #frozenset() 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素。# hash(不可变类型)print(round(1.5))print(round(1.4))# 10 ** 2 % 3print(pow(10,2,3))s=slice(1,4,2)l1=['a','b','c','d','e']l2=['aaa','bbb','ccc','ddd',444]#print(l1[1:4:2]) # l1[s]print(l2[1:4:2]) # l2[s]# =================》掌握v1='hello'v2=[111,222,333,444,5555,6666]res=zip(v1,v2)print(list(res))# =================》掌握print(divmod(10000,33))# =================》掌握class Foo:passobj=Foo()obj.xxx=1111print(dir(obj)) # obj.哪些属性# =================》掌握for i,v in enumerate(['a','b','c']):print(i,v)# =================》掌握res=eval('{"a":1}') # 执行字符串中的表达式print(res,type(res))# =================》掌握class Foo:passobj=Foo()print(isinstance(obj,Foo)) #isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。print(isinstance([],list)) # 类型判断推荐使用isinstanceprint(type([]) is list) # 不推荐使用# =================》掌握# import 'time' # 错误time=__import__('time')time.sleep(3)# 下个周:反射# setattr# getattr# delattr# hasattr
反射
反射推导过程
dir(对象)可以查看对象具有哪些属性列表;dic可以查看对象属性对应属性值的字典。
判断对象是否具有某种属性或者函数,然后再调用
# 什么是反射?# 指的是在程序运行过程中可以"动态(不见棺材不掉泪)"获取对象的信息# 为何要用反射?# 如何实现反射?class People:def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('<%s:%s>' %(self.name,self.age))obj=People('辣白菜同学',18)# 实现反射机制的步骤# 1、先通过多dir:查看出某一个对象下可以.出哪些属性来# print(dir(obj))# 2、可以通过字符串反射到真正的属性上,得到属性值# print(obj.__dict__[dir(obj)[-2]])
反射的四个函数
# 四个内置函数的使用:通过字符串来操作属性值# 1、hasattr() #判断对象obj是否有name属性# print(hasattr(obj,'name'))# print(hasattr(obj,'x'))# 2、getattr() #获取属性值# print(getattr(obj,'name'))# 3、setattr() #为对象的属性设置值# setattr(obj,'name','EGON') # obj.name='EGON'# print(obj.name)# 4、delattr() #删除对象属性# delattr(obj,'name') # del obj.name# print(obj.__dict__)# res1=getattr(obj,'say') # obj.say# res2=getattr(People,'say') # People.say# print(res1)# print(res2)# obj=10# if hasattr(obj,'x'):# print(getattr(10,'x'))# else:# pass# print(getattr(obj,'x',None))# if hasattr(obj,'x'):# setattr(obj,'x',111111111) # 10.x=11111# else:# passclass Ftp:def put(self):print('正在执行上传功能')def get(self):print('正在执行下载功能')def interactive(self):method=input(">>>: ").strip() # method='put'if hasattr(self,method):getattr(self,method)()else:print('输入的指令不存在')# obj=Ftp()# obj.interactive()
内置方法
类的内置方法,在对类使用某种操作时会触发
比如
- init方法,类实例化时触发
- str,print(类)时触发
- del,del 类 时触发

str
# 1、什么是内置方法?# 定义在类内部,以__开头并以__结果的方法# 特点:会在某种情况下自动触发执行# 2、为何要用内置方法?# 为了定制化我们的类or对象# 3、如何使用内置方法# __str__:在打印对象时会自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出class People:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):# print('运行了...')return "<%s:%s>" %(self.name,self.age)obj = People('辣白菜同学', 18)# print(obj.__str__())print(obj) # <'辣白菜同学':18># obj1=int(10)# print(obj1)
del方法
# __del__:在清理对象时触发,会先执行该方法class People:def __init__(self, name, age):self.name = nameself.age = ageself.x = open('a.txt',mode='w')# self.x = 占据的是操作系统资源def __del__(self):# print('run...')# 发起系统调用,告诉操作系统回收相关的系统资源self.x.close()obj = People('辣白菜同学', 18)# del obj # obj.__del__()print('============>')
call
功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。相当于使用普通函数
class CLanguage:# 定义__call__方法def __call__(self,name,add):print("调用__call__()方法",name,add)clangs = CLanguage() #类实例化# 以下含义相等clangs("C语言中文网","http://c.biancheng.net") #返回:调用__call__()方法 C语言中文网 http://c.biancheng.net# 相等clangs.__call__("C语言中文网","http://c.biancheng.net")
针对函数
def say():print("Python教程:http://c.biancheng.net/python")say()say.__call__() #函数自带call属性
用 call() 弥补 hasattr() 函数的短板
使用hasattr判断类是否有属性A,再使用hasattr(A,”call“)判断该属性是否具有函数调用属性,如果有则A是函数属性。
class CLanguage:def __init__ (self):self.name = "C语言中文网"self.add = "http://c.biancheng.net"def say(self):print("我正在学Python")clangs = CLanguage()if hasattr(clangs,"name"):print(hasattr(clangs.name,"__call__"))print("**********")if hasattr(clangs,"say"):print(hasattr(clangs.say,"__call__")) #say是函数属性
new
重写父类的new方法
class demoClass:instances_created = 0def __new__(cls,*args,**kwargs):print("__new__():",cls,args,kwargs)instance = super().__new__(cls)instance.number = cls.instances_created #为父类创建的空对象添加属性。cls.instances_created += 1return instancedef __init__(self,attribute):print("__init__():",self,attribute)self.attribute = attributetest1 = demoClass("abc")test2 = demoClass("xyz")print(test1.number,test1.instances_created)print(test2.number,test2.instances_created)
自定义元类中使用new
#定义一个元类class FirstMetaClass(type):# cls代表动态修改的类# name代表动态修改的类名# bases代表被动态修改的类的所有父类# attr代表被动态修改的类的所有属性、方法组成的字典def __new__(cls, name, bases, attrs):# 动态为该类添加一个name属性attrs['name'] = "C语言中文网"attrs['say'] = lambda self: print("调用 say() 实例方法")return super().__new__(cls,name,bases,attrs)
使用自定义元类
#定义类时,指定元类class CLanguage(object,metaclass=FirstMetaClass):passclangs = CLanguage()print(clangs.name)clangs.say()
元类
元类type介绍
#一:引入:# 一切都源自于一句话:一切皆为对象#二:什么是元类?# 元类就是用来实例化产生类的类# 关系:元类---实例化---->类(People)---实例化---->对象(obj)class People:def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s:%s' %(self.name,self.name))### print(People.__dict__)# 如何得到对象# obj=调用类()obj=People('egon',18)print(type(obj))# 如果说类也是对象# People=调用类(。。。)# 查看内置的元类:# 1、type是内置的元类# 2、我们用class关键字定义的所有的类以及内置的类都是由元类type实例化产生# print(type(People)) # 返回<class 'type'># print(type(int)) # 返回<class 'type'>
type创建类的过程
# 三:class关键字创造类People的步骤# 类有三大特征:# # 1、类名class_name="People"# # 2、类的基类class_bases=(object,)# # 3、执行类体代码拿到类的名称空间class_dic={}class_body="""def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s:%s' %(self.name,self.name))"""exec(class_body,{},class_dic)# print(class_dic)# # 4、调用元类type创建类People=type(class_name,class_bases,class_dic) #class_name="people",class_bases=(object,),类属性
自定义元类修改属性
自定义元类控制类的产生,只有继承了type类才是元类
# 四:如何自定义元类来控制类的产生class Mymeta(type): # 只有继承了type类的类才是元类# 空对象,"People",(),{...}def __init__(self,x,y,z):print('run22222222222....')print(self)# print(x)# print(y)# print(z)# print(y)# if not x.istitle():# raise NameError('类名的首字母必须大写啊!!!')# 当前所在的类,调用类时所传入的参数def __new__(cls, *args, **kwargs):# 造Mymeta的对象print('run1111111111.....')# print(cls,args,kwargs)# return super().__new__(cls,*args, **kwargs)return type.__new__(cls,*args, **kwargs) #调用type类的__new__### People=Mymeta("People",(object,),{...})# # 调用Mymeta发生三件事,调用Mymeta就是type.__call__# # 1、先造一个空对象=>People,调用Mymeta类内的__new__方法# # 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作# # 3、返回初始化好的对象#class People(metaclass=Mymeta): #需要指定metaclass元类是mymetadef __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s:%s' %(self.name,self.name))## # 强调:# # 只要是调用类,那么会一次调用# # 1、类内的__new__# # 2、类内的__init__
call
# 五:__call__class Foo:def __init__(self,x,y):self.x=xself.y=y# obj,1,2,3,a=4,b=5,c=6def __call__(self,*args,**kwargs):print('===>',args,kwargs)return 123obj=Foo(111,222)# print(obj) # obj.__str__res=obj(1,2,3,a=4,b=5,c=6) # res=obj.__call__()print(res)# 应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__# 总结:# 对象()->类内的__call__# 类()->自定义元类内的__call__# 自定义元类()->内置元类__call__
自定义元类控制类的调用
# 六:自定义元类控制类的调用=》类的对象的产生class Mymeta(type): # 只有继承了type类的类才是元类def __call__(self, *args, **kwargs):people_obj=self.__new__(self) # 1、Mymeta.__call__函数内会先调用People内的__new__self.__init__(people_obj,*args, **kwargs) # 2、Mymeta.__call__函数内会调用People内的__init__people_obj.__dict__['xxxxx']=11111 # print('people对象的属性:',people_obj.__dict__)return people_obj # 3、Mymeta.__call__函数内会返回一个初始化好的对象# 类的产生# People=Mymeta()=》type.__call__=>干了3件事# 1、type.__call__函数内会先调用Mymeta内的__new__# 2、type.__call__函数内会调用Mymeta内的__init__# 3、type.__call__函数内会返回一个初始化好的对象class People(metaclass=Mymeta):def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s:%s' %(self.name,self.name))def __new__(cls, *args, **kwargs):# 产生真正的对象return object.__new__(cls) #一般是调用父类的new方法# 类的调用# obj=People('egon',18) =》Mymeta.__call__=》干了3件事# 1、Mymeta.__call__函数内会先调用People内的__new__# 2、Mymeta.__call__函数内会调用People内的__init__# 3、Mymeta.__call__函数内会返回一个初始化好的对象obj1=People('egon',18)obj2=People('egon',18)# print(obj)print(obj1.__dict__)print(obj2.__dict__)
属性查找
# 属性查找的原则:对象-》类-》父类# 切记:父类 不是 元类class Mymeta(type):n=444def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>obj=self.__new__(self) # StanfordTeacher.__new__# obj=object.__new__(self)print(self.__new__ is object.__new__) #Trueself.__init__(obj,*args,**kwargs)return objclass Bar(object):# n=333# def __new__(cls, *args, **kwargs):# print('Bar.__new__')passclass Foo(Bar):# n=222# def __new__(cls, *args, **kwargs):# print('Foo.__new__')passclass StanfordTeacher(Foo,metaclass=Mymeta):# n=111def __init__(self,name,age):self.name=nameself.age=ageobj=StanfordTeacher('lili',18)print(obj.__dict__)# print(obj.n)# print(StanfordTeacher.n)
补充
链式操作
# coding:utf8# author:jasonclass MyClass(object):def func1(self):print('func1')return self #返回self对象def func2(self):print('func2')return selfobj = MyClass()obj.func1().func2()
