上节课复习:
1.编程思想
面向过程
核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么后干什么
基于该思想编写程序就好比在设计一条流水线,是一种机械式的思维方式
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差
面向对象
核心是对象二字,对象是特征(变量)与技能(函数)的结合体
基于该思想编写程序就好比在创造一个世界,世界是由一个个对象组成的,在上帝眼里所有存在的事物都是对象,任何不存在的事物也可以创造出来。
优点:可扩展性强
缺点:编程复杂度要高于面向过程
2.类
对象是特征与技能的结合体,而类则是一系列对象相同特征与技能的结合体
对象是具体存在的,而类是总结出来的抽象概念
类本质就是一个容器(名称空间)
对象本质也是一个容器(名称空间)
3.类与对象顺序
在现实世界中:一定是先有一个个具体存在的对象,然后随着人类文明的发展由人站在不同的角度总结出来的种类
在程序中:先定义类,后调用类产生对象
4.定义类
class OldboyStudent:
'''文档注释'''
#相同的特征
school='oldboy'
#相同的技能
def choose_course(self)
pass
注意: 类体代码会在类定义阶段立即执行,产生一个类的名称空间,用来将类体代码执行过程中产生的名字丢进去
5.使用类
两种用途:
1.当作一个容器使用
class OldboyStudent:
school='oldboy'
def choose_course(self):
pass
print(OldboyStudent.school)
OldboyStudent.country='China'
OldboyStudent.school='Oldboy'
del OldboyStudent.school
2.调用类来产生对象
class OldboyStudent:
school='oldboy'
#相同特征
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
#相同技能
def choose_course(self):
pass
stu1=OldboyStudent('andy',18,'male')
调用类会发生两件事
1.产生一个空对象
2.触发类的函数/对象的绑定方法__init__(stu1,'andy',18,'male')
强调__init__:
1.该方法是在产生对象后才调用
2.该方法可以由任意python代码,但唯独就是不能由返回值
6.属性查找
强调:类的属性与对象大的属性,访问时必须加前缀
先从对象自己的名称空间中找,如果没有则去类的名称空间中查找
7.绑定方法
类中定义的函数是类的函数属性,就是一个普通函数,没有自动传参的效果
但类中定义的函数其实是绑定给对象用的,称之为绑定方法,绑定方法的特殊之处
绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个位置参数自动传入
今日内容:
1.继承与派生
2.多态
3.封装
4.绑定方法与非绑定方法
classmethod
staticmethod
5.面向对象高级
反射
__str__
__del__
6.元类(视频)
一.继承
1.什么是继承
继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类、父类、超类。
继承描述的是一种“遗传”的关系:子类可以重用父类的属性
python中的继承注意点:
1.在python中支持一个子类同时继承多个父类,
2.python类分类两种:
新式类:但凡继承object的类,以及该类的子/子子..类都是新式类
在python3中一个类如果没有继承类,默认继承object类,即python3中所有类都是新式类
经典类:没有继承object的类,以及该类的子/子子..类都是经典类
python2中才区分新式类和经典类
2.为何要用继承
减少冗余代码
3.如何用继承
class Parent(object):
pass
class Parent2:
pass
class Subclass1(Parent1,Parent2)
pass
print(Subclass1.__bases__)
问题:
1.如何利用继承减少代码冗余
继承解决的式类与类之间的代码冗余问题,一定是一个类是另外一个类的子类
总结对象之间的相似之处就得到类,总结类与类之间的相似之处就是父类
2.在继承的背景下,属性查找优先级
3.新式类与经典类的区别
1 class OldboyStudent: 2 school='Oldboy' 3 def __init__(self,name,age,sex): 4 self.name=name 5 self.age=age 6 self.sex=sex 7 self.score=0 8 def choose_course(self): 9 print('%s is choosing course' %self.name)10 class OldboyTeacher:11 school='Oldboy'12 def __init__(self,name,age,sex,level):13 self.name=name14 self.age=age15 self.sex=sex16 self.level=level17 def score(self,stu,num):18 stu.score=num19
1 class OldboyPeople: 2 school='Oldboy' 3 class OldboyStudent(OldboyPeople): 4 def __init__(self,name,age,sex): 5 self.name=name 6 self.age=age 7 self.sex=sex 8 self.score=0 9 def choose_course(self):10 print('%s is choosing course' %self.name)11 class OldboyTeacher(OldboyPeople):12 def __init__(self,name,age,sex,level):13 self.name=name14 self.age=age15 self.sex=sex16 self.level=level17 def score(self,stu,num):18 stu.score=num
1 #使用继承,减少人的属性 2 class OldboyPeople: 3 school='Oldboy' 4 def __init__(self,name,age,sex): 5 self.name=name 6 self.age=age 7 self.sex=sex 8 class OldboyStudent(OldboyPeople): 9 #def __init__(self,name,age,sex):10 # self.name=name11 # self.age=age12 # self.sex=sex13 # self.score=014 def choose_course(self):15 print('%s is choosing course' %self.name)16 class OldboyTeacher(OldboyPeople):17 #def __init__(self,name,age,sex,level):18 # self.name=name19 # self.age=age20 # self.sex=sex21 # self.level=level22 def score(self,stu,num):23 stu.score=num24 stu1= OldboyStudent('andy',18,'male') 25 print(stu1.__dict__)26 tea1= OldboyTeacher('alex',30,'male') 27 print(tea1.__dict__)
输出:
{'name': 'andy', 'age': 18, 'sex': 'male'}
{'name': 'alex', 'age': 30, 'sex': 'male'}
1 #此时不仅需要继承,还需要派生 2 #如何在子类派生出的新方法中重用父类的功能? 3 #方式一:指名道姓地访问某一个类的函数,该方式需要注意: 4 #1.该方式与继承是没有关系的 5 #2.访问时某一个类的函数,没有自动传值的效果 6 class OldboyPeople: 7 school='Oldboy' 8 def __init__(self,name,age,sex): 9 self.name=name10 self.age=age11 self.sex=sex12 class OldboyStudent(OldboyPeople):13 def __init__(self,name,age,sex,num=0):14 OldboyPeople.__init__(self,name,age,sex)15 self.score=num16 def choose_course(self):17 print('%s is choosing course' %self.name)18 class OldboyTeacher(OldboyPeople):19 def __init__(self,name,age,sex,level):20 OldboyPeople.__init__(self,name,age,sex)21 self.level=level22 def score(self,stu,num):23 stu.score=num24 stu1= OldboyStudent('andy',18,'male') #OldboyStudent.__init__(stu1,'andy',18,'male')25 print(stu1.__dict__)26 tea1= OldboyTeacher('alex',30,'male',10) #OldboyTeacher.__init__(tea1,'alex',30,'male')27 print(tea1.__dict__)
输出:
{'name': 'andy', 'age': 18, 'sex': 'male', 'score': 0}
{'name': 'alex', 'age': 30, 'sex': 'male', 'level': 10}
1 #此时不仅需要继承,还需要派生 2 #如何在子类派生出的新方法中重用父类的功能? 3 #方式二:只能在子类中用 4 #在python2中:super(自己的类名,对象自己) 5 #在python3中:super() 6 #调用super()会得到一个特殊的对象,该特殊的对象时专门用来引用父类中的属性,完全参照mro列表 7 #注意: 8 # 1.该方式与继承严格依赖于继承的mro列表 9 # 2.访问是绑定方法,有自动传值的效果10 class OldboyPeople:11 school='Oldboy'12 def __init__(self,name,age,sex):13 self.name=name14 self.age=age15 self.sex=sex16 class OldboyStudent(OldboyPeople):17 def __init__(self,name,age,sex,num=0):18 #OldboyPeople.__init__(self,name,age,sex)19 super(OldboyStudent,self).__init__(name,age,sex)#跳过自己的类找父类属性20 self.score=num21 def choose_course(self):22 print('%s is choosing course' %self.name)23 class OldboyTeacher(OldboyPeople):24 def __init__(self,name,age,sex,level):25 super().__init__(name,age,sex)26 self.level=level27 def score(self,stu,num):28 stu.score=num29 stu1= OldboyStudent('andy',18,'male') #OldboyStudent.__init__(stu1,'andy',18,'male')30 print(stu1.__dict__)31 tea1= OldboyTeacher('alex',30,'male',10) #OldboyTeacher.__init__(tea1,'alex',30,'male')32 print(tea1.__dict__)
输出:
{'name': 'andy', 'age': 18, 'sex': 'male', 'score': 0}{'name': 'alex', 'age': 30, 'sex': 'male', 'level': 10}
1 #单继承背景下的属性查找顺序:对象-》对象的类-》对象类的父类 2 class Foo: 3 def f1(self): 4 print('Foo.f1') 5 6 def f2(self): 7 print('Foo.f2') 8 self.f1() #obj.f1() 9 10 class Bar(Foo):11 def f1(self):12 print('Bar.f1')13 14 15 obj=Bar()16 obj.f2()17 18 Foo.f219 Bar.f1
输出:
Foo.f2Bar.f1
1 #多继承背景下的属性查找顺序:对象-》对象的类-》按从左往右的顺序一个个的分支找下去 2 class I: 3 pass 4 5 class E: 6 7 x='E' 8 class F(I): 9 pass10 class H:11 pass12 13 class B(E):14 pass15 class C(F):16 pass17 class D(H):18 pass19 20 class A(B,C,D):21
一旦出现菱形继承问题,新式类与经典类在属性查找上的区别是
新式类:广度优先查找,在最后一个分支查找顶级类
经典类:深度优先查找,在第一个分支查找顶级类
在新式类中,提供了一个mro方法查看属性查找算法路线
二.组合
1.什么是组合
组合指的是一个对象拥有某一个属性,该属性的值是另外一个类的对象
obj=Foo()
obj.attr1=Bar1()
obj.attr2=Bar2()
obj.attr3=Bar3()
obj.attr.Bar中的属性
2.为何用组合
为了减少类与类之间代码冗余的问题
3.如何用组合
1 #一个学生只选修一门课程 2 class OldboyPeople: 3 school='Oldboy' 4 def __init__(self,name,age,sex): 5 self.name=name 6 self.age=age 7 self.sex=sex 8 class OldboyStudent(OldboyPeople): 9 def __init__(self,name,age,sex):10 #OldboyPeople.__init__(self,name,age,sex)11 super().__init__(name,age,sex)#跳过自己的类找父类属性12 self.score=013 14 def choose_course(self):15 print('%s is choosing course' %self.name)16 class OldboyTeacher(OldboyPeople):17 def __init__(self,name,age,sex,level):18 super().__init__(name,age,sex)19 self.level=level20 def score(self,stu,num):21 stu.score=num22 class Course:23 def __init__(self,c_name,c_price,c_period):24 self.c_name=c_name25 self.c_price=c_price26 self.c_period=c_period27 def tell_info(self):28 print('课程名:%s 价钱:%s 周期:%s' %(self.c_name,self.c_price,self.c_period))29 #创建课程30 python_obj=Course('python全栈开发',19800,'5mons')31 linux_obj=Course('Linux架构师',10000,'3mons')32 33 34 stu1=OldboyStudent('andy',18,'male') 35 stu2=OldboyStudent('tom',20,'male') 36 stu3=OldboyStudent('walter',22,'male')37 38 stu1.course=python_obj39 stu2.course=python_obj40 stu3.course=python_obj41 42 stu1.course.tell_info()
输出:
andy
课程名:python全栈开发 价钱:19800 周期:5mons
课程名:Linux架构师 价钱:10000 周期:3mons
1 #一个学生选修多门课,方法二 2 class OldboyPeople: 3 school='Oldboy' 4 5 def __init__(self, name, age, sex): 6 self.name = name 7 self.age = age 8 self.sex = sex 9 10 11 class OldboyStudent(OldboyPeople):12 13 def __init__(self,name,age,sex,):14 super().__init__(name,age,sex)15 self.score=016 self.courses=[]17 18 def choose_course(self):19 print('%s is choosing course' %self.name)20 21 def tell_all_course(self): #打印某学生所有课程信息22 for course_obj in self.courses:23 course_obj.tell_info()24 25 26 class OldboyTeacher(OldboyPeople):27 28 def __init__(self,name,age,sex,level):29 super().__init__(name,age,sex)30 self.level=level31 32 33 def score(self,stu,num):34 stu.score=num35 36 class Course:37 def __init__(self,c_name,c_price,c_period):38 self.c_name = c_name39 self.c_price = c_price40 self.c_period = c_period41 42 def tell_info(self):43 print(' <课程名:%s 价钱:%s 周期:%s> ' %(self.c_name,self.c_price,self.c_period))44 45 # 创建课程46 python_obj=Course('Python全栈开发',19800,'5mons')47 linux_obj=Course('Linux架构师',10000,'3mons')48 49 50 stu1=OldboyStudent('andy',18,'female')51 stu2=OldboyStudent('tom',38,'male')52 stu3=OldboyStudent('walter',48,'male')53 54 55 stu1.courses.append(python_obj)56 stu1.courses.append(linux_obj)57 stu2.courses.append(python_obj)58 stu2.courses.append(linux_obj)59 60 61 stu1.tell_all_course()62 stu2.tell_all_course() 课程名:%s>
输出:
<课程名:Python全栈开发 价钱:19800 周期:5mons>
<课程名:Linux架构师 价钱:10000 周期:3mons>
<课程名:Python全栈开发 价钱:19800 周期:5mons>
<课程名:Linux架构师 价钱:10000 周期:3mons>
三.多态与多态性
1.多态
指的是同一种事物的多种形态
2.多态性
可以在不用考虑对象具体类型的情况下直接使用对象
优点:
归一化,简化对象的使用
1 import abc 2 3 class Animal(metaclass=abc.ABCMeta): 4 @abc.abstractmethod 5 def speak(self): 6 pass 7 @abc.abstractmethod 8 def run(self): 9 pass10 #抽象基类:只用来制定规范,但凡继承该类的子类都必须实现speak和run,而名字必须叫speak和run11 #注意:不能实例化抽象类12 class People(Animal):13 def speak(self):14 print('say hello')15 def run(self):16 print('run')17 class Dog(Animal):18 def speak(self):19 print('汪汪汪')20 def run(self):21 pass22 class Pig(Animal):23 def speak(self):24 print('哼哼哼')25 def run(self):26 pass27 28 obj1=People()29 obj2=Dog()30 obj3=Pig()
1 import abc 2 3 class Animal(metaclass=abc.ABCMeta): 4 @abc.abstractmethod 5 def speak(self): 6 pass 7 @abc.abstractmethod 8 def run(self): 9 pass10 #抽象基类:只用来制定规范,但凡继承该类的子类都必须实现speak和run,而名字必须叫speak和run11 #注意:不能实例化抽象类12 class People(Animal):13 def speak(self):14 print('say hello')15 def run(self):16 print('run')17 class Dog(Animal):18 def speak(self):19 print('汪汪汪')20 def run(self):21 pass22 class Pig(Animal):23 def speak(self):24 print('哼哼哼')25 def run(self):26 pass27 28 def speak(animal):29 animal.speak()30 speak(obj1)31 speak(obj2)32 speak(obj3)
输出:
1 obj1=[1,2,3] 2 obj2='hello' 3 obj3={ 'x':1} 4 print(obj1.__len__()) 5 print(obj2.__len__()) 6 print(obj3.__len__()) 7 8 print(len(obj1)) 9 print(len(obj2))10 print(len(obj3))
输出:
3
5
1
3
5
1
四.封装
1.什么是封装
装就是将数据属性或函数属性存放到一个名称空间里
封指的是隐藏,该隐藏是为了明确地区分内外,即该隐藏是对外不对内(在类外部无法直接访问隐藏的属性,而在类内部是可以访问)
2.为何要封装
1.封数据属性
2.封函数属性
3.如何封装
在类内定义的属性前加__开头
1 class People: 2 __country='china' #_People__country='China' 3 __n=111 #_People__n=111 4 def __init__(self,name): 5 self.__name=name #self._People__name=name 6 def run(self): 7 print('%s is running' %self.__name) #self._People__name 8 obj=People('alex') 9 obj.run()10 #这种隐藏需要注意的问题:11 #1.这种隐藏只是一种语法上的变形,并没有真的限制访问12 print(People._People__country,obj._People__name)13 #2.这种变形只在类定义阶段检测语法时变形一次,类定义阶段之后新增的__开头的属性不会发生变形14 People.__x=115 obj.__y=216 print(People.__dict__)17 print(obj.__dict__)
输出:
alex is running
china alex
{'__module__': '__main__', '_People__country': 'china', '__init__': <function People.__init__ at 0x000001DE266607B8>, 'run': <function People.run at 0x000001DE26660620>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, '__x': 1}
{'_People__name': 'alex', '__y': 2}
1 #在继承中,父类如果不想让子类覆盖自己的方法,可以在该方法前加__开头2 class Parent1:3 def __func(self): #_Parent1__func4 print('parent1.func')5 class Sub1(Parent):6 def __func(self): #_Sub1__func7 print('sub1.func')
1 class Foo: 2 def __f1(self): #调用此入口 3 print('Foo.f1') 4 def f2(self): 5 print('Foo.f2') 6 self.__f1() 7 class Bar(Foo): 8 def __f1(self): 9 print('Bar.f1')10 obj=Bar()11 obj.f2()
输出:
Foo.f2
Foo.f1
1 class Foo: 2 def f1(self): 3 print('Foo.f1') 4 def f2(self): 5 print('Foo.f2') 6 self.f1() 7 class Bar(Foo): 8 def f1(self): 9 print('Bar.f1') #调用此入口10 obj=Bar()11 obj.f2()
输出:
Foo.f2
Bar.f1
1 #封装的真实意图:把数据属性或函数属性装起来就是为了以后使用,封起来即藏起来是为了不让外部直接使用 2 #1.封数据属性:把数据属性隐藏起来,不让外部直接操作隐藏的属性,而是靠类内开的接口间接操作属性, 3 # 可以在接口之上增加任意的控制逻辑来严格控制使用者对属性的操作 4 class People: 5 def __init__(self,name,age): 6 self.__name=name 7 self.__age=age 8 def tell_info(self): 9 print(' ' %(self.__name,self.__age))10 def set_info(self,name,age):11 if type(name) is not str:12 print('name必须是str')13 return14 if type(age) is not int:15 print('age必须是int')16 return17 self.__name=name18 self.__age=age19 20 obj=People('andy',18)21 obj.set_info('tom','age')22 obj.set_info('tom',20)23 obj.tell_info()
输出:
age必须是int
<name:tom age:20>
1 #2.封函数属性:隔离复杂度 2 #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱 3 #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做 4 #隔离了复杂度,同时也提升了安全性 5 6 class ATM: 7 def __card(self): 8 print('插卡') 9 def __auth(self):10 print('用户认证')11 def __input(self):12 print('输入取款金额')13 def __print_bill(self):14 print('打印账单')15 def __take_money(self):16 print('取款')17 18 def withdraw(self):19 self.__card()20 self.__auth()21 self.__input()22 self.__print_bill()23 self.__take_money()24 25 a=ATM()26 a.withdraw()
1 #五.property装饰器 2 # 将一个函数属性伪装成数据属性,只能在类中使用 3 #BMI =体重(kg)/身高^2(m) 4 class People: 5 def __init__(self,name,weight,height): 6 self.name=name 7 self.weight=weight 8 self.height=height 9 @property10 def bmi(self):11 return self.weight/(self.height**2)12 obj=People('alex',80,1.8)13 #print(obj.bmi())14 obj.bmi
输出:
24.691358024691358
六. classmethod与staticmethod 类中定义的函数有两大类(3小种)用途,一类时绑定方法,另外一类时非绑定方法 1.绑定方法: 特点:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数传入 1.1绑定给对象:类中定义的函数默认就是绑定对象的 1.2绑定给类:在类中定义的函数前将一个装饰器classmethod
2.非绑定方法 特点:既不与类绑定也不与对象绑定,意味着对象和类都能调用,就是一个普通函数,根本没有自动传值一说
1 class Foo: 2 def func1(self): 3 print('绑定给对象的方法',self) 4 @classmethod 5 def func2(cls): 6 print('绑定给类的方法',cls) 7 @staticmethod 8 def func3(x,y,z): 9 print('普通函数')10 obj=Foo()11 #obj.func1()12 #print(obj)13 14 #Foo.func2()15 16 #绑定方法17 print(obj.func1)18 print(Foo.func2)19 20 #非绑定方法21 print(obj.func3)22 print(Foo.func3)
输出:
<bound method Foo.func1 of <__main__.Foo object at 0x000001DE270A1D30>>
<bound method Foo.func2 of <class '__main__.Foo'>>
<function Foo.func3 at 0x000001DE2668F400>
<function Foo.func3 at 0x000001DE2668F400>
1 class Mysql:2 def __init__(self,ip,port):3 self.ip=ip4 self.port=port5 def tell_info(self):6 print('%s:%s' %(self.ip,self.port))7 8 obj=Mysql('1.1.1.1',3306)9 obj.tell_info()
输出:
1.1.1.1:3306
1 class Foo:2 pass3 class Bar(Foo):4 pass5 obj=Bar()6 print(isinstance(obj,Bar)) #判断obj是否时Bar的实例7 print(isinstance(obj,Foo)) #判断obj是否时Foo的实例8 print(issubclass(Bar,Foo)) #判断Bar是否是Foo的子类
输出:
True
True
True
七.反射
定义:指的是通过字符串来操作属性
1 class Foo:2 def __init__(self,name):3 self.name=name4 obj=Foo('alex')5 print(hasattr(obj,'name')) #判断'name' in obj.__dict__6 print(getattr(obj,'name'))7 setattr(obj,'name','tom')8 setattr(obj,'age',18)9 print(obj.__dict__)
输出:
True
alex
{'name': 'tom', 'age': 18}
1 class Ftp: 2 def get(self): 3 print('get') 4 def put(self): 5 print('put') 6 def login(self): 7 print('login') 8 def run(self): 9 while True:10 choice=input('>>>:').strip()11 if hasattr(self,choice):12 method=getattr(self,choice)13 method()14 else:15 print('命令不存在')16 obj=Ftp()17 obj.run()
1 #八.内置方法 2 # 1.__str__:在对象被打印时自动触发,然后该绑定方法返回值当作本次打印的结果 3 class People: 4 def __init__(self,name,age): 5 self.name=name 6 self.age=age 7 def __str__(self): 8 return ' ' %(self.name,self.age) 9 obj=People('andy',18)10 print(obj)
输出:
<name:andy,age:18>
1 # 2.__del__:在对象被删除前触发,在该方法内应该执行与该对象有关的系统资源的回收操作 2 class Foo: 3 def __del__(self): 4 print('run...') 5 obj=Foo() 6 del obj #obj.__del__() 7 print('其他代码1') 8 print('其他代码2') 9 print('其他代码3')10 print('其他代码4')
输出:
run...
run...
其他代码1
其他代码2
其他代码3
其他代码4