下面是第三部分的内容。内容中涉及的程序均来源于廖老师的Python教程(网址为https://www.liaoxuefeng.com/wiki/1016959663602400/)。
七、面向对象编程
面向对象相比函数更加抽象了些。
面向对象涉及:类,对象(即实例),方法和属性。
封装、继承和多态是面向对象的三大特点。
1. 类和实例
类的方法和普通的函数是没有区别的,因而之前说的“函数的参数”部分中的默认参数等都是适用的。
封装:类中的方法会访问类的属性的数据,无需知道方法的具体实现细节,从而实现封装。
>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8 #对类的属性数据直接赋值或修改
2. 访问权限
不想让实例中的属性被外部所访问,可以将属性的名称前加两个下划线(即__)。
若要获得实例中的属性值或想修改属性值的话,可以在类中自定义方法,实现上述的操作。
3. 继承和多态
继承:子类可以继承父类的方法。
多态:若子类中定义的方法和父类的名称一样,则调用该方法时会覆盖父类的方法。
相比于Java这样的静态语言,Python这样的动态语言的鸭子类型特点决定了不能像静态语言那样必须是继承于父类或子类,只要是有相同名字的方法的对象就可以。
4. 获取对象信息
type(xxx) 获取对象xxx的类型。这和isinstance是等价的。
isinstance可以判断对象或变量是否是其中的一种类型。例如
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
用dir()函数获得一个对象的所有属性和方法。例如
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
若想要查找某个对象是否具有某个属性或方法,可以采用
hasattr(对象,属性) 查找是否有该属性
setattr(对象,属性,属性值) 设置属性并赋值
getattr(对象,属性) 查找得到属性值
如果没有属性值的话,会抛出AttributeError的错误。
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
也可以像上述所示设置默认值。
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y
'19
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81
5. 实例属性和类属性
类属性属于类所有,所有的实例都具有类属性。
不要对类属性和实例属性起相同的名字。因为,名字相同时实例属性会屏蔽掉类属性,当删除掉实例属性后访问相同名称时是类属性。
八、面向对象高级编程
1. _slots_
使用_slots_限制实例的属性。
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
2. @property
为了更简单地访问属性并实现检查,可以采取装饰器@property的方式,将方法变为属性。
在@property将方法变为属性的时候,可以采用装饰器@xxx.setter(xxx是将其变为属性所对应的那个方法)把方法变为给属性赋值(在该方法中可以实现对属性的检查等操作)。注:在将方法变为属性、将方法变为属性赋值中,所用到的 方法名称是一样的。例子如下:
class Student(object):
@property #只读
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
>>> s = Student()>>> s.score = 60 # OK,实际转化为s.set_score(60)>>> s.score # OK,实际转化为s.get_score()60>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
@property保证了可以直接获得属性,并对属性值进行检查。
3. 多重继承
例如:Bat继承于类Mammal和Flyable。
class Bat(Mammal, Flyable):
pass
上述这种可以直接实现继承多个类的设计被称为MixIn。这使得我们只要将类进行组合就可以得到我们想要的类。
只允许单一继承的语言(如Java)是不允许MaxIn设计的。
4. 定制类
定制类:使用如_xxx_这样的方法(方法有特定的功能)可以帮助我们定制类。
常用的方法有:
_str_:得到类中的属性值;
_iter:支持类的类似于for ... in 循环;
_getitem_:获得元素;
_getattr_:类的方法或属性不存在时,避免出错;
_call_:使得可以对实例进行调用。
5. 使用枚举类
不是很明白本节内容!
九、错误、调试和测试
1. 异常处理机制
(1)try...except...finally
不需要在每个可能出错的地方都捕获错误,只要在合适的层来捕获就可以了。例如:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error:', e)
finally:
print('finally...')
(2)出错的时候,一定要关注错误的调试栈信息,才能定位错误信息。
(3)使用Python内置的logging模块记录错误信息。使用import logging来导入logging模块。
(4)raise可以用来抛出错误,也可以修改抛出的错误的类型!
2. 调试
print():程序中会有很多print(),以后可能会删除,比较麻烦。
assert():断言。但是,相比print()并没有太大的优势。
logging():和assert()相比,不会抛出错误,而且可以输出到文件。可以设置输出的不同级别,如
import logging
logging.basicConfig(level=logging.INFO)
级别有debug,info,warning,error等。
IDE:设置断点运行。
3. 单元测试
没太看懂!
4. 文档测试
Python中自带的文档测试模块(doctest模块)可以提取注释中的代码进行测试。
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__=='__main__':
import doctest
doctest.testmod()