函数是面向过程(创建对象的过程,如自已处理某些事情,然后按照步骤一步一步做)
函数是编程,面向对象(通过对象来使用,如自已传达一个指令给一个对象来执行某些事情)
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并抛出异常。
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__
:比较大小