使用@property 多重继承

作者 新城 日期 2017-08-27
使用@property 多重继承


(多重继承)[https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318680104044a55f4a9dbf8452caf71e8dc68b75a18000]

继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能

问题
有以下四种动物

  • Dog - 狗狗;
  • Bat - 蝙蝠;
  • Parrot - 鹦鹉;
  • Ostrich - 鸵鸟;
    如果按照哺乳动物和鸟类归类,我们可以设计出这样的类的层次



    但是如果按照“能跑”和“能飞”来归类,我们就应该设计出这样的类的层次:



    如果要把上面的两种分类都包含进来,我们就得设计更多的层次
    - 哺乳类:能跑的哺乳类,能飞的哺乳类;
    - 鸟类:能跑的鸟类,能飞的鸟类
    类的层次就复杂了


如果要再增加“宠物类”和“非宠物类”,这么搞下去,类的数量会呈指数增长,很明显这样设计是不行的。

正确的做法是采用多重继承。首先,主要的类层次仍按照哺乳类和鸟类设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal(object):   #动物类
pass

class Mammal(Animal): #哺乳类
pass

class Bird(Animal): #鸟类
pass

class Dog(Mammal): #以下是各种动物类
pass

class Bat(Mammal):
pass

class Parrot(Bird):
pass

class Ostrich(Bird):
pass

然后我们提前定义好动物具备的行为

1
2
3
4
5
6
7
class Runnable(object):
def run(self):
print('Running...')

class Flyable(object):
def fly(self):
print('Flying...')

比如让狗继承动物类和跑的行为

1
2
class Dog(Mammal, Runnable):        #此处继承了 哺乳类 和 跑的行为
pass

通过多重继承,一个子类就可以同时获得多个父类的所有功能

MixIn自由继承

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn

定制类

str
定义以下函数

1
2
3
4
class Student(object):
def __init__(self, name):
self.name = name
print(Student('Michael'))

输出 ><main.Student object at 0x109afb190>

后面序号是内存地址

为了得到对象内部具体数据 我们定义如下

1
2
3
4
5
6
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)

iter
如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个iter()方法,该方法返回一个迭代对象,
然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class 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)

getitem

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,
比如,取第5个元素:Fib()[5] 就会报错

要表现得像list那样按照下标取出元素,需要实现getitem()方法

1
2
3
4
5
6
7
8
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
f = Fib()
print (f(0)) #1

list有切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
print (f[0:5]) #输出[1, 1, 2, 3, 5]

getattr

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类

1
2
3
4
5
6
7
8
class Student(object):
def __init__(self):
self.name = 'Michael'

s = Student()

print (s.name) #输出正常
print (s.score) #报错

要想解决这个问题
1.在类中添加score这个属性
2.写一个getattr()方法,动态返回一个属性。修改如下

1
2
3
4
5
6
7
8
class Student(object):

def __init__(self):
self.name = 'Michael'

def __getattr__(self, attr):
if attr=='score':
return 99

call

创建一个实例的时候自动调用这个方法

1
2
3
4
5
6
7
8
9
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. 自动调用call方法

通过callable()函数可以判断一个对象是否可调用

1
2
3
4
5
callable(max)   #输出 True 可以调用

callable(Student()) #输出 True 可以调用

callable([1, 2, 3]) #输出 False 不可调用

关于函数定制(官方文档)[https://docs.python.org/3/reference/datamodel.html#special-method-names]