多继承和定制类

167 阅读4分钟

实现继承:

>>> 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
...

定义Runnable 和 Flyable 两个类:

>>> class Runnable(object):
...     def run(self):
...             print('Running...')
...
>>> class Flyable(object):
...     def fly(self):
...             print('Flying...')
...

多继承:

>>> class Dog(Mammal, Runnable):    #dog多继承一个Runnable
...     pass
...
>>> class Bat(Mammal, Flyable):   #Bat 多继承一个Flyable
...     pass
...

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

打印实例信息:

>>> class Student(object):
...     def __init__(self, name):
...             self.name = name
...
>>> print(Student('Michael'))
<__main__.Student object at 0x000001F96FC06898>

可以看到打印的内容不好看

定义 str()方法,返回好看的字符串:

>>> 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)

如果不用 print,打印的效果:

>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x000001F96FC06978>

这是因为 直接显示变量没有调用__str__(), 而是__repr__()。那么为了显示好看点,那么可以进行 重写,当然为了简便,我们直接赋值。

简便的重定义__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() 方法返回一个迭代对象, Python 的 for循环不断调用迭代对象的 next()方法, 实现斐波那契数列:

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

46368
75025

通过 getitem 获取 Fib 对应下标元素:

>>> 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()
>>> f[0]
1
>>> f[100]
573147844013817084101

list 切片方法:

>>> list(range(100))[5:10]
[5, 6, 7, 8, 9]

对 Fib 进行切片:

>>> f = Fib()
>>> f[0:5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __getitem__
TypeError: 'slice' object cannot be interpreted as an integer

发现对 Fib 作切片出错了。原因是 getitem()传入的参数可能是一个 int,也可能是对象的 slice 所以要作判断

正确的 Fib 切片:

>>> 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
...                     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
...
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>>

获取不存在的属性会报错:

>>> class Student(object):
...     def __init__(self):
...             self.name = 'Michael'
...
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

通过使用 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.sco     # 获取不存在的属性,会默认返回None
>>> s.score   # 获取不存在的属性,会试图调用 __getattr__(self, 'score') 来获取 score 值
99

也可以返回函数:

>>> class Student(object):
...     def __getattr__(self, attr):
...             if attr == 'age':
...                     return lambda: 25
...
>>> s = Student()
>>> s.age()
25

注意,只有在没有找到对应的属性,才会调用__getattr__() 方法。

只响应特定的属性,其他的就提示报错:

>>> class Student(object):
...     def __getattr__(self, attr):
...             if attr == 'age':
...                     return lambda:25
...             raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
...
>>> s = Student()
>>> s.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Student' object has no attribute 'a'
>>> s.age()
25

灵活应对API 改动,利用__getattr__,我们写一个链式调用:

>>> class Chain(object):
...     def __init__(self, path=''):
...             self._path = path
...     def __getattr__(self, path):
...             return Chain('%s/%s' %(self._path, path))
...     def __str__(self):
...             return self._path
...     __repr__ = __str__
...
>>> Chain().status.user.timeline.list
/status/user/timeline/list

一般通过实例再调用方法 如:instance.method()。 但通过 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() 判断一个对象是否能直接被调用:

>>> callable(Student('ssz'))  # Student 带有 __call__
True
>>> callable(max)             # max 是一个 Callable对象
True
>>> callable(None)
False
>>> callable('str')
False

总结: 1、一个子类通过多继承来实现父类的所有功能 2、定义__str__ 方法让打印的字符串更好看 3、重新定义__repr__() 让直接显示变量的字符串更好看 4、iter 来实现返回迭代对象 5、getattr 使获取不存在的属性不报错 6、callable()判断一个对象是否可以直接被调用