面向对象编程
目标,原则和模式
面向对象中的主体为对象
每个对象都是类的实例
类详细定义了 对象包含的实例变量(又称数据成员),还规定了对象可以执行的方法(又称成员函数)
目标
目标:健壮性,适应性,可重用性
健壮性:将错误修正的能力
适应性:能随着时间不断优化(可进化性)
可重用性:可重用,应该谨慎使用
原则
首要原则:
- 模块化
- 抽象化:从一个复杂的系统中提炼最基础的部分
- 封装:软件系统的不同组件不应显示其各自的实现的内部细节
设计模式
描述“典型”软件设计问题的解决方案。
一种可以应用于不同情况的解决方案,提供了通用模版的模式
模式,包括
- 一个名称(它表示了该模式)
- 一个语境(它描述应该该模式的情况)
- 一个模版(它描述如何应用该模式)
- 一个结果(它描述和分析该模式会产生什么结果)
软件开发
主要阶段:
- 设计
- 实现
- 测试和调试
设计
经验规则:
- 责任:把这些工作分为不同的角色,有各自不同的责任。试着用行为动词描述责任。这些角色将形成程序的类
- 独立:在尽可能独立于其他类的前提下规定每个类的工作。细分各个类的责任
- 行为:仔细且精细地为每个类定义行为,与其他类交互时,可以更好的理解这个类执行动作的结果
CRC卡,UML图
伪代码
通过一种专门为人准备的方法来描述算法
用于描述隐藏在数据结构和算法实现之后的主要编程思想
编码风格和文档
主要原则:
- 通常缩进4个空格
- 标识符命名要有意义
- 类:首字母大写
- 函数:包括类的成员函数,小写
- 标识某个对象,小写
- 常量:全部大写
- 添加合适的注释
文档
docstring的机制在源码中直接插入文档提供完整的支持
主体中的第一个语句字符串都认为是docstring,限定在三引号(""")中
docstring作为模块,功能或类的声明的一个域进行存储。可以当做文档用,可用多种方式检索
测试和调试
测试
单元测试自动化,unittest模块
使用回归测试,通过对所有先前测试的重新执行来确保对软件的更改不会引入新的错误
调试
打印语句:最简单
调试器:更好
pdb模块,提供调试支持
类定义
类是面向对象程序设计中抽象的主要方法。
例子:CreditCard类
self标识符
构造函数
__init__
方法就是类的构造函数
封装
数据成员前加下划线,比如_balance,表示非公有的
附加方法
错误检查
测试类
将测试封装在if __name__ == '__main__':
中
运算符重载和python的特殊方法
操作重载
常见的语法 | 特别方法的形式 |
---|---|
a+b | a.__add__(b) 或b.__radd__(a) |
a-b | a.__sub__(b) 或b.__rsub__(b) |
a*b | a.__mul__(b) 或b.__rmul__(a) |
a/b | a.__truediv__(b) 或b.__rtruediv__(a) |
a//b | a.__floordiv__(b) 或b.__rfloordiv__(a) |
a%b | a.__mod__(b) 或b.__rmod__(a) |
a**b | a.__pow__(b) 或b.__rpow__(a) |
a<<b | a.__lshift__(b) 或b.__rlshift__(a) |
a>>b | a.__rshift__(b) 或b.__rrlshift__(a) |
a&b | a.__and__(b) 或b.__rand__(a) |
a^b | a.__xor__(b) 或b.__rxor__(a) |
a|b | a.__or__(b) 或b.__ror__(a) |
a+=b a-=b … | a.__iadd__(b) a.__isub__(b) … |
+a | a.__pos__(b) |
-a | a.__neg__(b) |
~a | a.__invert__(b) |
abs(a) | a.__abs__(b) |
a<b | a.__lt__(b) |
a<=b | a.__le__(b) |
a>b | a.__gt__(b) |
a>=b | a.__ge__(b) |
a==b | a.__eq__(b) |
a!=b | a.__ne__(b) |
vin a | a.__contains__(v) |
a[k] | a.__getitem__(k) |
a[k]=v | a.__setitem__(k,v) |
del a[k] | a.__delitem__(k) |
a(arg1,arg2,...) | a.__call__(arg1,arg2,...) |
len(a) | a.__len__() |
hash(a) | a.__hash__() |
iter(a) | a.__iter__() |
next(a) | a.__next__() |
bool(a) | a.__bool__() |
float(a) | a.__float__() |
int(a) | a.__int__() |
repr(a) | a.__repr__() |
reversed(a) | a.__reversed__() |
str(a) | a.__str__() |
非运算符重载
iter()
对应__iter__
隐式的方法
提供默认功能
例子:多维向量类
迭代器
支持__next__()
的特殊方法,如果有下一个元素,返回该元素,否则产生一个StopIteration异常,表示没有下一个元素
例子:Range类
class Range():
def __init__(self,start,stop=None,step):
if step==0:
raise ValueError("step cannot be 0")
if stop is None:
start,stop=0,start
self._length=max(0,(stop-start+step-1)//step)
self._start=start
self._step=step
def __len__(self):
return self._length
def __getitem__(self,k):
if k<0:
k+=len(self)
if not 0<=k<self._length:
raise IndexError('index out of range')
return self._start+k*self._step
继承
在一个分层的方式中,水平层次上类似的抽象定义组合在一起,使下层的组件更加具体,上层的组件更加通用
模块化和层次化就是继承
python的异常层次结构
数列的层次图
一个通用数字数列类:
class Progression:
def __init__(self,start=0):
self._current=start
def _advance(self):
self._current+=1
def __next__(self):
if self._current is None:
raise StopIteration()
else:
answer=self._current
self._advance()
return answer
def __iter__(self):
return self
def print_progression(self,n):
print(" ".join(str(next(self)) for j in range(n)))
if __name__ == '__main__':
p=Progression()
p.print_progression(10)
# 0 1 2 3 4 5 6 7 8 9
一个等差数列类:
from 一个通用数字数列类 import Progression
class ArithmeticProgression(Progression):
def __init__(self,increment=1,start=0):
super().__init__(start)
self._increment=increment
def _advance(self):
self._current+=self._increment
if __name__ == '__main__':
xxx=ArithmeticProgression(2,0)
xxx.print_progression(20)
# 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
一个等比数列类:
from 一个通用数字数列类 import Progression
class GeomertricProgression(Progression):
def __init__(self,base=2,start=1):
super().__init__(start)
self._base=base
def _advance(self):
self._current*=self._base
if __name__ == '__main__':
xxx=GeomertricProgression()
xxx.print_progression(10)
# 1 2 4 8 16 32 64 128 256 512
一个斐波那契数列类:
from 一个通用数字数列类 import Progression
class FibonacciProgression(Progression):
def __init__(self,first=0,second=1):
super().__init__(first)
self._prev=second-first
def _advance(self):
self._prev,self._current=self._current,self._prev+self._current
if __name__ == '__main__':
xxx=FibonacciProgression(2,3)
xxx.print_progression(10)
# 2 3 5 8 13 21 34 55 89 144
抽象基类
设计一个基类:避免重复代码
命名空间和面向对象
命名空间是一个抽象名词,它管理着特定范围内定义的所有标识符,将每个名称映射到相应的值
实例和类命名空间
每个已定义的类都有一个单独的类命名空间。用于管理一个类中所有实例所共享的成员或没有引用任何特定实例的成员
条目是怎样在命名空间中建立的
成员函数是最典型的在类命名空间居中声明的条目类型
类数据成员
有一些值(常量),被一个类的所有实例共享时,就会存储在父类的命名空间中
嵌套类
class A:
class B:
...
B类为嵌套类
优点:
- 表明嵌套类的存在需要外部类的支持,有助于潜在的命名冲突(可以在各自的容器类中嵌套各自的节点定义来避免歧义)
- 允许更高级的形式继承,使外部类的子类重载嵌套类的定义
字典和__slots__
声明
默认情况下,每个命名空间像一个dict类的实例,虽然字典结构支持相对有效的名称查找,但是需要的额外内存使用量超出了他存储原始数据的内存
python,使用流表示一个类的所有实例,类定义必须提供一个名为__slots__
的类级别的成员分配给一个固定的字符串序列,来服务于变量
可以使用__slots__
来简化类的声明,但并不会这么做,因为将使用python程序非典型
名称解析和动态调度
名称解析的过程:
- 实例命名空间中查找
- 实例所属的类命名空间中搜索
- 通过继承层次结构向上,检查每一个父类的类命名空间
- 没找到,引发一个AttributeError异常
深拷贝和浅拷贝
浅拷贝:简单赋值
深拷贝:
palette=copy.deepcopy(warmtones)