面向对象
OOP Object Oriented Programing
面向对象是认识世界的一种方法论, 一切皆对象
类class: 抽象的概念,一类具有共同特征的事物的集合;用计算机语言来描述,就是属性和方法的集合
实例instance: 类的一个实现
属性,是对象状态的抽象
方法,是对象行为的抽象
面向对象3要素:
- 封装
隐藏数据:对外只暴露一些接口,只能通过接口访问对象
组装:将数据和操作组装在一起 - 继承
多继承少修改OCP(Open-closed Principle),使用继承来改变 - 多态
动态绑定
命名上的一些约定
类名用大驼峰,类变量用全大写
变量名加下划线后缀, 避免与保留关键字冲突
变量名加下划线前缀, 表示变量非公有,
私有属性: 双下划线开头的属性名,对象属性字典中被改名_CLASS__ATTRIBUTE
私有方法: 双下划线开头的方法名,被解释器改名_CLASS_METHOD
单下划线前缀不带保护
前后双下滑线, 系统定义的变量, 避免定义
当使用__定义属性时,保存到属性字典中的key,变成_class__attr,class为定义属性语句所处的类
当使用.__attr
访问属性时,访问的其实是._class__attr
,class为访问语句所处的类
类中的方法分类
0、也没有装饰, 没有参数的方法,实例不可调用(因为默认实例会作为参数会隐式传入),禁止使用
1、没有装饰器,至少有一个参数:self
,即此方法被实例通过属性方式调用时,隐式传入实例本身,类对象调用时,就是普通函数,self占用一个参数位置
2、@classmethod 至少一个参数:cls
,表示不管是类对象调用,还是实例调用,都隐式传入一个类对象本身,cls占用一个传参位,
常作为工厂函数,代替constructor,实例化一个需要不同参数的实例,例如rectangle 类实例化一个 square,只要一个参数 side_length
3、@staticmethod 可以没有参数,随意传参,不隐式传参
补丁:Monkey Pathch
运行时,对类的属性、方法进行动态替换,慎用,测试
把实例的属性保护起来,不让外部直接访问,外部使用getter方法访问,setter方法设置属性
属性装饰器 :property 常用来使属性read_only
@property;@method.setter;@method.deleter
Class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
self.__age = age
@age.deleter
def age(self):
del self.__age
tom = Person('Tom')
print(tom.age) # getter
tom.age = 20 # setter
del tom.age # deleter
Class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def getage(self):
return self.__age
def setage(self, age):
self.__age = age
def delage(slef):
del self.__age
age = property(getage, setage, delage, 'age property')
Class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
age = property(lambda self: self.__age) # getter
对象的销毁
def __del__(self): print('del')
类中可以定义__del__
方法,称为析构函数(方法), 执行del
命令时调用
当对象引用计数为0时, 自动调用
类的一些特殊属性和方法
__base__
类的基类
__bases__
类的基类元组
__mro__
显示属性方法查找顺序 method resolution order,类的元组, mro() 调用
__subclasses__()
:子类的列表
对象的属性和方法都可以使用.
访问, 表示属性和对象发生了绑定
属性的访问顺序:实例自己的字典,实例所属类的字典,实例所属类的父类的字典
类中定义的属性(变量和方法)在外部看都变成了cls.attr或instance.attr,这里的外部包括类方法的内部,而在类的内部看还是attr
类的执行顺序,为从上到下,每一句都执行,语句块结束后才生成类
函数只做声明,调用时才执行
初始化函数init中,再实例化类,就会产生递归
类是语句块的最外层,只要类生成了,在当前modul中都可以访问到
继承和多态
继承加覆盖形成多态
override 覆盖
overload 重载, 例: type
函数
super类,
def __init__(self, type1=None, type2=None)
super() -> same as super(__class__owner, self<first argument>)
super(type1) -> unbound super object
super(type1, obj) -> bound super object; requires isinstance(obj, type1)
super(type1, type2) -> bound super object; requires issubclass(type2, type1)
Typical use to call a cooperative superclass method:
class C(B):
def meth(self, arg):
super().meth(arg)
This works for class methods too:
class C(B):
@classmethod
def cmeth(cls, arg):
super().cmeth(arg)
用super调用type1
(第一个参数)的父类的方法(绑定或非绑定, 第二个参数),
用法: 子类的同名方法中调用父类的方法,达到增强方法的目的,类似装饰器
作为一个好的习惯,如果父类中定义了__init__
方法,你就应该在子类的__init__
中调用它
class A(B):
def __init__(self,arg):
super().__init__(arg)
Python2.2 之后引入共同祖先类object,为了兼容分为古典类(旧式类)和新式类
Python3 中全部都是新式类,新式类都继承自object,新式类可以使用super,使用超类方法
多继承
java中舍弃了多继承,java中一个类可以实现多个接口,一个接口可以继承多个接口,接口只有方法的声明
多继承可能带来二义性,解决二义性,要看深度优先还是广度优先
python3 解决多继承二义性使用C3算法,计算出一个MRO有序列表
尽量避免多继承
Mixin
定义Mixin类,只有特定方法的实现,其他类需要这些方法是,就继承Mixin类
多使用
Mixin体现的是一种组合的设计模式,多组合,把功能类独立出来复用
Mixin类的祖先类也应该是Mixin类
上下文管理
with object as foo:
进入调用__enter__(self),return值赋给foo
执行语句
退出调用__exit__(exc_type, exc_value, exc_traceback),return 等效True 就压制异常
魔术方法
__dir__
:
dir()
函数调用,如果没有提供,尽量收集对象的属性,对象 -> 类 -> 基类属性名全部收集, list实例没有__dict__
属性,但用dir()可以找到它所有的的属性和方法__init__
,__del__
:
初始化,和销毁__hash__
:
hash()
调用,返回int,定义了这个方法,类的实例才可hash.
字典,set用hash值做key,去重的话,要同时提供__eq__
方法,来判断是否相同
不定义虽然是hashable,但hash值不确定__bool__
:
bool类实例化时调用,bool(),返回True or False,没有定义就找__len()__
,长度非0即为True,len方法也没实现,默认是True,只有bool() -> False对象的特殊属性
__name__
标识符,字符串, 模块的name为A.B.C, 类名为C
__module__
所属module
__class__
实例的类,type()
__bases__
,__base__
基类tuple, 基类
__doc__
文档
__mro__
method resolution order, mro()调用
__dict__
属性字典
__qualname__
类的限定名, A.B.C
可视化
* __repr__
:
repr()
调用,返回对象的字符串表达,就按照object的定义,返回内存信息
* __str__
:
str()
,format()
,print()
调用,返回对象的字符串表达,没有定义,就去调用__repr__
* __int__
: coverting object to built-in types
* __bytes__
:
bytes(object)
调用,返回对象的bytes表达,没有定义抛TypeError
运算符重载
operator_method(self, other)
__lt__
,__le__
,__eq__
,__gt__
,__ge__
,__ne__
<, <=, ==, >, >=, !=
__add__
,__sub__
,__mul__
,__truediv__
,__mod__
,__floordiv__
,__pow__
,__divmod__
,&__and__
,|__or__
,^__xor__
都有对应的r方法,当第1个参数没有实现方法是,调用第2个参数的r方法.__iadd__
,__isub__
,__imul__
,__itruediv__
,__imod__
,__ifloordiv__
,__ipow__
:
+=, -=, *=, /=, %=,/ /=, **=
note: 比较大小,只要实现三种就可以,例<,<=,==
,另外三种调用实现
容器相关方法
* __len__
:
len()
调用,返回对象长度(>=0的整数),没有定义报TypeError
* iter
:
迭代对象时调用(for语句),返回一个新的迭代器对象,不能next()
* __contains__
:
使用in
成员运算符时调用,没有实现,就调用__iter__
方法遍历,直到迭代找到为止
* __getitem__
:
使用self[key]
访问对象时调用
* __setitem__
:
使用self[key] = value
设置对象属性是调用
* __missing__
:
字典或其子类使用self[key]
访问时,key不存在时调用,拦截KeyError异常
可调用
* __call__
:
实例被调用时调用
上下文管理
__exit__
,__enter__
:
with Point() as p:
同时实现两个方法,才属于上下文管理对象,enter
进入与此对象相关的上下文时执行,exit
退出与此对象相关的上下文时执行, 上下文管理是安全的,发生异常,__exit__
依然执行
__enter__
没有参数,返回值赋给as子句中的变量__exit__
有三个参数都与异常有关:exc_type
,exc_value
,traceback
,如果返回等效True的值,压制异常,否则继续抛出异常
上下文的应用场景:
1. 增强功能
2. 资源管理,打开资源就需要关闭,例如文件对象,网络连接,数据库连接
3. 权限验证,在__enter__
中处理@contextlib.contextmanager
装饰器,使一个函数具有上下文管理的功能,使用方法@contextlib.contextmanager def foo(): print('enter') try: yield something # yield只能有一个,作为__enter__方法的返回值 finally: print('exit')
反射reflection
指的是程序运行时获取定义信息,Python中,能够通过一个对象,找出其type, class, attribute, method的能力,称为反射或自省
- 内建函数:
getattr(object, name[, default])
:
访问属性值,不存在则返回default,如果没有deault,抛出AttributeError, 调用的是__getattribute__
setattr(object, name, value)
:
设置object属性值,存在则覆盖,不存在则新增,调用的__setattr__
hasattr(object, name)
:
判断object是否存在名字为name的属性delattr(self, name)
:
删除对象属性,调用的是__delattr__
__setattr__
:
当尝试给属性赋值时调用,如果没有实现,object默认添加属性到对象的__dict__
中.描述器例外.__delattr__
:
删除对象属性时调用,如果不实现,object默认删除属性__getattribute__
:
getattr(self,name) 和 self.attr 时调用
如果没有实现,就执行object类
的getattribute方法,即object.__getattribute__(self, name)
,即按照默认属性查找顺序查找,
如果实现了同时又返回object.__getattribute__(self, name)
,则属性的查找顺序不变
如果在其中抛出AttributeError,则调用__getattr__
, 没有实现的话, 再抛AttributeError__getattr__
:
访问对象的属性时,按照继承关系查找,查找失败要抛出AttributeE时调用,相当于设置默认值,如果没有实现,抛出AttributeError
描述器Descriptors
__get__
,__set__
,__delete__
:
签名如下:
class A ;; class B;x=A() ;; b=B()
__get__(self, instance, owner)
:B.x, b.x
访问x时调用,B访问时,instance为None
__set__(self, instance, value)
:b.x=value
时调用
__delete__(self, instance)
:
self是A的实例A(), instance是b, owner是B, value是设置的值
如果一个类中实现了__get__
,__set__
,__delete__
三个方法中的任何一个,就称为一个描述器类,这个类的实例作为另一个类的属性值时,
如果仅实现了__get__
, 就是非数据描述器 non-data descriptor
如果同时实现了__set__
, 就是数据描述器 data descriptor
属性搜索__dict__
先于非数据描述器,数据描述器先于__dict__
,因为定义__set__
后,设置属性值的时候都会调用__set__
,而不是在__dict__
中添加,即数据描述器绑定的属性行为不能被覆盖,而非数据描述器绑定的属性行为可以(通过__setattr__
方法覆盖)
描述器应用:classmethod
,staticmethod
,property
装饰器
描述器应用:flask作者写的网络工具werkzeug,实现缓冲器
槽位slots
__slots__
明确的定义指定的属性,保护实例不被添加其他属性,阻止__dict__
的自动创建
应用场景: 实例在百万以上,实例属性简单,固定且不用动态增加,如列表节点,Poin等,使用__dict__
可能会占用很大的空间
私有属性可以被继承,__slots__
继承不了
使用__slots__