
- 面向对象介绍
- 封装
- 继承
- 多态
- 反射
- 内置方法
- 元类
'''
面向过程:
核心是"过程"二字
过程的核心思想是将程序流程化
过程是”流水线“,用来分步骤解决问题
面向对象:
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条)