复习:
1、 编程思想
面向过程
核心是过程二字,过程指的是解决问题的步骤,即先干什么,再干什么,后干什么
基于该思想编写程序就好比在设计一条流水线,是一种机械式思维
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差
面向对象
核心是对象二字,对象是特征(变量)与技能(函数)的结合体
基于该思想编写程序就好比在创造一个世界(你就是这个世界的上帝),世界是由一个个对象组成的,在上帝眼里所有存在的事务都是对象,任何不存在的事物也可以创造出来
优点:可扩展性强
缺点:编程负责度高
2、 类
对象是特征与技能的结合体,而类则一些列对象相同特征与技能的集合体
对象是具体存在的,而类是总结出来的抽象的概念
类的本质就是一个容器(名称空间)
对象的本质也是一个容器(名称空间)
3、 类与对象
在现实世界中:一定先有一个个具体存在的对象,然后随着人类文明的发展由人站在不同的角度总结出来的种类的概念
在程序中:先定义类,后调用类产生对象
4、 定义类
class OldboyStudent:
'''文档注释'''
#相同特征
school='oldboy'
#相同的技能
def choose_course(self):
pass
注意:类体代码会在类定义阶段立即执行,产生一个类的名称空间,用来将类体代码执行的过程中产生的名字都丢到该名称空间中
5、 使用类
两种用途:
1. 当做一个容器
print(OldboyStudent.__dict__)
print(OldboyStudent.school)
print(OldboyStudent.__dict__['school'])
OldboyStudent.country='China'
OldboyStudent.school='OLDBOY'
print(OldboyStudent.__dict__)
del OldboyStudent.school
print(OldboyStudent.__dict__)
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('frank',18,'male')
# 调用类会产生两件事:
# 1、产生一个空对象
# 2、触发类的函数/对象的绑定方法__init__(stu1,'frank',18,'male')
#
# 强调:__init__:
# 1.该方法是在产生对象之后才调用
# 2.该方法内可以有任意python代码,但唯独就是不能有返回值
# 6、属性查找
# 强调:类的属性与对象的属性,访问时必须加前缀
# 先从对象自己的名称空间中找,如果没有则去类的名称空间中找
#
# 7、绑定方法
# 类中的定义的函数时类的函数属性,就是一个普通函数,没有自动传参的效果
# 但类中定义的函数其实就是绑定给对象用的,称之为绑定方法,绑定方法的特殊
# 之处绑定给谁就应该有谁来调用,谁来调用就将谁当作第一个位置参数传入
一、继承介绍:
1、 什么是继承
继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类、父类、超类
继承描述的是一“遗传”的关系:子类可以重用父类的属性
在python中的继承注意两点:
1. 在python中支持一个子类同时继承多个父类
2. Python中类分为两种:
新式类:但凡继承object的类,以及该类的子类,都是新式类
在python3中一个类如果没有继承类,默认继承object类,即python3中所有的类都是新式类
经典类:没有继承object的类,以及该类的子类,都是经典类
在python2中才区分新式类和经典类
2、 为何要用继承
减少代码冗余
3、 如果用继承
class Parent1(object):
pass
print(Parent1.__bases__)
class Parent2:
pass
class Subclass1(Parent1,Parent2):
pass
print(Subclass1.__bases__)
1. 如果利用继承减少代码冗余
2. 在继承的背景下,属性查找的优先级
3. 新式类与经典类的区别
二、利用继承减少代码冗余
继承解决的类与类之间的代码冗余的问题,一定是一个类是另外一个类的子类
继承关系的查找
总结对象之间的相识之处得到类,总结类与类之间的相识之处得到类的父类
class OldboyPeople:
school = 'Oldboy'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
self.score = 10
class OldboyStudent(OldboyPeople):
def choose_course(self):
print('% is choosing course' % self.name)
class OldboyTeacher(OldboyPeople):
# def __init__(self, name, age, sex, level):
# self.name = name
# self.age = age
# self.sex = sex
# self.level = level
def score(self, stu, num):
stu.score = num
stu1=OldboyStudent('Frank',18,'male') #OldboyPeople.__init__(stu1,'Frank',18,'male')
print(stu1.__dict__)
tea1=OldboyTeacher('sunny',19,'male') #OldboyPeople.__init__(tea1,'sunny',18,'male')
print(tea1.__dict__)
三、在子类派生出的新方法中重用父类功能的方式一
# 在子类派生出的新方法中重用父类功能的方式一:
# 指名道姓访问某一类的函数
# 注意:
# 1.该方式与继承没有关系
# 2.访问某一类的函数,没有自动传值的效果
class OldboyPeople:
school='Oldboy'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class OldboyStudent(OldboyPeople):
def __init__(self,name,age,sex,num=0):
OldboyPeople.__init__(self,name,age,sex)
self.score=num
def choose_course(self):
print('% is choosing course' %self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
OldboyPeople.__init__(self,name,age,sex)
self.level=level
def score(self,stu,num):
stu.score=num
stu1=OldboyStudent('frank',18,'male')#OldboyStudent.__init__(stu1,'frank',18,'male')
print(stu1.__dict__)
tea1=OldboyTeacher('frank',18,'male',20)#OldboyTeacher.__init__(tea1,'frank',18,'male',20)
print(tea1.__dict__)
四、在单继承背景下的属性查找
单继承背景下属性查找的顺序:对象---》对象的类---》父类
# class Foo:
# x=333
# pass
#
# class Bar(Foo):
# # x=222
# pass
#
# obj=Bar()
# # obj.x=123
# print(obj.x)
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()
五、多继承背景下属性查找的顺序:
对象---》对象的类—》按照从左往右的顺序一个一个的分支找下去
一旦出现菱形继承的问题,新式类与经典类在属性查找上的区别是:
新式类:广度优先查找,在最后一个分支查找顶级类
经典类:深度优先查找,在第一个分支查找顶级类
class G(object):
def test(self):
print('from G')
pass
#第三层
class E(G):
# def test(self):
# print('from E')
pass
class F(G):
# def test(self):
# print('from F')
pass
#第二层
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
# def test(self):
# print('from C')
pass
class D(G):
# def test(self):
# print('from D')
pass
#第一层
class A(B,C,D):
# def test(self):
# print('from A')
pass
obj=A()
obj.test()
新式类:对象—》A—》B—》E—》C—》F—》D-G
经典类:对象—》A—》B—》E—》G—》C—》F—》D
在新式类中,提供了一个mro方法
print(A.mro())
六、在子类派生出的新方法中重用父类功能的方式二:
只能在子类中用
在python2:super(自己的类名,对象自己)
在python3:super
调用super()会得到一个特殊的对象,该特殊对象是专门来引用父类中的属性,完全参照mro列表
注意:
1. 该方式与继承严格依赖于继承mro列表
2. 访问时绑定方法,有自动传值效果
class OldboyPeople:
school='Oldboy'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class OldboyStudent(OldboyPeople):
def __init__(self,name,age,sex,num=0):
# OldboyPeople.__init__(self,name,age,sex) #OldboyPeople.__init__(sut1,'frank',18,'male')
super(OldboyStudent,self).__init__(name,age,sex)
def choose_course(self):
print('%s is choosing course' %self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
super().__init__(name,age,sex)
self.level=level
def score(self,stu,num):
stu.score=num
stu1=OldboyStudent('frank',18,'male') #OldboyStudent.__init__(stu1,'frank',18,'male')
print(stu1.__dict__)
tea1=OldboyTeacher('sunny',19,'male',10) #OldboyTeacher.__init__(tea1,'sunny',19,'male',10)
print(tea1.__dict__)
举例:
class A:
def test(self):
print('A.test()')
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
obj=C()
print(C.mro())
obj.test()
七、利用组合减少代码冗余
1. 什么是组合
组合指的是一个对象用友某一个属性,改属性的值是另外一个类的对象
2. 为何组合
为了减少类与类之间的代码冗余
3. 如何使用
class OldboyPeople:
school='Oldboy'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class OldboyStudent(OldboyPeople):
def __init__(self,name,age,sex):
super().__init__(name,age,sex)
self.score=0
self.courses=[]
def choose_course(self):
print('%s is choosing course' %self.name)
def tell_all_course(self):
for course_obj in self.courses:
course_obj.tell_info()
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
super().__init__(name,age,sex)
self.level=level
def score(self,stu,num):
stu.score=num
class Course:
def __init__(self,c_name,c_price,c_period):
self.c_name=c_name
self.c_price=c_price
self.c_perod=c_period
def tell_info(self):
print('<课程名:%s 课程价格:%s 课程时间:%s>' %(self.c_name,self.c_price,self.c_perod))
python_obj=Course('python','18000','3mons')
linux_obj=Course('linux','10000','2mons')
stu1=OldboyStudent('frank',18,'male')
stu2=OldboyStudent('sunny',19,'male')
'''
一个学生选修一个课程
stu1.course=python_obj
stu2.course=linux_obj
stu1.course.tell_info()
stu2.course.tell_info()
'''
#一个学生选择多门课程
stu1.courses.append(python_obj)
stu1.courses.append(linux_obj)
stu1.tell_all_course()
八、多态与多态性
1.多态
多态指的是同一种事物的多种形态
2.多态性
可以在不用考虑对象具体情况下而直接使用对象
优点:
归一化,简单使用对象
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractclassmethod
def speal(self):
pass
@abc.abstractclassmethod
def run(self):
pass
# 抽象基类:是用来指定规范,但凡继承改类的的子类都必须实现speak和run,而名字叫speak和run
# 注意:不能实例化抽象基类
#这种方法不实用
class Txt:
def read(self):
print('txt read')
def write(self):
print('txt write')
class Process:
def read(self):
print('Process read')
def write(self):
print('Process write')
class Disk:
def read(self):
print('Disk read')
def write(self):
print('Disk write')
obj1=Txt()
obj2=Process()
obj3=Disk()
obj1.read()
obj1.write()
obj2.read()
obj2.write()
obj3.read()
obj3.write()
九、封装
1. 什么是封装
装就是将数据属性或者函数属性放到一个名称空间里
封指的是隐藏,该隐藏是为了明确区分内外,即该隐藏是对外不对内(在类外部无法直接访问隐藏的属性,而内部可以直接访问)
2. 为何要封装
1. 封数据属性
2. 封函数属性
3. 如何封装
在类中定义的属性前加__开头
class People:
__country='China'
def __init__(self,name):
self.__name=name
def run(self):
print('%is running' %self.__name)
print(People.__dict__)
print(People._People__country)
obj=People('frank')
print(obj.__dict__)
print(obj._People__name)
总结这种隐藏问题需要注意的问题:
1. 这种隐藏只是一种语法上的变形,并没有真正的限制访问
2. 这种变形只是在类的定义阶段检测语法是变形一次,定义阶段后新增的__开头的属性不会产生变形
People.__x=1
print(People.__dict__)
3. 在继承中,父类如果不想让子类覆盖自己的方法,可以在改方法前加__开头
class Parent1:
def __func(self):
print('parent1.func')
class Sub1(Parent1):
def __func(self):
print('sub1.func')
print(Parent1.__dict__)
print(Sub1.__dict__)
执行结果:
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()
封装的正式意图:把数据属性或函数属性装起来为了以后使用,封起来即藏起来为了不让外部直接使用
1. 封装数据属性:把数据属性藏起来,是为了不让外部直接操作隐藏的属性,而通过类内开辟的接口来间接操作属性,我们可以在接口之上附加任意的控制逻辑来严格控制使用者对属性的操作
class People:
def __init__(self,name,age):
self.__name=name
self.__age=age
def tell_info(self):
print('<name:%s age:%s>' %(self.__name,self.__age))
def set_info(self,name,age):
if type(name) is not str:
print('名字必须是字符串')
return
if type(age) is not int:
print('年龄必须是整数')
return
self.__na me=name
self.__age=age
obj=People('frank',18)
obj.tell_info()
obj.set_info('FRANK',19)
obj.tell_info()
2. 封函数属性,隔离复杂度
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
a=ATM()
a.withdraw()
十、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装饰器就是将一个函数属性伪装一个数据属性
class People:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
@property
def bmi(self):
return self.weight/(self.height **2)
obj=People('frank',70,1.72)
print(obj.bmi)
十一、classmethod与staticmethod方法使用
类中定义的函数有两大类(3小种)用途:一类是绑定方法,另外一类是非绑定方法
1. 绑定方法:
特点:绑定给谁就应该谁来调用,谁来调用就会将谁当作第一个参数传入
1.1. 绑定给对象:类中定义的函数默认就是绑定对象
1.2. 绑定给类的:在类中定义的函数上加一个装饰器classmethod
2. 非绑定方法
特点:既不与类绑定,也不与对象绑定,意味着或者类都可以调用,但无论谁来调用都是一个普通的函数,没有自动传值
class Foo:
def func1(self):
print('绑定给对象的方法',self)
@classmethod
def func2(cls):
print('绑定给类的方法',cls)
@staticmethod
def func3():
print('普通函数')
obj=Foo()
obj.func1()
obj.func2()
obj.func3()
class Mysql:
def __init__(self,ip,port):
self.id=self.create_id()
self.ip=ip
self.port=port
def tell_info(self):
print('%s:%s:%s' %(self.id,self.ip,self.port))
@staticmethod
def create_id():
import uuid
return uuid.uuid4()
obj=Mysql('1.1.1.1',3306)
obj.tell_info()
十二、反射
class Foo:
pass
class Bar(Foo):
pass
obj=Bar()
print(isinstance(obj,Bar)) #判断obj是不是Bar的对象
print(issubclass(Bar,Foo)) #判断Bar是不是Foo子类
反射:指的是通过字符串来操作属性
class Foo:
def __init__(self,name):
self.name=name
obj=Foo('frank')
print(hasattr(obj,'name'))
print(getattr(obj,'name'))
print(setattr(obj,'age',18))
print(obj.__dict__)
delattr(obj,'name')
print(obj.__dict__)
class Ftp:
def get(self):
print('get')
def put(self):
print('put')
def login(self):
print('login')
def run(self):
while True:
choice=input('>>>:').strip()
if hasattr(self,choice):
method=getattr(self,choice)
method
else:
print('命令不存在')
obj=Ftp()
obj.run()
十三、内置方法:
__str__:在对象被打印时自动触发,然后将该绑定方法的返回值(必须是字符串)当作本次打印的结果
class People:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '<name:%s age:%s>' %(self.name,self.age)
obj1=People('frank',18)
print(obj1)
__del__:在对象被删除前自动触发,在该方法内应该执行与该对象有关的系统资源的回收操作