面向对象介绍
面向过程:
核心是"过程"二字
过程的终极奥义就是将程序流程化
过程是"流水线",用来分步骤解决问题的
面向对象:
核心是"对象"二字
对象的终极奥义就是将程序"整合"
对象是"容器",用来盛放数据与功能的
类也是"容器",该容器用来存放同类对象共有的数据与功能
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']=x
stu_obj['stu_age']=y
stu_obj['stu_gender']=z
stu_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']=x
stu_obj['stu_age']=y
stu_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=18
obj.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']=x
stu_obj['stu_age']=y
stu_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=18
self.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=x
self.stu_age=y
self.stu_gender=z
def choose(self,x):
print('正在选课')
self.course=x
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.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]),类的实例化,实例化出l1
l2=[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 = nickname
self.addr = addr
self.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 = name
self.course = None
def related_course(self, course_obj):
# self.course = course_name
self.course = course_obj
def 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=name
self.period=period
self.price=price
def 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__x
def __f1(self): # _Foo__f1
print('from test')
# print(Foo.__dict__)
# # print(Foo._Foo__x)
# # print(Foo._Foo__f1)
# II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形
class Foo:
__x = 1 # _Foo__x = 1
def __f1(self): # _Foo__f1
print('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 = 1
def __f1(self): # _Foo__f1
print('from test')
def f2(self):
print(self.__x) # print(self._Foo__x)
print(self.__f1) # print(self._Foo__f1)
Foo.__y=3
print(Foo.__dict__)
print(Foo.__y)
class Foo:
__x = 1 # _Foo__x = 1
def __init__(self,name,age):
self.__name=name
self.__age=age
obj=Foo('egon',18)
# print(obj.__dict__)
# print(obj.name,obj.age)
为何隐藏
# 2、为何要隐藏?
# I、隐藏数据属性"将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口(方法)来允许类外部间接地操作数据,
接口之上可以附加额外的逻辑来对数据的操作进行严格地控制:
# 设计者:egon
class People:
def __init__(self, name):
self.__name = name
def get_name(self):
# 通过该接口就可以间接地访问到名字属性
# print('小垃圾,不让看')
print(self.__name)
def set_name(self,val):
if type(val) is not str:
print('小垃圾,必须传字符串类型')
return
self.__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 = name
self.weight = weight
self.height = height
# # 定义函数的原因1:
# # 1、从bmi的公式上看,bmi应该是触发功能计算得到的
# # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值
# # 说白了,每次都是需要临时计算得到的
#
# # 但是bmi听起来更像是一个数据属性,而非功能
@property
def 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 = 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('不让删除')
# del self.__name
name=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个方法名称都相同:name
class People:
def __init__(self, name):
self.__name = name
@property
def name(self): # obj1.name
return self.__name
@name.setter
def name(self, val): # obj1.name='EGON'
if type(val) is not str: #增加了判断,修改是否合法
print('必须传入str类型')
return
self.__name = val
@name.deleter
def name(self): # del obj1.name #阻止删除
print('不让删除')
# del self.__name
obj1=People('egon')
# 人正常的思维逻辑
print(obj1.name) #
# obj1.name=18
# del obj1.name
继承
# 1、什么是继承
# 2、为何要用继承:用来解决类与类之间代码冗余问题
# I:继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
# II:需要注意的是:python支持多继承
# 在Python中,新建的类可以继承一个或多个父类
新式类与经典类
新式类与经典类在多继承查找父类时,新式类广度优先,经典类深度优先
class Parent1(object):
x=1111
class Parent2(object):
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1,Parent2): # 多继承
pass
print(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=name
self.age=age
self.sex=sex
def choose_course(self):
print('学生%s 正在选课' %self.name)
class Teacher:
school='OLDBOY'
def __init__(self,name,age,sex,salary,level):
self.name=name
self.age=age
self.sex=sex
self.salary=salary
self.level=level
def score(self):
print('老师 %s 正在给学生打分' %self.name)
# 示范2:基于继承解决类与类之间的冗余问题
class OldboyPeople: #定义基类
school = 'OLDBOY'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.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,10
def __init__(self, name, age, sex, salary, level): #将父类的参数也传进去;子类重写init方法,必须调用一次父类init方法
# 指名道姓地跟父类OldboyPeople去要__init__
OldboyPeople.__init__(self,name,age, sex) #调用父类的init方法
self.salary = salary
self.level = level
def 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')
pass
class B(A):
def test(self):
print('from B')
pass
class C(A):
# def test(self):
# print('from C')
pass
class 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
# # 二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
# # 都是一个分支一个分支地找下去,然后最后找object
class E:
# def test(self):
# print('from E')
pass
class F:
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
# def test(self):
# print('from C')
pass
class 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->object
obj = A()
obj.test() # 结果为:from F
# 三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
# 经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)
# 新式类:广度优先,会在检索最后一条分支的时候检索大脑袋
class G: # 在python2中,未继承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')
class B(E):
# def test(self):
# print('from B')
pass
class 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->D
obj = A()
obj.test() #
# 总结:
# 多继承到底要不用???
# 要用,但是规避几点问题
# 1、继承结构尽量不要过于复杂
# 2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系
多继承mixins机制
# 多继承的正确打开方式:mixins机制
# mixins机制核心:就是在多继承背景下尽可能地提升多继承的可读性
# ps:让多继承满足人的思维习惯=》什么"是"什么
class Vehicle: #交通工具类
pass
class FlyableMixin: #可以飞,作为所有飞机的mixin父类
def fly(self):
pass
class CivilAircraft(FlyableMixin,Vehicle): # 民航飞机,mixin父类写在继承最左边
pass
class Helicopter(FlyableMixin,Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
pass
import socketserver
# 补充:通常Mixin结果的类放在左边
调用其他类的功能、super
# 在子类派生的新方法中如何重用父类的功能
# 方式一:指名道姓调用某一个类下的函数=》不依赖于继承关系
class OldboyPeople:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def 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 = level
self.salary=salary
tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)
# 方式二:super()调用父类提供给自己的方法=》严格依赖继承关系
# 调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性
class OldboyPeople:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def 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) #python2
super().__init__(name,age,sex) # 调用的是方法,自动传入对象
self.level = level
self.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):
pass
obj=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:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
# 2、为何要有多态=》多态会带来什么样的特性,多态性
# 多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
class Animal: # 统一所有子类的方法
def say(self):
print('动物基本的发声频率。。。',end=' ')
class People(Animal):
def say(self):
super().say() #使用父类的方法,不写super调用父类的say,则相当于重写了sya
print('嘤嘤嘤嘤嘤嘤嘤')
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 settings
class Mysql:
def __init__(self,ip,port):
self.ip=ip
self.port=port
def 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=3306
obj1=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=ip
self.port=port
@staticmethod # 将下述函数装饰成一个静态方法
def create_id():
import uuid
return uuid.uuid4()
@classmethod
def f1(cls):
pass
def f2(self):
pass
obj1=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():
pass
class Foo:
pass
print(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 % 3
print(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:
pass
obj=Foo()
obj.xxx=1111
print(dir(obj)) # obj.哪些属性
# =================》掌握
for i,v in enumerate(['a','b','c']):
print(i,v)
# =================》掌握
res=eval('{"a":1}') # 执行字符串中的表达式
print(res,type(res))
# =================》掌握
class Foo:
pass
obj=Foo()
print(isinstance(obj,Foo)) #isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
print(isinstance([],list)) # 类型判断推荐使用isinstance
print(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=name
self.age=age
def 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:
# pass
class 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 = name
self.age = age
def __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 = name
self.age = age
self.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 = 0
def __new__(cls,*args,**kwargs):
print("__new__():",cls,args,kwargs)
instance = super().__new__(cls)
instance.number = cls.instances_created #为父类创建的空对象添加属性。
cls.instances_created += 1
return instance
def __init__(self,attribute):
print("__init__():",self,attribute)
self.attribute = attribute
test1 = 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):
pass
clangs = CLanguage()
print(clangs.name)
clangs.say()
元类
元类type介绍
#一:引入:
# 一切都源自于一句话:一切皆为对象
#二:什么是元类?
# 元类就是用来实例化产生类的类
# 关系:元类---实例化---->类(People)---实例化---->对象(obj)
class People:
def __init__(self,name,age):
self.name=name
self.age=age
def 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=name
self.age=age
def 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元类是mymeta
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
#
# # 强调:
# # 只要是调用类,那么会一次调用
# # 1、类内的__new__
# # 2、类内的__init__
call
# 五:__call__
class Foo:
def __init__(self,x,y):
self.x=x
self.y=y
# obj,1,2,3,a=4,b=5,c=6
def __call__(self,*args,**kwargs):
print('===>',args,kwargs)
return 123
obj=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=name
self.age=age
def 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=444
def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
obj=self.__new__(self) # StanfordTeacher.__new__
# obj=object.__new__(self)
print(self.__new__ is object.__new__) #True
self.__init__(obj,*args,**kwargs)
return obj
class Bar(object):
# n=333
# def __new__(cls, *args, **kwargs):
# print('Bar.__new__')
pass
class Foo(Bar):
# n=222
# def __new__(cls, *args, **kwargs):
# print('Foo.__new__')
pass
class StanfordTeacher(Foo,metaclass=Mymeta):
# n=111
def __init__(self,name,age):
self.name=name
self.age=age
obj=StanfordTeacher('lili',18)
print(obj.__dict__)
# print(obj.n)
# print(StanfordTeacher.n)
补充
链式操作
# coding:utf8
# author:jason
class MyClass(object):
def func1(self):
print('func1')
return self #返回self对象
def func2(self):
print('func2')
return self
obj = MyClass()
obj.func1().func2()