面向对象3大特性
封装
简述:方法封装到来类中(函数封装到类中,并且第一个参数为self,变成方法);数据封装到对象中(__init__中,添加参数数据)
私有属性/方法:封装后,__开头的属性或函数不能再外部调用,否则会报错。在类的内部依然可以访问
其实这仅仅这是一种变形操作,类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式
而__x(self)这种方法在类的定义阶段就已经进行了变形成_类名__x 示例:
实例:
flask中
- session/request封装到来RequestContext对象中
- app/g封装到了AppContext中
继承
简述:如果多各类中有相同的方法,为了避免重复编写,可以将其放在父类(基类)中
python中类的继承分为:单继承和多继承
查看类继承的父类__base__或__bases__,示例:
__base__
只查看从左到右继承的第一个子类,bases__则是查看所有继承的父类
` (<class ‘__main.ParentClass1’>,) `
经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
继承实现的原理
F.mro() 等同于F.__mro__
生成简单的所有基类的线性顺序列表,且从左到右进行匹配
应用:Flask: wtforms中meta使用(meta作用定制csrf token) 注意!只有新式类有__mro__
深度优先(按照基类一路往下走到底)和广度优先(走到倒数第二个,倒回来重新走第二个参数基类)
当类是经典类(python2不继承object)时,多继承情况下,再要查找属性不存在时,会按照深度优先的方式查找下去
当类是新式类(python2继承object/python3)时,多继承情况下,再要查找属性不存在时,会按照广度优先的方式查找下去
在子类中调用父类的方法
方式一:指名道姓,即父类名.父类方法()
示例
方式二:super()
示例
这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系, super仍然会按照mro继续往后查找
实例:
Django中
rest framework中的视图类的继承多态
简述:鸭子模型:天生支持多态,对于参数来说可以传入任何类型的对象,只要保证有想要的send方法即可
简述:通过一个接口(函数),进行反复调用,达到同样的结果
因为python是非强类型的语言,例如,传入的参数或者给的某个值,只要有某个方法存在我就认为他是某一个类型,所以本身就是多态的
实例:
list()/dict()等实例对象,可调用append(),insert(),pop()等相同的方法,就是多态性面向对象程序设计与面向过程程序设计
面向过程:一般用于那些功能一旦实现之后就很少需要改变的场景, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,著名的例子有Linux內核,git,以及Apache HTTP Server等
面向对象:如果你要处理的任务是复杂的,且需要不断迭代和维护 的一切皆对象
python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
多个对象都有绑定方法func,是相同的功能,但内存地址不同
操作绑定方法,因为内存地址不同,所以不会相互影响
重用性有两种方式:
继承:详情看上述内容
组合:组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合,即:
类的__init__
中:self.coursename = Course() # 其中,这个Course()为另外一个类的对象
使用场景:当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
总结:扩充类的功能,合并其他类的方法接口
定义:接口是一组功能的入口,要调用某一组功能,需要通过接口来进行调用,而不需要关注这组功能是如何实现的,要的只是结果。
应用:基类定义成抽象类,为派生类提供接口,如果派生类不定义接口的方法,那么无法使用,目的为了约束实现该接口的类中必须定义指定的某些方法抽象类:
把相同的特征抽象到一个类中,使用方法和继承没什么区别,只需要导入import abc
定义的抽象类,无需实现功能
抽象类不能实例化,只能被继承
抽象类是用来规定类的格式的,抽象类有的方法,继承他的子类就必须有他的方法
为多态性进行铺垫
示例:
实际开发中不会使用abc这种方法,而会使用抛出异常的方法进行类的规定!!
使用:raise NotImplementedError(“继承的类需要实现该方法”)
当实例化的对象调用该抛异常方法时,如果没有重写该方法,则会抛出NotImplementedError
示例:
绑定方法与非绑定方法
绑定方法
绑定给对象(在类中的方法第一个参数为self都是绑定给对象的)
绑定给类(classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数)
非绑定方法
在类内部用staticmethod装饰的函数即非绑定方法,就是普通函数,谁都可以调用,且没有自动传值
对比示例:
查看名称空间
对象
__dict__
类在定义的时候创建的名称空间,类的变量,类体里的函数function
其中,私有属性或方法__x在定义时变形为:_类名__x
类的内部获取当前运行的 class 的名字:
self.__class__.__name__
内置方法
isinstance(obj,cls)检查是否obj是否是类 cls 的对象,相当于加强版的type(),可以检查出继承
反射:访问、检测和修改它本身状态或行为的一种能力(自省)
自省函数:
反射的好处
实现可插拔机制
动态导入模块
setattr/delattr/__getattr__应用时会出现的一些问题
描述符!!!
__get__(self, instance, owner)
instance:当前对象/owner:实例化对象的类本身__set__(self, instance, value)
instance:当前对象/value设置的值__delete__(self, instance)
instance:当前对象- 定义:
新式类实现了这三个方法的类就是描述符,作用是用来代理另外一个类的属性的
另一个类调用/赋值修改/del删除一个属性时,触发描述符的执行,描述符本身无法触发
在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是对象的属性字典,所以描述符对类没有作用 - 描述符分类:
数据描述符:至少实现了__get__,__set__
方法
非数据描述符:没有实现__set__方法(函数就是一个由非描述符类实例化得到的对象,字符串也一样) - 调用优先级
- 类属性
- 数据描述符
- 实例属性
- 非数据描述符
- 找不到属性触发
__getattr__()
- 应用
实现:@property、@classmethod、@staticmethod - 描述符总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件内置方法
__setitem__/__getitem__/__delitem__/__delattr__
详情:
__str__,__repr__,__format__
改变对象的字符串显示__str__,__repr__
(触发场景:使用print()触发)
str函数或者print函数—>obj.__str__()
repr或者交互式解释器—>obj.__repr__()
如果__str__
没有被定义,那么就会使用__repr__
来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常,两个都存在时候,__str__
优先级高
自定制格式化字符串__format__
(触发场景:使用format触发)
issubclass和isinstance - issubclass:判断基类派生类
print(issubclass(B,A)) #B是A的子类,返回True
- isinstance:判断对象和类
print(isinstance(a1,A)) #a1是A的实例
__slots__
限制绑定属性,__slots__ = ('name', 'age')
用tuple定义允许绑定的属性名称,则不能在外界绑定其他的属性,否则会错。__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
触发场景:直接在类中设置属性__slots__=['name','age']
__doc__
它类的注释描述信息,通常运用到元类、抽象类中,用来规定类的样式
该属性无法被继承
触发场景:在类中编写注释”"”content”””
__next__和__iter__
实现迭代器iterator协议
触发场景:
如果类中实现了__iter__
则可以被循环
当进行.next()的时候调用__next__
__module__和__class__
__module__
表示当前操作的对象在哪个模块
__class__
表示当前操作的对象的类是什么
触发场景:直接用对象进行调用obj.__module__/obj.__class__
__del__
析构方法,当对象在内存中被释放时,自动触发执行
触发场景:
一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__
。
文件f.close()/数据库
conn.close()
__enter__和__exit__
with open('a.txt') as f: '代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明
__enter__
和__exit__
方法
详情:
__call__
触发场景:对象后面加括号,触发执行。
详情:
__new__
实例化对象时真正的构造方法
详情:
##元类:
元类是类的类,是类的模板。元类是用来控制如何创建类的
创建类的两种方式
方式一:使用class关键字
方式二:就是手动模拟class创建类的过程):将创建类的步骤拆分开,手动去创建
详情:
类的执行顺序!!
元类的__init__
在类的创建已经触发
元类的__call__
返回类实例化的obj
类的__new__
类的__init__
详情:类中各语句的执行顺序
解析顺序
- 静态
- 继承管理
- 构造
执行顺序 - 静态
- 构造
- 继承
异常处理
常见异常:
- AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
- IOError 输入/输出异常;基本上是无法打开文件
- ImportError 无法引入模块或包;基本上是路径问题或名称错误
- IndentationError 语法错误(的子类) ;代码没有正确对齐
- IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
- KeyError 试图访问字典里不存在的键
- KeyboardInterrupt Ctrl+C被按下
- NameError 使用一个还未被赋予对象的变量
- SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
- TypeError 传入对象类型与要求的不符合
- UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
- 更多常用异常:
StopIteration 迭代器/生成器没有值了
NotImplementedError 用来做接口约束
Exception 捕捉所有异常
自定义异常:
断言:assert 条件