面向对象编程
1. 定义
核心是对象。对象是"容器",是用来盛放数据与功能的
对象的终极奥义就是将程序’‘整合’’
程序=数据+功能
学生选课系统
学生的容器=学生的数据+学生的功能
课程的容器=课程的数据+课程的功能
2.类与对象
类:类也是"容器",用来存放同类对象之间共有的数据和功能
类是对象相似数据与功能的集合体
所以类中最常见的变量与函数的定义,但是类体中是可以包含其他代码的
注意:类体代码是在类定义阶段就会立即执行,会产生类的名称空间
#先定义类
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
#取值语法(函数属性)
#Student.set_info#等价于student.__dict__['set_info']
#取值语法(数据属性)
#Student.stu_school#等价于student.__dict__['stu_school']
#后调用对象
# stu1_obj=Student()#定义阶段执行,调用阶段不执行只是建好关联
# print(stu1_obj.__dict__)
class Student:
stu_school='oldboy'
def __init__(obj,x,y,z):
obj.stu_name=x
obj.stu_age=y
obj.stu_gender=z
def tell_stu_info(obj):
print('学生信息:名字:%s 年龄:%s 性别:%s'%(obj['stu_name'],
obj['stu_age'],
obj['stu_gender']
)
)
def set_info(obj,x,y,z):
obj['stu_name']=x
obj['stu_age']=y
obj['stu_gender']=z
stu1_obj=Student('egon',18,'male')#会触发student.__init__(空对象即stu1.obj)
print(stu1_obj.tell_stu_info())
#总结__init__方法
#1.会在调用类时自动触发执行,用来为对象初始化自己独有的数据
#2.__init__内应该存放是为对象初始化属性的功能,但是是可以存放任意其他代码想要在类调用时就立刻执行的代码都可以放到该方法内
3.属性查找与绑定方法
优先找对象内的,再查找类的
类中存放的是对象共有的数据与功能,类也可以访问
1)类的数据属性2)类的函数属性
但其实类的数据上游戏是共享给所有对用的,大家访问的地址一样
在对象内赋值只会影响自己的对象内属性,而不会影响类的属性
Student.set_info(stu1_obj,'lili',20,'female')
Student.tell_stu_info(stu1_obj)
类的函数属性是绑定给对象用的,类调用自己的函数必须严格按照函数的做法来,绑定方法的不同,其地址也不同
3.1 调用类中的函数方法
stu1_obj.tell_stu_info()#会将自身传入
4.封装、继承、多态
4.1封装
封装<->整合
将封装的属性进行隐藏:在属性名前加__会产生对外隐藏的效果
class People:
def __init__(self,name,weight,height):
self.__name=name
self.__weight=weight
self.__height=height
@property
def bmi(self):
bmi_num=self.__weight/(self.__height**2)
if bmi_num in range(24,30):
return f'用户{self.__name}的BMI指数是{bmi_num}'
obj1=People('egon',65,1.50)
print(obj1.bmi)
#property可以将函数名整合,调用其中的方法
#1.将函数名改为需要用户的操作的名字
#2.查看 @property 修改@函数名.setter 删除@函数名.deleter
4.2 继承
在python中新建的类可以继承一个或多个父类,新建的类称为子类或派生类,父类称为基类或超类,子类会遗传父类的属性
需要注意的是,python支持多继承
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1):#单继承
pass
class Sub2(Parent1,Parent2):#多继承
pass
#Sub1.__bases__查看父类
##补充:在py2中有经典类和新式类的区分
##新式类:继承了object类的子类,以及该子类的子类....
##经典类:没有继承object类的子类,以及该子类的子类....
#py3中没有继承任何类会默认继承object类,所以都是新式类
其是用来解决类与类之间代码冗余问题
多继承的优点:子类可以同时遗传多个父类的属性,最大限度地重用代码多继承的缺点:违背了人的思维习惯,代码可读性变差,扩展性变差
#方式一:类与类出现冗余
class Student:
school='OLDBOY'
def __init__(self,name,age,sex,stu_id):
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)
#方法二:基于继承解决冗余问题
class OldboyPeople:
school="OLDBOY"
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Student(OldboyPeople):
def __init__(self, name, age, sex,id):
OldboyPeople.__init__(name, age, sex)
self.id=id
def choose_course(self):
print('%s正在选课'%self.name)
class Teacher(OldboyPeople):
def __init__(self,name,age,sex,salary,level):
OldboyPeople.__init__(self,name,age,sex)
self.salary=salary
self.level=level
def score(self):
print('%s 正在打分'%self.name)
4.2.1单继承背景下属性查找
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()#查找的是Bar.f1(),要调用Foo.f1可以直接写Foo.f1(),或者将f1函数名隐藏
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2() # Foo.f2 Bar.f1
4.2.2菱形继承(钻石问题)
#类似于一下情况#object不算菱形
class G:
pass
class A(G):
pass
class B(G):
pass
class C(A, B):
pass
MRO(方法解析顺序)创建类时就会产生
类以及该类的对象访问属性都是参照该类的MRO列表
原则:
- 子类优先与父类被检查
- 多个父类会根据他们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
总结:类相关的属性查找(类名.属性、该类的对象.属性),都参照该类的mro
非菱形多继承查找顺序:
经典类与新式类一样,是一个分支一个分支找下去,最后查找object
菱形继承查找问题
1.经典类:深度优先;先一条路找父类,然后顺序查找,并且不再查找共同父类
2.新式类:广度优先;先一个一个分支找,最后查找分支共同的父类
4.2.3mixins问题
机制核心:在多继承背景下尽可能的提升多继承的可读性
4.2.4 在子类中重用父类的方法
- 指名道姓的引用
class SchoolPeople:
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(SchoolPeople):
def __init__(self,name,age,sex,level,salary):
SchoolPeople.__init__(self,name,age,sex)
self.level=level
self.salary=salary
-严格依赖继承
class SchoolPeople:
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(SchoolPeople):
def __init__(self,name,age,sex,level,salary):
super(Teacher,self).__init__(self,name,age,sex)#调用super()类会得到一个特殊的对象,该对象会参照属性发起者的类的mro去当前类的父类中找属性
self.level=level
self.salary=salary
4.3多态
同一种事物有多种形态,可以在不考虑对象具体类型的基础上使用对象
4.3.1绑定方法与非绑定方法
一、绑定方法
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):
return cls(settings.ip,settings.port)
obj1=Mysql.from_conf()
print(obj1.__dict__)
二、非绑定方法
class Mysql:
def __init__(self,ip,port):
self.ip=ip
self.port=port
@staticmethod#将下述函数装饰成一个静态方法
def cread_id():
import uuid
return uuid.uuid4()
5.内置函数
abs()#绝对值
all()#可迭代对象取出后做bool运算,所有为True,返回True,,列表为空,bool运算为True,返回True
any()#可迭代对象中存在任意一个为True,返回True,对象为空,返回False
bin()#二进制
oct()#八进制
hex()#十六进制
callable()#判断对象是否能被调用
chr()/ord()#ascii编码
complex()#复数
dir()#查看对象下有哪些属性
divmod(a,b)#得到元组(a/b,余数)
enumerate()#枚举可以得到位置与值
eval()#eval执行字符串中的表达式
frozenset()#不可变集合
isinstance()#判断对象是否为类的实例,即类型判断
issubclass#判断是否为子类
pow(10,2,3)#等价于10**2%3
repr()#在交互式环境下打印的默认值
round()#四舍五入
slice#切片
sum()#求和
zip()#拉链函数
__import__()#括号内填入要导入的模块的字符串名