- 面向对象介绍
- 封装
- 继承
- 多态
- 反射
- 内置方法
- 元类
''' 面向过程: 核心是"过程"二字 过程的核心思想是将程序流程化 过程是”流水线“,用来分步骤解决问题 面向对象: 1、核心是”对象“二字 2、对象的终极奥义就是将程序”整合“ 3、对象就是”容器“,用来盛放数据与功能 优点: 1、提升程序的解耦合程度,进而增强程序的可扩展性 缺点: 2、可扩展性差 4、python这门语言到底提供了什么语法来允许我们将数据与功能整合在一起? 5、类: 类是对象相似数据与功能的集合体 所以类体中常见的是变量与函数定义,但是类体其实是可以包含任意其他代码的 强调!!:类体代码是再类定义阶段就会立即执行,会产生类的名称空间 6、使用顺序 # 一、先定义类 class Student: stu_school = 'oldboy' def __init__(obj, x): #对象下面的属性 obj.stu_name = x 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'] = x stu_obj['stu_age'] = y stu_obj['stu_gender'] = z # 功能的定义 pass # 属性访问的语法: # 访问数据属性 print(Student.set_school) # 访问函数属性 print(Student.set_info) # 二、再调用类产生对象 stu1_obj = Student('hongwei') print(stu1_obj.set_info) print(stu1_obj.__dict__) #调用类的过程由称之为实例化,发生了三件事 # 1、先产生一个空对象 # 2、python会调用类中的__init__方法,然后将空对象已经调用类括号内传入的参数一同传给init # 3、返回初始完的对象 #总结__init__方法 # 1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据 # 2、__init__内因该存放是为对象初始化属性的功能,但是是可以存放任意其他代码,想要在 # 类调用时就立刻执行的代码都可以放到该方法内 # 3、__init__方法必须返回Now # 三、绑定方法: #谁来调用绑定方法就会将谁当做第一个参数自动传入 stu1_obj.tell_stu_info() ''' 案例 #学校信息 class School: school_name='ke' def __init__(self,nickname,addr): self.addr = addr self.nickname = nickname self.classes = [] def related_class(self,class_obj): self.classes.append(class_obj) def tell_class(self): print(self.nickname) for class_obj in self.classes: class_obj.tell_course() #班级信息 class Class: def __init__(self,name): self.name=name #班级名字 self.coures = None def related_course(self,course_obj): #班级课程 self.coures = course_obj def tell_course(self): print('班级名:%s '%(self.name)) # print('课程信息:%s'%(self.coures)) self.coures.tell_info() #课程信息 class Course: def __init__(self,name,period,price): self.name = name self.period = period self.price = price def tell_info(self): print('<课程名:%s 周期:%s 价钱:%s >'%(self.name,self.period,self.price)) class Student: pass # 一、班级 *** 作 # 创建班级 class_obj1 = Class('脱产14期') class_obj2 = Class('脱产15期') #创建课程 class_obj1.related_course('python全栈开发') class_obj2.related_course('HCIE云计算') # 二、校区 *** 作 # 创建校区 school_obj1 = School('宏伟魔都校区','瑞昌') school_obj2 = School('宏伟帝都校区','北京') # 为学校开设班级 school_obj1.related_class(class_obj1) school_obj2.related_class(class_obj2) # 查看每个校区开设的班级 # school_obj2.tell_class() # print(school_obj1.nickname) #三、课程 #1、创建课程 course_obj1 = Course('python全栈开发','6mons',2000) course_obj2 = Course('HCIE云计算','6mons',2000) #2、查看课程的详细信息 # course_obj1.tell_info() # course_obj2.tell_info() #3、为班级关联课程对象 class_obj1.related_course(course_obj1) class_obj2.related_course(course_obj2) #四、验证 # school_obj2.tell_class()封装
# 一、封装介绍 #封装是面向对象三大特性最核心的一个特性 #封装 = 整合 #二、将封装的属性进行隐藏 *** 作 #如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果 #需要注意的问题: #1、在类外部无法直接访问双下划线开头的属性,但知道了类名和属性名就可以拼出属性 #所以说这种 *** 作并没有严格意义上的限制外部访问,仅仅只是一种语法意义 #2、这种隐藏对外不对内,应为__开头的属性会在检查类体代码语法时同意变形 #3、这种变形 *** 作旨在检查类语法的时候发生一次,之后定义的__开头的都不会变形 #为什么要隐藏? #隔离复杂度 class Foo: __x=111 #_Foo__x def __init__(self,y): self.__y = y # 三、property(类的装饰器) # 案例一、 class People: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height #定义函数的原因: # 1、从bmi的公式上看,bmi是应该出发功能计算得到的 # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值 # 3、 @property def bmi(self): return self.weight / (self.height **2 ) obj = People('hw',85,1.86) print(obj.bmi) # 案例二 class People: def __init__(self,name): self.__name = name def get_name(self): return self.__name def set_name(self,val): if type(val) is not str: print('必须传入str类型') return self.__name = val def del_name(self): print('不让删除') name = property(get_name,set_name,del_name) obj = People('kehongwei') obj.name = 18 del obj.name print(obj.name)继承
''' 1、什么是继承 继承是一个类继承另一个类 python支持多继承 在python中,新建的类可以继承一个或多个父类 class a: pass class b: pass class c(a): #单继承 pass class d(a,b): #多继承 pass 2、为何要用继承?:用来解决类与类之间代码冗余问题 3、多继承? 优点:子类可以同时遗传多个父类属性 缺点: 1、违背人的思维习惯 2、代码可读性变差 3、不建议使用多继承,扩展性变差 如果不可避免的用到多继承,应该使用mixins 4、如何实现继承(怎么使用) # 基于继承解决类与类之间的冗余问题(在此基础上加上学生学号) class Func: school = '宏伟' def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex class Student(Func): # school='宏伟' # def __init__(self,name,age): # self.name = name # self.age = age def choose_course(self): print('%s 正向选课'%self.name) # obj = Student('HW',18,'female') # print(obj.__dict__) # print(obj.school) # obj.choose_course() class Teacher(Func): school = '宏伟' def __init__(self,name,age,sex,salary): Func.__init__(self,name,age,sex) self.name = name self.age = age self.sex = sex self.salary = salary def choose_course(self): print('%s 正向选课'%self.name) obj2=Teacher('hh',9,'male',4000) print(obj2.__dict__) 5、单继承背景下的属性查找 # 示例1 class Foo: def f1(self): print('FOO.F1') def f2(self): print('FOO.F2') self.f1() #obj.f1() Foo.f1(self) #想调用当前类的f1 class Bar(Foo): def f1(self): print('Bar.f1') obj = Bar() obj.f2() # 示例2 class Foo: def __f1(self): print('FOO.F1') def f2(self): print('FOO.F2') self.__f1() class Bar(Foo): def f1(self): print('Bar.f1') obj = Bar() obj.f2() 6、多继承背景下的属性查找 1、先查找父类,父类没有就找父类的父类,总之就是先把父类这个分支找完 2、通过mro表来查找 7、如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样 1、新式类与经典类 父类或者以上由boject就是新式类,否则就是经典类 2、深度优先与广度优先 1、经典类:深度优先。会在检索第一条分支的时候直接一条道走到黑 2、新式类:广度优先,会在检索最后一条分支的时候检索object 8、多继承 多继承要不要用? 要用,但是需规避几点问题 1、继承结构经历不要过于复杂 2、需要使用mixinx:要在多继承的背景下满足继承什么是什么的关系(单继承)=》mixinx 9、 mixins机制核心: # mixins提升多继承的可读性 #满足人的思维,什么是什么 10、子类中重用父类属性有两种方式: #方式一:指名道姓的调用 #方式二:super()调用父类提供给自己的方法 =》严格以来继承关系 #调用super()会得到一个特殊对象,该对象会参照发起属性查找的那个类的mro取重用父类属性 '''多态
1、多态:指的是一类事务有多种形态 动物的多种形态:牛,马 文件的多种形态:文本文件,可执行文件 2、多态性:指在不考虑实例类型的情况下使用实例 举个栗子:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班 *** 作,学生执行的是放学 *** 作,虽然二者消息一样,但是执行的效果不同 3、为什么要用多态性(多态性的好处) 1.增加了程序的灵活性 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal) 2.增加了程序额可扩展性 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用 class Cat(Animal): #属于动物的另外一种形态:猫 ... def talk(self): ... print('say miao') ... def func(animal): #对于使用者来说,自己的代码根本无需改动 ... animal.talk() ... cat1=Cat() #实例出一只猫 func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能 say miao ''' 这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1) ''' 4、鸭子类型 如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子,程序也是如此 #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass反射
# 反射 #1、什么是反射 #指的是程序在运行过程中可以动态获取对象的信息 #2、为什么要用反射? #3、实现反射机制的步骤 #1、通过dir:查看对象可以.出那些属性来 print(dir(obj)) #2、通过字符串反射到真正的属性上,得到属性值 print(obj.__dict__[dir(obj)[-2]]) #4、四个内置函数的使用:通过字符串来 *** 作属性值 #1、hasattr() 查看对象下有没有name属性 print(hasattr(obj,'name')) #2、setattr() 给属性赋值 *** 作, obj.name = 'asd' print(setattr(obj,'bool','asd')) #3、getattr() 获取对象的属性值 print(getattr(obj,'name')) #4、delattr() 删除对象属性 delattr(obj,'name')内置方法
#内置方法 #1、什么是内置方法? #定义在类内部,__开头并以__结尾的方法 #特点:会在某种情况自动触发执行 #2、为什么需要用内置方法 #为了定制我们的类和对象 #3、如何使用内置方法? #__str__:在打印对象时会自动触发,然后将返回值(必须是字符串类型)当作本次结果 #__del__:在清理对象时出发,会先执行该方法 #__new__:在执行类的时候会第一个执行该方法 #__call__:将对象做成一个可以加括号调用的对象,加括号时会调用call这个方法,然后将该方法得到的值返回元类
# 元类 #1、什么是元类? #就是用来实例化产生类的类 #关系:元类---实例化---》类---实例化---》对象 #2、查看内置元类: #1、type是内置的元类 #2、class关键字定义的所有类都是由元类type实例化产生的 print(type()) #查看内置的元类 #3、class关键字创造类的步骤 #1、引入类的三个特征 类名,类的基类,执行类体代码拿到类的命名空间 #2、执行步骤 #1、拿到类名: class_name = 'obj' #2、拿到类的基类: class_bases=(object,) #3、拿到执行类体代码拿到类的命名空间 class_dic = {} #4、调用元类 a = type(class_name,class_bases,class_dic) #4、如何自定义元类来控制类的产生 #第四步:调用Mymeta发生三件事,调用Mymeta就是type.__call__(该方法也会做以下三个步骤) #1、先造一个空对象People,调用类内的__new__方法 #2、调用mymeta这个类内的__init__方法,完成初始化对象的 *** 作 #3、返回初始化好的对象 #强调:只要是调用类,会依次调用类内的 __new__ ,__init__ class Mymeta(type): # 会将空对象,class_name,class_bases,class_dic这四个参数传入 def __init__(self, x, y, z): if not x.capitalize(): raise NameError('类名的首字母必须大写!') # print('run') # print(self) # print(x) # print(y) # print(z) def __new__(cls, *args, **kwargs): # 造Mymeta的对象 print(cls, args, kwargs) # return super().__new__(cls, *args, **kwargs) return type.__new__(cls, *args, **kwargs) class People(metaclass=Mymeta): def __init__(self, name, age): self.name = name self.age = age #5、__call__:将对象做成一个可以加括号调用的对象,加括号时会调用call这个方法,然后将该方法得到的值返回 # type.__call__函数干的三件事 #1、先造一个空对象People,调用类内的__new__方法 #2、调用mymeta这个类内的__init__方法,完成初始化对象的 *** 作 #3、返回初始化好的对象 #总结 #对象()-》类内的__call__ #类()-》自定义元类内的__call__ #自定义元类() -》内置元类内的__call__ class Foo: def __init__(self,x,y): self.x = x self.y = y def __call__(self, *args, **kwargs): return 123 obj = Foo(123123, 123123) a = obj() print(a) #6、自定义元类控制类的调用 #people = Mymeta() => type.__call__ => 干了三件事 #1、type.__call__函数内会调用Mymeta内的__new__ #2、type.__call__函数内会调用Mymeta内的__init__ #3、type.__call__函数内会返回一个初始化好的对象 #obj = People('asd',18) ->Mymeta.__call__->干了三件事 # 1、Mymeta.__call__函数内会调用People内的__new__ # 2、Mymeta.__call__函数内会调用People内的__init__ # 3、Mymeta.__call__函数内会返回一个初始化好的对象 class Mymeta(type): def __call__(self, *args, **kwargs): # 1、type.__call__函数内会调用Mymeta内的__new__ people_obj = self.__new__(self) # 2、type.__call__函数内会调用Mymeta内的__init__ self.__init__(people_obj,*args, **kwargs) people_obj.__dict__['xxx']=111 # 3、type.__call__函数内会返回一个初始化好的对象 return people_obj class People(metaclass=Mymeta): def __init__(self, name, age): self.name = name self.age = age def __new__(cls, *args, **kwargs): #产生真正的对象 return object.__new__(cls) #小试牛刀: # 将People添加个属性,xxx=111 obj = People('hw', 18) abc = People('hw', 19) print(obj.__dict__) print(abc.__dict__) #7、属性查找 #正常的属性查找规则:对象 》 类 》 父类 #强调:该属性查找不会衍生到元类 #调类时的属性查找:类》父类》元类 #8、作业 #1、在元类中控制把自定义类的数据属性都变成大写 #2、在元类中控制自定义的类无需__init__方法 #3、在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性 #4、基于元类实现单例模式
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)