python面向对象编程

(11) 第11章:面向对象编程

函数是面向过程(创建对象的过程,如自已处理某些事情,然后按照步骤一步一步做)
函数是编程,面向对象(通过对象来使用,如自已传达一个指令给一个对象来执行某些事情)

1、函数、类函数、静态函数

函数类型:import types,判断函数的类型
types.FunctionType:自已创建的函数(匿名函数和普通函数)类型
types.LambdaType:匿名函数类型
types.BuiltinFunctionType:内置函数类型
types.GeneratorType:生成器对象–>如(x for i in range(8))列表推导式

构造函数:创建实例、初始化函数:添加初始属性、self–>当前实例对象
通过类模型获取实体对象–>
类定义class、首字母大写、初始化函数__init__、self
2.2版本(经典类class Car():pass、新式类class Car(object):pass)
3.x版本只有新式类:class Car():pass
新式类:一定继承某个父类,在py2.2引入
经典类:不继承任何类 在py3.x以前是有的 后面就没了
car.__class__.__base__:查看父类


实例方法:和实体相关联(某个对象具有某个功能) ,具体是一个实例的动作,必须要通过创建实例才能调用的方法,定义函数的时候第一个参数是self,就是具体某个实例的动作


@staticmethod(静态方法)
–>静态函数:表示跟当前类没有关系的,只是方便就在类里面写,不用在导入
–>没有self参数,可通过类名或者实例都可以直接调用
–>执行初始操作
–>脱离实体,初始化工作,跟当前的类没有关系,只是一个普通的函数写到了类里面


@classmethod(类方法)
–>类函数:如有写类不需要创建实例的时候就定义类函数
–>有cls参数:定义类方法第一个参数必须是cls–>class
–>cls类对象:cls代表类本身
–>和类关联,表示为这个类别都有的一个动作


其他
类方法静态方法可以直接通过类名直接调用,不用创建实例(实体方法不行)
三个类型的方法都可以通过创建实例去调用
__call__():对象当作方法来用,会自动执行–>调用:实例对象名()
__str__():print(实例对象)会自动执行,方法体要把对象返回(return)
当某种场景触发,会自动执行魔法方法
vars(a):列出类a对象初始化的属性,以字典的形式返回

2、构造函数、析构函数
构造函数–>制造对象

def __new__(cls,*args,**kwargs):
	return super().__new__(cls)

构造函数:一定要renturn 父类的构造函数(参数是类对象)(先构造,在初始初始化数据),如果没有返回实例对象,就会返回None,就没有对应的属性和方法
析构函数:销毁对象(当前脚本退出自动销毁,或者手动销毁 del 实例对象名)

def __del__(self):
	pass

_attribute:保护变量(外部可访问,子类对象也可以访问)
__attribute:私有变量(外部不可访问,子类也不能访问,只能在类内部访问)

3、类以及实例对象

:范称 一种归纳一种抽象。
实例: 具体的某个类的一个对象
我喜欢我家浴缸里的红色的那条小丑鱼:鱼是类 小丑鱼是实例,对象

4、面向对象变成三大特性(多态、封装、继承)

继承复写:子类重写父类方法,子类调用父类方法,先在子类找,在去父类找,可通过super()调用父类方法
多态(建立在继承关系上):同一个功能,不同的表现,根据传递的对象,显示不同的形态,传递父类显示父类的形态,子类显示子类的形态

5、鸭子形态(不管是不是鸭子,看起来像鸭子,可以说你是鸭子)

没有继承关系,但是具有同名接口方法,就可称为鸭子形态

6、self和super用法

类是抽象概念,实例对象是实际存在的对象
self:指当前实例对象,只能放在第一个参数位置,当使用实例对象调用实体方法的时候,python会自动把实例对象传递给self
super():用在子类调用父类引用
当子类和父类有同一个属性的时候,可super().__init__(a)传递给父类进行初始化
在py2.x的时候使用super(xxx,self).__init__(a),不能省略类对象和实例对象的传递
在py3.x两种方法都可以使用
当传递一个父类没有的属性,就不能进行接收,调用就会报错
父类和子类的属性不要相同,子类属性比父类多
如下,super的用法

class A(object):
	def __init__(*args,**kwargs)
		super(A,self).__init__(*args,**kwargs)

7、多继承 @继承知识的博客,有c3算法详解

一个类可继承多个类:class A(B,C):pass
子类调用属性和方法:按照MRO(方法解析顺序),用于确定子类实例中某方法继承自哪个父类
在py3.x里面A.__mro__ :获取MRO顺序
在py2.x里面 import inspect inspect.geitmro(A):获取MRO顺序
在python3.x是c3算法 搜索父类继承
py2.2中:经典类是DFS(深度优先) 有了新式类,是BFS 广度优先
py2.3中:新式类 是 c3算法
经典类只是在python3之前,之后就没了
如果c3算法不能处理所有的继承类,就会报错

广度优先不符合单调性:在正常线性继承的时候,a继承的b类的父类e有a需要的方法,a继承的c类也有a需要的方法,理论的情况下应该是b就有这个方法,毕竟是继承了e类,但是结果是这个方法是从c类中来的,这就不符合单调性

深度优先在菱形继承中会有子类的方法被永久屏蔽:a集成了b,c类,b,c是d的子类,a所需的方法,d类有,c类也有,按照深度优先,先是去b,在去d,然后再d找到了,也就是说,如果a所需的方法d和c都有,那么永久都不会调用到c这个类

C3算法线性化步骤:

1. 选取merge中的第一个列表记为当前列表 K。
2. 从列表K中拿出第一个元素,如果该元素不在其他列表的tail中则将该元素移入类C的线性化列表中。将其从merge的每一个列表中移出。
3. 否则设置K为merge中的下一个列表,重复步骤2.
4.如果 merge 中所有的类都被移除,则输出类创建成功;如果不能找到下一个 h,则输出拒绝创建类 C并抛出异常。

下面是图和解析步骤
python面向对象编程_第1张图片

c3算法,如元素是第一个或者不存在
L[E] = [E,object]
L[D] = [D,object]
L[F] ] [F,object]

L[B] = B +merge(L[E],L[D],E,D)
L[B] = B+merge([E,object],[D,object,E,D)
L[B] = [B,E,D,object]

L[C] = [C,D,F,object]

L[A] = A+merge(L[B],L[C],B,C)
L[A] = A+merge([B,E,D,object][C,D,F,object], B, C)

L[A]= 【A,B】+ merge([E,D,object][C,D,F,object], C)
L[A]= 【A,B,E】+ merge([D,object][C,D,F,object], C)           
L[A]= 【A,B,E,C】+ merge([D,object][D,F,object])     
# 注意从上一行到这一行,先选择D但不符合条件,然后选择C
L[A]= 【A,B,E,C,D】+ merge([object][F,object])
L[A]= 【A,B,E,C,D,F】+ merge([object][object])    
#注意从上一行到这一行,没选择object

L[A]= 【A,B,E,C,D,F,object

8、多态

有了继承之后,不管调用父类的方法还是子类的方法,能实现效果就行
调用方只管调用,不管细节, 调用子类对象与调用父类对象一样。 符合“开闭原则”
对新增扩展开放,对修改闭合。

9、实例属性的限制

py3.x版本才有的:__slots__属性来限制实例对象动态绑定的属性q
__slots__ = ('a','b'...):元组内的属性就是可以绑定的,就相当于有了a和b两个属性,如下代码
既要体现python动态特性,也要内存优化
__slots__:可以限制一个类的属性,只对当前类起作用,不对子类起作用

class A:
	__slots__  = ('name','age')
a = A()
a.name = 'a'
a.sex = '1'#这样会报错

上面代码中A这个类只能访问name和age两个属性
如不加__slots__的情况下,创建的实力对象可以动态加任何属性,如下

class A:
	pass
a = A()
a.name = 'a'
a.age = 18
a.sex = '1'
#这样不会报错

10、动态绑定
上面的代码实现了动态绑定属性,下面这个是动态绑定方法,在调用的时候把实例对象传进去就可以,如下

class Dog:
	def __init__(self,name='little dog'):
		self._name = name

def get_dog_name(self):
	return self._name

dog = Dog()
dog.get_dog_name = get_dog_name
print(dog.get_dog_name(dog)) 

动态解析性语言,动态绑定:先构造实例,在动态绑定属性,随属性绑定越多,占用内存越大,可以绑定属性,也可以绑定方法
在绑定方法的时候,先用类名.方法 = 方法 的方式绑定方法,然后再创建实例去调用这个方法,这样就不用在调用这个方法的时候传递实例本身了,不然先创建实例在绑定方法,在调用这个方法,这样就需要传递一个参数是实例本身

11、属性包装
将函数包装成属性来用,用@property–>将函数包装成属性的装饰器,获取属性

class Person():
	#获取属性
	@property 
	def age(self):
		return self._age  
	#修改属性
	@age.setter 
	def age(self,age):
		self._age = age
	#删除属性
	@age.deleter 
	def age(self):
		del self._age
#获取属性、设置属性、删除属性   方法名一定要一样
p1 = Person() 
p1.age = 22#设置
print(p1.age)#获取
del p1.age#删除

最终目的把函数转化为属性来用,没有执行任何函数,都是对象.属性名来触发三个函数
_age使用保护变量,如果不实用保护变量,就会触发递归调用(函数名和方法名相同),属性名和传递的变量名要区分
(不能无限递归,会超出解析器的最大限制,而报错停住脚本执行)

12、元类和枚举类

枚举类:
定义常量使用全大写如:ONE = 1,全大写的一般都是常量,不希望去修改
限制修改:把常量放到枚举类
enum枚举类模块名,Enum枚举类,unique唯一约束
将类变成枚举类,直接继承Enum,unique作为装饰器直接修饰这个类(如ONE = 1,这样只能出现一次)

from enum import Enum,unique
@unique #表示唯一
class A(Enum):
	ONE = 1  
A.ONE.value
#获取常量值  若修改这个常量会报错(AttributeError)

元类: type 在创建对象的时候不会去调用构造函数__init__
创建类的类–>metaclass 类就是元类的对象
a.__class__:得出实例对象的类
a.__class__.__class__:得出元类
实例对象—>定义class类—>元类
Person = type(‘Person’,(),{‘name’:‘a’,‘work’=work}) def work(self):pass
第一个参数是类名,第二个是父类,第三个数类的属性和方法
p1 = Person()创建类实例对象 p1.name获取类属性名 p1.work()调用类方法
定义类通过class定义,也可以通过type()函数(元类)去定义类

13、常用的魔法方法

__call__、__str__、__new__、__init__、__del__、__add__、__cmp__
__函数名__:具有特殊含义,特殊使用场景

__str__:打印输出的时候,渲染输出格式
__cmp__:比较大小

你可能感兴趣的:(python,python基础)