VII python面向对象
以下概念及举例均在python3.*中实现;
1、
面向过程&面向对象:
op | oo | |
产生背景 | 科学计算为目标的必然产物 | 软件应用领域的扩张和系统膨胀之后应运而生 |
编程方法 | 自顶向下 | 自底向上 |
代码主体结构 | 程序=数据(变量)+算法(函数|过程) | 程序=对象+交互 |
数据操作主体 | 由函数|过程进行加工与展现 | 在对象的方法中加工与展现 |
模拟方法 | 通过函数|过程操纵表现世界的数据与状态 | 把世界描绘成具有主动性的对象之间交互 |
编程思维 | 搞清处理数据的步骤 | 面向对象分析 |
运行效率 | 较高 | 较低 |
鸡兔同笼:
先假设全部为鸡,计算出腿数;
算出腿数量差;
得出鸡数量;
计算另一动物的数量;
注:
自猜想的数据类型;
变量名为大写就认为是常量;
大小写敏感;
2、
对象编程体验:
自动运行小游戏模拟:
在一维的地图上,有一只虫子和一只蚂蚁,每一次它们都走过一个-3,-2,2,3随机单位的距离(选定走法),若达到地图边界则放弃移动,当蚂蚁虫子处于同一位置时,蚂蚁吃掉虫子,程序结束;
op:
虫子的初始位置;
蚂蚁的初始位置;
进入循环(条件为蚂蚁和虫子不在同一位置);
依照规则,蚂蚁和虫子移动位置;
直到蚂蚁和虫子走到同一位置,程序结束;
oo:
游戏中的对象有:地图、虫子、蚂蚁;
地图是一维的,只需要记录虫子和蚂蚁的位置;
蚂蚁和虫子知道自己的位置;
蚂蚁和虫子能按规则移动;
定义地图、蚂蚁、虫子三个类;
主程序中实例化它们,并通过对象间的交互来完成游戏的模拟;
3、
面向对象入门:
理解对象:
对象可以指自然界中的任何事物;
计算机为解决某个领域问题所使用的事物(自然界中的事物的模型化);
事物(对象)只有自身的特征或能力;
计算机中的对象具有解决问题所需的特征或能力;
对象优越性:
封装(将模型的特征和能力打包在一起;模型的改变由模型自身来完成,就像自然界的事物一样;隐藏模型的细节,外界只能使用它,而不必(不能)改变它);
继承(符合自然界的分类规律;快速的代码重用);
多态(子类可以继承父类的特征与能力,还可通过自定义来修改其特征与能力;duck typing鸭子类型);
组合(一个模型可以由其它的模型组成);
注:
duck typing来源于james whitcomb riley提出的鸭子测试:
当看到一只鸟走起来像鸭子,叫起来像鸭子,游泳起来像鸭子,那么这只鸟就可被称为鸭子;
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的;
在有的语言中必须用接口来实现;
4、
定义和使用类:
最简类定义;
类实例化;
类与实例之间的关系(
定义类就是建立模型;
实例化就是建立真实事物;
例如:模具、印章);
有特征和能力的类(
特征|属性,是类自身包含或知道的数据;
能力,以方法体现,是类具有能动性的体现);
实例化步骤(
调用__new__()方法创建实例,__new__()方法自动从object继承;
调用__init__()方法对其初始化,__init__()方法在类中定义);
添加类说明docstring(
紧跟在类名之后,以三引号包围的字符串;
查看类说明,类名.__doc__或help(类名));
新式类与经典类(
python2.*版本,默认是经典类,继承object为新式类;
python3.*版本,统一为新式类,不用继承object;
区别:经典类继承为深度优先;新式类继承为广度优先)
描述对象的特征(实例属性;类属性;私有属性;特殊属性):
实例属性:
类被实例化以后才具有的属性;
一般在__init__()方法中创建并初始化;
直接使用即定义,self.属性名;
引用方法,self.属性名;
self用来代表类的实例;
类外用实例名.属性名方式定义和引用;
相同类的不同实例,其实例属性是不相关的;
一般不建议在__init__()方法之外创建和初始化实例属性;
一般不推荐类外定义和修改,修改可以单独定义方法;
In [13]: class TestClass:
....: def __init__(self):
....: self.a = 0
....: self.b = 10
....: def info(self):
....: print 'a:',self.a,'b:',self.b
....: def define_c(self):
....: self.c = 20
In [14]: tc=TestClass()
In [15]: tc.info()
a: 0 b: 10
In [16]: tc.color='red' #类外用
In [17]: print tc.color
red
In [18]: tca = TestClass()
In [19]: tcb = TestClass()
In [20]: tca.a = 100
In [21]: tcb.a = 200
In [22]: tca.info()
a: 100 b: 10
In [23]: tcb.info()
a: 200 b: 10
In [24]: tc = TestClass()
In [25]: tc.define_c()
In [26]: print tc.c
20
类属性:
类定义后就存在,而且不需要实例化;
类属性使得相同类的不同实例共同持有相同变量;
In [27]: class TestCss:
....: cssa = 'class-attribute'
....: def __init__(self):
....: self.a = 0
....: self.b = 10
....: def info(self):
....: print 'a:',self.a,'b:',self.b,TestCss.cssa
In [29]: tc = TestCss()
In [30]: tc.info()
a: 0 b: 10 class-attribute
In [29]: tc = TestCss()
In [30]: tc.info()
a: 0 b: 10 class-attribute
In [31]: tca = TestCss()
In [33]: tca.info()
a: 0 b: 10 class-attribute
In [34]: TestCss.cssa = 0 #类外修改其属性
In [35]: tc.info()
a: 0 b: 10 0
In [36]: tca.info()
a: 0 b: 10 0
私有属性:
不提供限制属性访问的关键字(无法限制类的各种属性在类外直接访问);
使用__开头的变量名加以标示,只有类对象自己能访问(限制性的);
使用_开头的变量名加以标示,只有类对象及其子类能访问(非强制性),对程序员来说,是标示性的而非限制性的,在类外还可以修改和查看;
In [37]: class TestPri:
....: def __init__(self):
....: self.__a = 0 #在此处初始化时实例属性就被隐藏了
....: def info(self):
....: print self.__a
....:
In [38]: a = TestPri()
In [39]: a.info()
0
In [40]: a.__a = 3 #私有属性不能在外部进行修改,此处相当于重新定义了一个实例属性,与类中定义的self.__a = 0是不一样的
In [41]: a.info()
0
In [43]: print a.__a
3
In [44]: class TestPri:
....: def __init__(self):
....: self._a = 10
....: def info(self):
....: print self._a
....:
In [45]: a = TestPri()
In [46]: a.info()
10
In [47]: a._a = 30 #在类外可修改和查看
In [48]: a.info()
30
In [49]: print a._a
30
特殊属性:
__doc__
__name__ #类名称
__dict__ #实例属性的所有属性名及值组成的dictionary
__module__ #该类所在的模块名
__base__ #该类的父类
5、让对象具有能动性:
类的方法的定义:
def fun_name(self,…):
pass
其中,self表示类的实例,在调用方法时由系统自动提供;
方法定义时必须指明self参数;
类的方法的调用:
与普通函数调用类似,即使参数列表为空也不能省略();
在类的内部调用,self.方法名(参数列表);
在类的外部调用,实例名.方法名(参数列表);
以上两种调用方法,提供的参数列表中都不用包括self;
类内方法相互调用:
在一个类的内部,方法之间是可以相互调用的;
调用方法,self.方法名(参数列表);
构造方法及其作用:
构造方法即使用__init__()方法;
构造方法的作用是在类实例化时初始化实例;
__init__()方法就是类实例化的第二步自动调用的函数,其方法名是固定的,但其参数同普通方法一样,至少要带self参数;
初始化实例包括,定义和初始化实例属性,或调用类的一些方法;
构造方法可带有除self外的其它各种参数,关键字参数、默认参数、用tuple收集参数、用字典收集关键字参数等,可以达到在实例化类时,为相应的属性传入指定的值;
注:
定义类的实例方法,和实例属性一样,必须进行类实例化之后,才能存在和调用它们;
python3.*版本,print是函数;python2.*,print是语句;
6、深入类的属性:
同名的类属性与实例属性:
实例名.属性名,优先引用实例属性;
类名.属性名,只能引用类属性;
属性访问的特殊方法(反射,或叫自省):
用字符串来操作类的属性|方法的方式;
主要工具函数(在编写框架或特殊项目时才用到):
hasattr(obj_name,'属性名')
setattr(obj_name,'属性名',值)
getattr(obj_name,'属性名')
In [50]: class TestClass:
....: a = 0
....: def __init__(self):
....: self.a = 10
....: self.b = 20
....:
In [51]: a = TestClass()
In [52]: a.a
Out[52]: 10
In [53]: a.b
Out[53]: 20
In [54]: TestClass.a
Out[54]: 0
In [55]: TestClass.b #抛异常,此类未定义b属性
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-55-39cab4ae7dc4> in <module>()
----> 1 TestClass.b
AttributeError: class TestClass has no attribute 'b'
In [56]: getattr(a,'a') #获取实例a的a属性
Out[56]: 10
In [57]: setattr(a,'a',20)
In [58]: a.a
Out[58]: 20
In [59]: hasattr(a,'b')
Out[59]: True
属性包装:
将方法包装成属性,以隐藏相关实现,用户使用时用属性的方式来用;
控制属性的类型和范围,如某属性只能是正数且在0-100之间;
虚拟属性(由其它属性处理后得来);
三种属性操作(3个装饰器):
@property #可读
@<property-name>.setter #可写
@<property-name>.deleter #可删
举例:
attribute_package.py
#!/usr/bin/env python3.6
#
class Washer:
def __init__(self,water=100,scour=2):
self._water = water
self.scour = scour
self.year = 2010
@property
def water(self):
return self._water
@water.setter
def water(self,water):
if 0 < water <= 500:
self._water = water
else:
print('set failure')
@property
def total_year(self):
return 2018 - self.year
if __name__ == '__main__':
w = Washer()
print(w.water)
w.water = -123
print(w.water)
print(w.total_year)
描述符:
将实现特殊协议方法的类作为另一个类的类属性;
用来拦截和控制属性访问并可重复使用;
协议方法:
__get __()
__set__()
__delete__()
分类:
数据描述符(实现全部协议方法);
非数据描述符(实现部分协议方法);
说明:
所有类成员函数都是非数据描述符;
同名的实例属性和非数据描述符(以方法为例)访问优先级;
注:
描述符只能在新式类中使用;
__call__(),让类的实例如函数一样可调用;
举例(数据描述符):
data_description.py
#!/usr/bin/env python3.6
#
class NonNeg:
def __init__(self,default=0):
self.default = default
def __get__(self,instance,owner):
return self.default
def __set__(self,instance,val):
if val > 0:
self.default = val
else:
print('the value must be nonnegative')
def __delete__(self,instance):
pass
class Movie:
rating = NonNeg()
score = NonNeg()
if __name__ == '__main__':
m = Movie()
print('rating:',m.rating)
print('score:',m.score)
m.rating = 80
print('rating:',m.rating)
m.score = 30
print('score:',m.score)
举例(非数据描述符):
In [1]: class Test:
...: def pr(self):
...: print('test')
...:
In [2]: t = Test()
In [3]: dir(t.pr) #pr方法实现了__get__()方法
Out[3]:
['__call__',
'__class__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__func__',
'__ge__',
'__get__',
'__getattribute__',
In [4]: t.pr = 10 #实例属性和非数据描述符同名,优先级为先实例属性再非数据描述符,此例中t.pr的属性掩盖了pr()方法
In [5]: t.pr
Out[5]: 10
In [6]: del t.pr
In [7]: t.pr
Out[7]: <bound method Test.pr of <__main__.Test object at 0x7f10ecd1c1d0>>
In [8]: t.pr()
test
7、
类方法、静态方法:
静态方法:
定义:
用staticmethod装饰;
参数不用self;
访问特性:
不能引用或访问实例属性;
可通过类、类变量访问类属性;
调用方式:
可用类、类实例调用;
本质:
在类中的一个普通函数;
使面向对象程序中的函数归属于类,易于代码管理;
用法:
与类相关,但不依赖或改变类与实例;
创建不同的实例;
把类相关工具方法放入类中;
举例:
static_method.py
class Washer:
company = 'panasonic'
def __init__(self,water=10,scour=2):
self.water = water
self.scour = scour
self.year = 2010
@staticmethod
def spins_ml(spins):
print('company:',Washer.company)
#print('year:',self.year) #报错,不能引用实例属性
return spins * 0.4
if __name__ == '__main__':
print(Washer.spins_ml(8))
w = Washer()
print(w.spins_ml(9))
w = Washer(200,Washer.spins_ml(10))
print(w.company)
print(w.year)
print(w.water)
print(w.scour)
print(w.spins_ml(10))
类方法:
定义:
@classmethod;
必须提供参数cls;
访问特性:不能引用或访问实例属性;
调用方法:可以用类、类实例调用;
继承特性:传入的类变量cls是子类,而非父类;
用途:
与类相关,但不依赖或改变类的实例;
工厂方法,创建类实例,完成有关预处理;
在类内调用静态方法时不用硬编码类名;
举例:
class_method.py
class Washer:
company = 'panasonic'
def __init__(self,water=10,scour=2):
self.water = water
self.scour = scour
self.year = 2010
def spins_ml(spins):
return spins * 0.4
@classmethod
def get_washer(cls,water,scour):
print('company:',Washer.company)
#print('year:',self.year)
return cls(water,cls.spins_ml(scour))
def start_wash(self):
print(self.water)
print(self.scour)
print('start wash')
if __name__ == '__main__':
w = Washer.get_washer(100,8)
w.start_wash()
8、
类的继承与方法重载:
继承特点:
oo编程的优点之一;
减少代码和灵活制定新类;
子类具有父类的属性和方法;
子类不能继承父类的私有属性|方法;
子类可添加新的方法;
子类可修改父类的方法;
继承语法:
定义类时,在类名后写(继承的类名);
多重继承时,括号中放多个父类名;
例:
class MyClass(BaseClass):
重载的语法:
直接定义和父类同名的方法;
修改父类的方法:
在重载的方法中调用父类方法;
同时添加相应的业务逻辑;
多重继承时如何调用父类方法;
模拟游戏解析:
sprite,ant,worm
例(编写一个色子类):
1、具有6个面,每个面为一种颜色;
2、每种颜色代表一种数值1-6;
3、实现一个通过颜色计算两种其代表数值和的静态方法;
4、实现一个类方法gen_dice,用于产生这个类的实例;
例:
In [2]: class A:
...: pass
...:
In [3]: A.__base__ #类A的父类是object,object是python中的新式类
Out[3]: object
In [4]: class B(A):
...: pass
...:
In [5]: B.__base__ #类B的父类是类A
Out[5]: __main__.A
In [6]: class C:
...: pass
...:
In [7]: class D(A,C):
...: pass
...:
In [8]: D.__bases__ #D的所有父类
Out[8]: (__main__.A, __main__.C)
In [9]: class A:
...: def foo(self):
...: print('A foo')
...:
In [10]: class B:
....: def foo(self):
....: print('B foo')
....:
In [11]: class C(A,B):
....: pass
....:
In [12]: c = C()
In [13]: c.foo() #输出先继承的类的方法
A foo
In [14]: class D(B,A):
....: pass
....:
In [15]: D().foo()
B foo
举例:
wash.py
9、
类的特殊方法:
深入理解类:
类也是一个对象,但具有创建其自身实例的能力;
类可以和一个变量进行绑定;
可以为类增加属性;
可把类作为函数的参数传递;
元类:
类的创建和管理者type;
所有的类都是元类type的实例(python3.*);
类实例化过程;
例:
In [16]: class Empty:
....: pass
....:
In [17]: ept = Empty
In [18]: ept
Out[18]: __main__.Empty
In [19]: ept.__base__
Out[19]: object
In [20]: ept()
Out[20]: <__main__.Empty at 0x7fd0adf7c470>
In [21]: ept.foo='foo'
In [22]: ept.foo
Out[22]: 'foo'
In [23]: def use_class(mc):
....: return mc()
....:
In [25]: use_class(Empty)
Out[25]: <__main__.Empty at 0x7fd0adf7ef28>
In [26]: type(Empty)
Out[26]: type
In [27]: Hello = type('Hello',(object,),dict(helo=lambda lf:print('hello')))
In [28]: h = Hello()
In [29]: h.helo()
hello
10、
鸭子类型与多态:
多态:
一种类型具有多种类型的能力;
允许不同的对象对同一消息作出灵活的反应;
以一种通用的方式对待可使用的对象;
非动态语言必须通过继承和接口来实现;
python中的多态:
通过继承实现多态(子类可作为父类使用);
子类可重载父类的方法实现多态;
例:
In [31]: class Animal:
....: def move(self):
....: print('Animal is moving...')
....:
In [32]: class Dog(Animal):
....: pass
....:
In [33]: def move(obj):
....: obj.move()
....:
In [34]: a = Animal()
In [35]: move(a)
Animal is moving...
In [36]: d = Dog()
In [37]: move(d)
Animal is moving...
In [38]: class Cat(Animal):
....: def move(self):
....: print('Cat is moving...')
....:
In [41]: class Sheep(Animal):
....: def move(self):
....: print('Sheep is moving...')
....:
In [42]: move(Cat())
Cat is moving...
In [43]: move(Sheep())
Sheep is moving...
In [44]: move(7) #报错
动态语言与鸭子类型:
变量绑定的类型具有不确定性;
函数和方法可接受任何类型的参数;
调用方法时不检查提供的参数类型;
调用时是否成功由参数的方法和属性确定;
调用不成功则抛出错误;
python中不用定义接口;
例:
In [48]: def test(foo):
....: print(type(foo))
....:
In [49]: test(3)
<class 'int'>
In [50]: test(3.1)
<class 'float'>
In [51]: type(3)
Out[51]: int
In [52]: type(3.1)
Out[52]: float
多态的好处:
可实现开放的扩展与修改的封闭;
使python更具灵活性;
11、
python与设计模式:
设计模式:
用来提高代码复用和可维护性的方式;
能够很好的指导软件设计过程;
是成功的软件设计模式和体系结构;
用于解决特定类型问题的面向对象编程模型;
python与设计模式:
由于语言的特性不同,设计模式的实现方式和实现难度也会不同;
有的模式已经在语言中内置了,如迭代器模式;
单例模式可直接用模块级变量来实现;
普通工厂模式可直接通过传入“类名”作为参数实现;
策略模式:
让一个对象的某个方法可以随时改变,而不用更改对象的代码;
对于动态类型的python语言,不需要定义接口;
基本的实现方法,用类作为参数传递;
最简单的实例方法,函数作为参数传递;
例(单例模式):
single_class.py
class singleClass:
def __init__(self,x=0):
self.x = 0
sc = singleClass()
def tsc():
print(sc.x)
sc.x = 10
print(sc.x)
def tsc2():
print(sc.x)
sc.x = 9
print(sc.x)
if __name__ == '__main__':
tsc()
tsc2()
例(实现单实例的方式):
singleton.py
class Singleton:
def __new__(cls,*args,**kwargs):
if not hasattr(cls,'_sgl'):
cls._sgl = super().__new__(cls,*args,**kwargs)
return cls._sgl
if __name__ == '__main__':
sa = Singleton()
sb = Singleton()
print(id(sa))
print(id(sb))
例(普通工厂模式):
normal_factory.py
class Ab:
a = 3
class Ac:
a = 0
class MyFactory:
def get_instance(self,ins):
return ins()
if __name__ == '__main__':
mf = MyFactory()
print(type(mf.get_instance(Ab)))
例(策略模式——用类作为参数传递):
class Moveable:
def move(self):
print('Move...')
class MoveOnFeet(Moveable):
def move(self):
print('Move on feet...')
class MoveOnWheel(Moveable):
def move(self):
print('Move on wheel...')
class MoveObj:
def set_move(self,moveable):
self.moveable = moveable()
def move(self):
self.moveable.move()
class Test:
def move(self):
print('i am fly')
if __name__ == '__main__':
m = MoveObj()
m.set_move(Moveable)
m.move()
m.set_move(MoveOnFeet)
m.move()
m.set_move(MoveOnWheel)
m.move()
m.set_move(Test)
m.move()
例(策略模式——用函数作为参数传递):
def movea():
print('move a')
def moveb():
print('move b')
class MoveObj:
def set_move(self,moveable):
self.moveable = moveable
def move(self):
self.moveable()
if __name__ == '__main__':
m = MoveObj()
m.set_move(movea)
m.move()
m.set_move(moveb)
m.move()
12、
装饰模式:
一般,通过继承可获得父类的属性,还可通过重载修改其方法;
装饰模式可不以继承的方式而返回一个被修改的类;
类装饰器;
例:
decorator.py
class BeDeco:
def be_edit_fun(self):
print('source fun')
def be_keep_fun(self):
print('keep fun')
class Decorator:
def __init__(self,dec):
self._dec = dec()
def be_edit_fun(self):
print('start...')
self._dec.be_edit_fun()
def be_keep_fun(self):
self._dec.be_keep_fun()
if __name__ == '__main__':
bd = BeDeco()
bd.be_edit_fun()
bd.be_keep_fun()
dr = Decorator(BeDeco)
dr.be_edit_fun()
dr.be_keep_fun()
例:
decorator.py
def deco(a_class):
class NewClass:
def __init__(self,age,color):
self.wrapped = a_class(age)
self.color = color
def display(self):
print(self.color)
print(self.wrapped.age)
return NewClass
@deco
class Cat:
def __init__(self,age):
self.age = age
def display(self):
print(self.age)
if __name__ == '__main__':
c = Cat(12,'black')
c.display()
13、
通过组合来构建复杂的对象:
组合:
类似用各种零件组装机器(零件——基础类,机器——包含其它类的类);
体现自下而上的面向对象编程方法;
雪人案例:
面向对象分析;
抽象出其类;
实现基类(shape不带角度参数的图形元素;shapeAngles需要角度参数的图形元素);
实现子类(HatTop,HatBotton,Face,Sense,BodyOutline,Button);
组装子类1(Hat,Head,Body,Shape);
组装子类2(snow);