前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Day11面向对象高级编程2/3

Day11面向对象高级编程2/3

作者头像
林清猫耳
发布2018-04-26 15:31:52
6530
发布2018-04-26 15:31:52
举报
文章被收录于专栏:我爱编程我爱编程我爱编程

多重继承

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。 为了更好地看出继承关系,我们把RunnableFlyable改为RunnableMixInFlyableMixIn。类似的,你还可以定义出肉食动物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)
  1. 第一次,Chain(),这个和其他类一样,调用Chain类的构造函数,返回一个Chain类的对象,假设命名为chpath=''
  2. 第二次,对ch变量调用他的a属性,因为不存在,调用__getattr__函数,返回一个Chain对象,这个时候里面的path就是''+'/'+'a'='/a'
  3. 第三次把a对象当做函数调用,参数是'mike',显然函数不存在,调用__call__方法,参数传进去,并且将新对象返回,这个时候返回的还是一个Chain的对象,path='/a'+':'+'mike'='/a:mike'
  4. 同第二步
  5. 同第二步
  6. 同第二步
  7. 同第二步
  8. 打印输出对象的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,剩下的类同。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.03.31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 多重继承
    • MixIn
    • 定制类
      • __ iter__
        • __ getitem__
          • __ getattr__
            • __ call__
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档