多重继承
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
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
通过多重继承,一个子类就可以同时获得多个父亲的所有功能。
MixIn
在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich
继承自Bird
。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich
除了继承自Bird
外,再同时继承Runnable
。这种设计通常称之为MixIn。
为了更好地看出继承关系,我们把Runnable
和Flyable
改为RunnableMixIn
和FlyableMixIn
。类似的,你还可以定义出肉食动物CarnivorousMixIn
和植食动物HerbivoresMixIn
,让某个动物同时拥有好几个MixIn:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。
编写一个多进程模式的TCP服务,定义如下:
class MyTCPServer(TCPServer, ForkingMixIn):
pass
编写一个多线程模式的UDP服务,定义如下:
class MyUDPServer(UDPServer, ThreadingMixIn):
pass
如果你打算搞一个更先进的协程模型,可以编写一个CoroutineMixIn
:
class MyTCPServer(TCPServer, CoroutineMixIn):
pass
这样一来,我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。
定制类
>>> class Student(object):
... def __init__(self, name):
... self.name = name
...
>>> print(Student('Innove'))
<__main__.Student object at 0x060D6B90>
打印出一堆<__main__.Student object at 0x060D6B90>
,不好看。
怎么才能打印得好看呢?只需要定义好__str__()
方法,返回一个好看的字符串就可以了:
>> class Student(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
...
>>> print(Student('Innove'))
Student object (name: Innove)
但是量不用print,打印出来的实例还是不好看:
s = Student('Innove')
>>> s
<__main__.Student object at 0x0599D8B0>
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
>>> s = Student('Innove')
>>> s
Student object(name:Innove)
__ iter__
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 > 1000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
>>> for n in Fib():
print(n)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
__ getitem__
__getitem__
方法可以按照下标取出元素。
class Fib(object):
def __getitem__(self,n):
a , b = 1 , 1
for x in range(n):
a, b = b, a + b
return a
>>> Fib()[24]
75025
>>> Fib()[197]
107168651819712326877926895128666735145224
__getitem__()
传入的参数可能是一个int,也可能是一个切片对象slice
,如果要切片需要做判断:
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[3:8]
[3, 5, 8, 13, 21]
>>> f[3:8:2]
[3, 5, 8, 13, 21]
没有对负数和step参数的处理。
__ getattr__
Python的一个机制,写一个getattr()方法,动态返回一个属性。
class Student(object):
def __init__(self):
self.name = 'Innove'
def __getattr__(self,attr):
if attr == 'score':
return 77
>>> Student().score
77
>>> Student().name
'Innove'
也可以返回函数:
class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 22
>>> Student().age() #注意调用方式
25
任意调用如Student().abc
都会返回None,这是因为我们定义的__getattr__
默认返回就是None
。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError
的错误:
class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 25
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
利用完全动态的__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'
__ call__
任何类,只需要定义一个__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() # self参数不要传入
My name is Michael.
__call__()
还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()
的类实例:
>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False
通过callable()
函数,我们就可以判断一个对象是否是“可调用”对象。
return Chain('%s/%s'% (self._path,attr))
这句话表示返回Chain类的一个对象
Chain().a('mike').b.c.e.f
,这句话总共执行了7次函数。
这句话相当于以下几句
ch=Chain()
a=Chain.a
amike=a()
b=aminke.b
c=b.c
e=c.e
f=c.f
print(f)
- 第一次,
Chain()
,这个和其他类一样,调用Chain类的构造函数,返回一个Chain类的对象,假设命名为ch
,path=''
; - 第二次,对
ch
变量调用他的a
属性,因为不存在,调用__getattr__
函数,返回一个Chain对象,这个时候里面的path就是''+'/'+'a'='/a'
- 第三次把
a
对象当做函数调用,参数是'mike'
,显然函数不存在,调用__call__
方法,参数传进去,并且将新对象返回,这个时候返回的还是一个Chain的对象,path='/a'+':'+'mike'='/a:mike'
- 同第二步
- 同第二步
- 同第二步
- 同第二步
- 打印输出对象的path,为'/a:mike/b/c/e/f'
class Chain(object):
def __init__(self,path=""):
self._path = path
def __getattr__(self,path):
return Chain("%s/%s" %(self._path,path)) #返回实例并将path传入
def __call__(self,path):
return Chain("%s/%s" %(self._path,path))
def __str__(self):
return self._path
__repr__ = __str__
print(Chain().a.b.user("Michael").c.d)
代码执行流程:
Chain()
创建一个实例,并且 path初始默认为""
,Chain().a
时,类中并没有 a
属性,Python解析器调用 getattr
函数 --> __getattr__(self,path='a')
,
并返回一个Chain实例,然后把/a
赋值给 path 传入,继续b
,因为同样没有b
属性,执行getattr
函数,将/a/b
传入,
然后.user(“Michael”)
,先会执行getattr
返回Chain实例,但是因为有()
括号在,所以返回的是Chain()
,
这个就会调用call
函数了,然后把“Michael”
作为path传入,然后call
函数就返回了/a/b/user/Michael
,剩下的类同。
网友评论