一、面向对象基础
本文主要介绍python中的面向对象,分为面向对象基础和面向对象进阶。
编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 ,一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。 两种最重要的编程范式分别是面向过程编程和面向对象编程。
python编程范式:
1.面向过程:根据业务逻辑从上到下写代码
2.函数式编程:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
3.面向对象:对函数进行分类和封装,让开发“更快更好更强...”
面向对象编程
OOP编程(Object Oriented Programming )是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下:
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后的实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
类的定义
#/usr/bin/env python#-*- coding:utf-8 -*-#Author:W-D
class Foo:#Foo为类名
def __init__(self,name,age):#类的构造方法
self.name=name
self.age=agedefbar(self):print("my name is %s age:%s"%(self.name,self.age))
obj1=Foo('wd',22)#类的实例化
obj1.bar()#调用对象中的方法
python中类的三大特性
封装
继承
多态
封装:对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。
例如:
#/usr/bin/env python#-*- coding:utf-8 -*-#Author:W-D
classperson:def __init__(self,name,age):
self.name=name
self.age=agedefbar(self):print("my name is %s age:%s"%(self.name,self.age))#通过self调用
obj1=person('wd',22)#将wd和22封装到obj1的name,age中,调用的时候可以通过obj1.name和obj1.age来调用
继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。继承又可以分为单继承和多继承。
例如:
#!/usr/bin/env python#-*- coding:utf-8 -*-#Author:W-D
classfather:defprint_name(self):print("this is father")class son(father):#继承father
passperson1=father()
person1.print_name()#继承了father类的print_name方法
结果:
thisis father
多态:基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现。
例如:
#!/usr/bin/env python#-*- coding:utf-8 -*-#Author:W-D
classfather:pass
class son1(father):#继承father类
defshow_name(self):print("son1.name")class son2(father):#继承father类
defshow_name(self):print("son2.name")def fun(obj):#同一接口不同表现
obj.show_name()
jack=son1()
mack=son2()
fun(jack)
fun(mack)
结果:
son1.name
son2.name
类的构造方法和析构方法:
构造方法(__init__):类创建对象时,自动触发执行。
析构方法(__del__):当对象在内存中被释放时,自动触发执行,内存释放分为两种,程序结束或者对象被删除的时候。
例如:
#!/usr/bin/env python#-*- coding:utf-8 -*-#Author:W-D
importtimeclassperson:def __init__(self,name,age):
self.name=name
self.age=ageprint("exec init ....")def __del__(self):print('exec del.....')
a=person('wd',22)#实例化过程,执行__init__构造方法
print('before del obj.....')del a #删除对象内存指针,释放对象内存地址,执行__del__析构方法
print('after del obj.....')
结果:execinit ....
beforedelobj.....exec del.....
afterdel obj.....
python继承中的构造方法
在Python中子类继承父类的过程中,如果子类不覆盖父类的__init__()方法,则子类默认将执行与父类一样的初始化方法。但是假如子类自己重写了(也成为覆盖)父类的__init__()方法,那么就需要显式的调用父类的初始化方法了。有两种方法可以做到:
1.父类名.__init__() :经典类写法
2.super(子类名,self)__init__():新式类写法,super方法支持调用父类方法,调用方式为super(子类名,self).方法名()
例如:
多继承中的相同方法寻找顺序
如果一个类继承了继承了多个类,并且父类和子类都具有相同的方法,使用子类方法,若子类中没有该方法,那么寻找方法的顺序是分别是:深度优先和广度优先。
根据版本不同,顺序不同。
在python2.X版本中:
当类是经典类时,多继承情况下,会按照深度优先方式查找
当类是新式类时,多继承情况下,会按照广度优先方式查找(就近原则,先找上一级,找完在找上上级)
在python3.5中:
无论是经典类还是新式类都一样,都是按广度优先的方式查找
python3中类的分类:
经典类:定义中未继承object类称为经典类
新式类:继承了object类或者父类继承了object类称为新式类
例如:
总结:
面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
类 是一个模板,模板中包装了多个“函数”或者属性供使用
对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
面向对象三大特性:封装、继承和多态
二、面向对象进阶
1.类的实例化过程解析
步骤1:先申请示例的内存空间
步骤2:将实例的内存地址和参数传递给相应的类
步骤3:执行类的构造方法将参数封装在实例中,示例创建完毕
2.类成员详细介绍
类成员:python3可将类成员分为属性、方法、特殊成员,按所属关系可分为公有成员、私有成员。
属性:属性的调用方式都是通过对象或者类名加点的方式,如:类.属性,对象.属性,属性分为:普通属性、私有属性、公有属性,公有属性保存在类中,其他保存在对象中。
公有成员:任何地方都能访问。
私有成员:只允许在类的内部访问,定义时候需要加上双下划线。
方法:普通方法、类方法、静态方法、属性方法,类中的方法都保存在类中。
特殊成员:如__doc__、__dict__等,稍后详细介绍。
a、属性
普通属性:普通属性通过类的构造方法得到,并且普通属性保存在实例中。
公有属性:定义在类中,并且公有属性保存在类中,访问公有属性最好通过类访问。
例如:
私有属性:定义私有属性的方法:在属性前加__,如self.__name=name,私有属性只允许类的内部访问,对外部不可见,可以通过return方法让外部可见,想要强行访问可用过【对象._类名__私有属性名】访问。
例如:
b、方法
普通方法:普通方法就是定义在类中的普通函数。
类方法:由类调用,在普通方法前加上@classmethod,类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量用。
静态方法:由类调用,在方法前加@staticmethod无默认参数(意思类不会传递self或者其他参数,但是可以自己加参数),其实和普通函数相同,只是调用通过类,如果被装饰的方法含有self参数,调用的时候会当作普通参数处理,而不是当作实例本身。
属性方法:通过@property把一个方法变成一个普通属性,定义时只传递self参数,调用时和普通属性一样通过对象.属性的方式调用(相当于一个属性),并且类属性方法无法通过del删除(除了设置了删除方法,后续提到)。
例如:
类属性方法(property)详细说明:
上面说过了,python类可分为经典类和新式类,相对于经典类而言,新式类(继承object)属性丰富。
经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法。
新式类中的属性有三种访问方式,并分别对应了三个被@property(获取)、@方法名.setter(设置)、@方法名.deleter(删除)修饰的方法,由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除。
例如:
property类源码剖析:
classproperty(object):"""property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x"""
def deleter(self, *args, **kwargs): #real signature unknown
"""Descriptor to change the deleter on a property."""
pass
def getter(self, *args, **kwargs): #real signature unknown
"""Descriptor to change the getter on a property."""
pass
def setter(self, *args, **kwargs): #real signature unknown
"""Descriptor to change the setter on a property."""
pass
def __delete__(self, *args, **kwargs): #real signature unknown
"""Delete an attribute of instance."""
pass
def __getattribute__(self, *args, **kwargs): #real signature unknown
"""Return getattr(self, name)."""
pass
def __get__(self, *args, **kwargs): #real signature unknown
"""Return an attribute of instance, which is of type owner."""
pass
def __init__(self, fget=None, fset=None, fdel=None, doc=None): #known special case of property.__init__
"""property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
# (copied from class doc)"""
源码
property类构造方法有参数为4个:
第一个参数是方法名,调用 对象.属性 时自动触发执行方法
第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
下面使用公有属性创建
classFOO(object):def __init__(self,name):
self.name=namedefget_name(self):returnself.namedef set_name(self,value):#必须两个参数
self.name=valuereturnself.namedefdel_name(self):delself.nameprint('del name....')
BAR=property(get_name,set_name,del_name,'something')
obj=FOO('wd')#自动调用property中的get_name
print(obj.BAR)
obj.BAR='jack'#自动调用第二个参数中的set_name
print(obj.BAR)del obj.BAR #自动调用第三个参数的del_name
print(FOO.BAR.__doc__)#自动获取第4个参数中获取的值
c、类特殊成员(类成员修饰符)
python中类特殊成员在类生成时自动生成,大致成员如下:
1.__doc__:类的描述信息
classFOO:'''this foo class'''
pass
print(FOO.__doc__)
结果:
this fooclass
2.__module__ 和 __class__ : __module__: 表示当前操作的对象在那个模块,__class__ 表示当前操作的对象的所属类是什么。
classFOO:'''this foo class'''
pass
print(FOO.__module__)print(FOO.__class__)
结果:__main__
3.__init__:类的构造方法,创建实例时候自动执行。
4.__del__:析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
5. __call__:对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
classFOO:'''this foo class'''
def __call__(self, *args, **kwargs):print('exec call....')passobj=FOO()
obj()
结果:exec call....
6. __dict__:类或对象中的所有成员,返回是个字典的形式。
classFOO:'''this foo class'''CITY='beijing'
def __init__(self,name,age):
self.name=name
self.age=agedef __call__(self, *args, **kwargs):print('exec call....')
obj=FOO('wd',22)print(obj.__dict__)print(FOO.__dict__)
结果:
{'age': 22, 'name': 'wd'}
{'__weakref__': , '__dict__': , '__doc__': 'this foo class', 'CITY': 'beijing', '__call__': , '__module__': '__main__', '__init__': }
7. __str__:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
classFOO:'''this foo class'''CITY='beijing'
def __init__(self,name,age):
self.name=name
self.age=agedef __call__(self, *args, **kwargs):print('exec call....')def __str__(self):return 'this is FOO'obj=FOO('wd',22)print(obj)#调用__str__输出return内容
结果:
thisis FOO
8.__getitem__、__setitem__、__delitem__:用于索引操作,如字典。以上分别表示获取、设置、删除数据
classFoo(object):def __getitem__(self, key):print ('__getitem__', key)def __setitem__(self, key, value):print('__setitem__', key, value)def __delitem__(self, key):print('__delitem__', key)
obj=Foo()
result= obj['k1'] #自动触发执行 __getitem__
obj['k2'] = 'wupeiqi' #自动触发执行 __setitem__
del obj['k1'] #自动触发执行 __delitem__
9. __iter__ :用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
classFoo(object):def __init__(self, sq):
self.sq=sqdef __iter__(self):returniter(self.sq)
obj= Foo([11,22,33,44])for i inobj:print(i)
结果:11
22
33
44
10. __new__ \ __metaclass__
classFoo(object):def __init__(self, name):
self.name=name
obj= Foo("wd")
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的构造方法创建。
print(type(obj)) # 输出: 表示,obj 对象由Foo类创建
print (type(Foo)) # 输出: 表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
1.普通方式:
classFoo(object):def __init__(self, name):
self.name= name
2.特殊方式(使用type类的构造函数):
classperson(object):defget_name(self):print('doing something....')deffunc(self):print('name:{}'.format(self.name))def __init__(self,name,age):
self.name=name
self.age=age
Foo= type('Foo', (person,), {'func': func,'__init__':__init__})
bar=Foo('jack',22)
bar.func()
bar.get_name()#第一个参数为类名#第二个参数为要继承的类名(可以不写,代表不继承某个类)#第三个参数为方法,格式为{'方法名':方法内存地址}
--> 类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由谁来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看类创建的过程
(该过程在python2中,在python3中有所变化)
python3中创建过程:
classMyType(type):def __init__(self,*args,**kwargs):print("Mytype __init__",*args,**kwargs)def __call__(self, *args, **kwargs):print("Mytype __call__", *args, **kwargs)
obj= self.__new__(self)print("obj",obj,*args, **kwargs)print(self)
self.__init__(obj,*args, **kwargs)returnobjdef __new__(cls, *args, **kwargs):print("Mytype __new__",*args,**kwargs)return type.__new__(cls, *args, **kwargs)print('here...')class Foo(object,metaclass=MyType):def __init__(self,name):
self.name=nameprint("Foo __init__")def __new__(cls, *args, **kwargs):print("Foo __new__",cls, *args, **kwargs)return object.__new__(cls)
f= Foo("wd")print("f",f)print("fname",f.name)#自定义元类# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__
metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
反射:
通过字符串映射或修改程序运行时的状态、属性、方法, 有以下4个方法。
1.getattr(object,name,default=None):获取属性,返回属性内存地址。
2.hasattr(object,name):判断有无属性,返回True或者Flase。
3.setattr(x,y,v):设置属性,x.y=v
4.delattr(x,y):删除属性
示例:
classFoo(object):def __init__(self):
self.name= 'wupeiqi'
deffunc(self):return 'func'obj=Foo()##### 检查是否含有成员 ####
hasattr(obj, 'name')
hasattr(obj,'func')##### 获取成员 ####
getattr(obj, 'name')
getattr(obj,'func')##### 设置成员 ####
setattr(obj, 'age', 18)
setattr(obj,'show', lambda num: num + 1)
setattr(Foo,'city','beijing')print(Foo.city)##### 删除成员 ####
delattr(obj, 'name')
delattr(Foo,'func')#删除类中方法
getattr(Foo,'func')#报错