Day-9: 面对对象高级编程
数据封装、继承和多态只是面向对象编程中最基础的3个概念。
下面整理面向对象高级编程的更为强大的技巧。
使用__slots__:Python属于动态语言,可以允许已创建好的类动态地绑定任何属性和方法。但是,给实例绑定后,由该类创建的其他其他实例是没有绑定的;不过,可以给类绑定,那么有该类创建的实例均会拥有该属性和方法。
>>> class Student(object): ... pass ...
>>> s = Student() >>> s.name = 'Michael' # 动态给实例绑定一个属性 >>> print s.name Michael
>>> def set_age(self, age): # 定义一个函数作为实例方法 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s, Student) # 给实例绑定一个方法 >>> s.set_age(25) # 调用实例方法 >>> s.age # 测试结果 25
>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = MethodType(set_score, None, Student)
同时,正因为动态语言的这种特性,为了避免过于属性绑定,class类中使用__slots__来限制允许的属性。
>>> class Student(object): ... __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 ...
>>> s = Student() # 创建新的实例 >>> s.name = 'Michael' # 绑定属性'name' >>> s.age = 25 # 绑定属性'age' >>> s.score = 99 # 绑定属性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'
__slots__属性只对父类起作用,但继承的子类是不起作用的,除非子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__。
使用@property装饰器:
在实际使用中,为了检查修改参数的合法性,会定义一个get和set函数,对set函数进行一系列的严格验证。
class Student(object): def get_score(self): return self._score def set_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.set_score(60) # ok! >>> s.get_score() 60 >>> s.set_score(9999) Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
为了简化上述调用和设置的过程,将两个方法直接变成调用属性一样简单,使用@property装饰器。
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!
就能像调用普通属性一样方便了!
也可以只定义只读属性:
class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2014 - self._birth
- 多重继承
在继承时,由于对象会有多个属性,一般按照继承关系来说,选取其中一个大的类别作为主线,即主线是单一继承下来的。除主线外,其他的属性可以作为功能,多重继承下来。这种设计称为Mixin。
例如,下面中,Mammal和Bird作为主线,Runnable和Flyable作为Mixin功能增加进去,构成多重继承。
class Animal(object): pass # 大类: class Mammal(Animal): pass class Bird(Animal): pass
class Runnable(object): def run(self): print('Running...') class Flyable(object): def fly(self): print('Flying...')
class Dog(Mammal, Runnable): pass
class Bat(Mammal, Flyable): pass
- 定制类
之前提到过的以双下划线开头和结尾的属性,是属于特殊属性,它们是用来定制类的。
__str__():显示print该类的实例的名称
>>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print Student('Michael') Student object (name: Michael)
__repr__():直接显示变量
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__ >>>s = Student('Michael') >>>s Student object (name: Michael)
__iter__():服务于for...in的迭代循环,该方法返回一个迭代对象,然后,Python的for循环会一直调用next()方法得到循环的下一个值,直到遇到StopIteration错误后退出循环。
lass Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def next(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration(); return self.a # 返回下一个值
>>> for n in Fib(): ... print n ... 1 1 2 3 5 ... 46368 75025
__getitem__():允许像list一样按索引取值和切片。
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice):
start = n.start
stop = n.stop
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
>>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3] 3 >>> f[10] 89 >>> f[100] 573147844013817084101
>>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
__getattr__():当调用类的属性或者方法不存在时,运行__getattr__()方法。
class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99
>>> s = Student() >>> s.name 'Michael' >>> s.score 99
__call__():对实例本身进行调用时,启用该方法。
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name)
>>> s = Student('Michael') >>> s() My name is Michael.
通过callable()函数,可以判断一个对象是否是“可调用”对象。
更多定制类的方法,参考Python文档
注:本文为学习廖雪峰Python入门整理后的笔记