python核心编程-第四章-深刻类和对象

第四章、深刻类和对象

4.1鸭子类型

当看到一只鸟走起来像鸭子、游泳像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子python

4.2抽象基类(abc模块)

判断某一个对象是否有某一个属性,hasattr()程序员

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __getitem__(self, item):
        return self.employee[item]

    def __len__(self):
        return len(self.employee)


print(hasattr(Company, "__len__"))
company = Company(['tom', 'bob', 'jane'])
print(hasattr(company, "__len__"))

判断某一个对象是不是某种类型,isinstance()web

对于判断对象是否能够调用某函数,更好的方法是去判断其是不是某种类型,而不是该对象是否有某个属性。redis

# 在某些状况下但愿断定某个对象的类型,咱们须要强制某个子类必须实现某些方法

from collections.abc import Sized
class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __getitem__(self, item):
        return self.employee[item]

    def __len__(self):
        return len(self.employee)


company = Company(['tom', 'bob', 'jane'])
print(isinstance(company, Sized))
# 设计一个抽象基类,指定子类必须事项某些方法
# 实现一个web框架,集成cache(redis, cache, memorychache)
class CacheBase():
    def get(self, key):
        raise NotImplementedError
    def set(self, key, value):
        raise NotImplemnetedError
   

class RedisCache(CacheBase):
    pass


redis_cache = RedisCache()
redis_cache.set("key", "value")

报错以下示:算法

1606741217010

# 做以下更改
class RedisCache(CacheBase):

    def __init__(self):
        self.dic = {}

    def get(self, key):
        return self.dic[key] if key in self.dic else None

    def set(self, key, value):
        self.dic[key] = value


redis_cache = RedisCache()
redis_cache.set("key", "value")
print(redis_cache.get("key"))

1606741758636

4.3使用instance而不是type

  • type判断一个对象是谁的实例
  • isinstance是判断这个对象的继承链,isinstance(a,b)若是a在b的下方就返回True,不然False
class A(object):
    pass


class B(A):
    pass


class C(B):
    pass


c = C()
print(type(c))  # <class '__main__.C'>
print(type(C))  # <class 'type'>
print(isinstance(C, B))  # False
print(isinstance(C, object))  # True

4.4类属性和实例属性以及查找顺序

先实例属性再类属性编程

若是把实例当作是类,那么类变量就是父类中的属性,而实例变量是子类的属性,子类中没有父类有 会从父类中继承,子类中有父类有 至关于覆盖重写框架

class A:
    name = 'bb'
    def __init__(self, x):
        self.x = x


a1 = A(1)
a2 = A(2)

print(a1.name)  #继承类变量bb
print(a1.x)  # 就是实例变量x的值
print(A.name)  # 类变量bb

A.name = 'dd'  # 更改了类变量
print(A.name)  # 类变量发生更改dd
print(a1.name)  # 实例的类变量也更改dd

a1.name = 'name'
print(a1.name)  # 在此更改类变量
print(A.name)  # 删除了实例属性

A.name = 'last'  # 在此更改了类变量
del a1.name  # 删除了实例变量
print(a1.name)  # 继承类变量last

类变量和属性的调用顺序:函数

在python中,为了重用代码,能够使用在子类中使用父类的方法,可是在多继承中可能会出现重复调用的问题,支持多继承的面向对象编程均可能会致使钻石继承(菱形继承)问题 ,以下示:测试

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")

class B(A):
    def __init__(self):
        print("进入B…")
        A.__init__(self)
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        A.__init__(self)
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        B.__init__(self)
        C.__init__(self)
        print("离开D…")
      
d = D()
# 实际上会先找B, 而后找A, 而后是C, 再找A
'''
进入D…
进入B…
进入A…
离开A…
离开B…
进入C…
进入A…
离开A…
离开C…
离开D…
'''

钻石继承(菱形继承)会带来什么问题?.net

多重继承容易致使钻石继承(菱形继承)问题,上边代码实例化 D 类后咱们发现 A 先后进入了两次。另外,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(若是遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。

如何避免钻石继承(菱形继承)问题?

为解决这个问题,Python 使用了一个叫“方法解析顺序(Method Resolution Order,MRO)”的东西,还用了一个叫 C3 的算法。

MRO 的顺序基本就是:在避免同一类被调用屡次的前提下,使用广度优先和从左到右的原则去寻找须要的属性和方法。有则调用,没有就查找添加到MRO中。

在继承体系中,C3 算法确保同一个类只会被搜寻一次。例子中,若是一个属性或方法在 D 类中没有被找到,Python 就会搜寻 B 类,而后搜索 C类,若是都没有找到,会继续搜索 B 的基类 A,若是仍是没有找到,则抛出“AttributeError”异常。

能够使用 类名.mro 得到 MRO 的顺序(注:object 是全部类的基类,金字塔的顶端):

print(D.mro())
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

为了防止多继承中的一些问题,使用C3算法解决,它可以保证调用链中全部的类仅出现一次,也就是说每一个节点后面的类都不继承它,不然将它从调用链中删除,D.mro()查看调用链。

super()的使用就是基于这条调用链,当使用super()时,会查找调用链,找到当前类,调用下一个类,没有则找下一个。super()默认是当前类,也能够写类名

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")

class B(A):
    def __init__(self):
        print("进入B…")
        super().__init__()
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        super().__init__()
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        super().__init__()
        print("离开D…")
    
    
d = D()
'''
进入D…
进入B…
进入C…
进入A…
离开A…
离开C…
离开B…
离开D…
'''

4.4.1mro序列

MRO的局限性:类型冲突时, 子类改变了基类的方法搜索顺序。而 子类不能改变基类的方法搜索顺序。在 Python 2.2 的 MRO 算法中并不能保证这种单调性,它不会阻止程序员写出上述具备二义性的继承关系,所以极可能成为错误的根源。

博客:https://mp.weixin.qq.com/s?src=11&timestamp=1606794886&ver=2739&signature=SRS5nobqUFoRtzuAsoe8pOknkOYeNxLZz90OnfG3sQtaaMcaI23ZYMSjmGLKogS997A1whxzOgqTugCvuNSlEz5akZ86QE4myAw2JboDp-DRHrsLiWKCj97Ld2lKTq&new=1

  • MRO(Method Resolution Order: 方法解析顺序) 是一个有序列表L,在类被建立时就计算出来。
  • 通用的计算公式:
mro(Child(Base1, Base2)) = [Child] + merge(mro(Base1), mro(Base2), [Base1, Base2])
 (其中Child继承自Base1, Base2)
  • 若是继承至一个基类:class B(A)
    这时B的mro序列为 :
mro( B ) 
= mro( B(A) ) 
= [B] + merge( mro(A) + [A] )
= [B] + merge( [A] + [A] )
= [B,A]
  • 若是继承至多个基类:class B(A1, A2, A3 …)
    这时B的mro序列
mro(B)  
= mro( B(A1, A2, A3 …) )
= [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
= ...

PS: 计算结果为列表,列表中至少有一个元素即类本身,如上述示例[A1,A2,A3]。merge操做是C3算法的核心。

(参考博客:https://blog.csdn.net/u011467553/article/details/81437780)

4.4.2 表头和表尾

  • 表头:
    列表的第一个元素
  • 表尾:
    列表中表头之外的元素集合(能够为空)
  • 示例
    列表:[A, B, C]
    表头是A,表尾是B和C

4.4.3 列表之间的+操做

+操做:

list_ = ['A'] + ['B']  
print(list_)  # ['A', 'B']

4.4.4 merge操做(C3算法)

img

如计算merge( [E,O], [C,E,F,O], [C] )
有三个列表 : list1    list2   list3

1 merge不为空,取出第一个列表列表list1的表头E,进行判断                              
各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,于是跳过当前当前列表
2 取出列表list2的表头C,进行判断
C不在各个列表的集合中,于是将C拿出到merge外,并从全部表头删除
merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] )
3 进行下一次新的merge操做 ......
  
merge( [E,O], [C,E,F,O], [C]) 
= [C] + merge( [E,O], [E,F,O] )
= [c] + [E] + merge( [O], [F, 0])
= [c] + [E] + [F] + merge( [O], [0])
= [C] + [E] + [F] + [0]

看看以前的

print(D.mro())
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

mro(D(B, C))
= [D] + merge(mro(B(A)), mro(C(A)), [B, C])
= [D] + merge([B] + merge(A(object), [A]), [C] + merge(A(object), [A]), [B, C])
= [D] + merge([B] + merge([A, object], [A]), [C] + merge([A, object], [A]), [B, C])
= [D] + merge([B] + [A, object], [C] + [A, object], [B, C])
= [D] + merge([B, A, object], [C, A, object], [B, C])
= [D] + [B] + merge([A, object], [C, A, object], [C])
= [D] + [B] + [C] + merge([A, object], [A, object])
= [D] + [B] + [C] + [A] + merge([object], [object])
= [D] + [B] + [C] + [A] + [object]
= [D, B, C, A, object]
= [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

4.4.5实战测试: 案例一

多继承UML图:

# 备注:O==object, 如何计算mro(A) ?
mro(A)
= mro(A(B, C))
= [A] + merge(mro(B(D, E)), mro(C(E, F)), [B, C])
= [A] + merge([B] + merge(mro(D(O)), mro(E(O)), [D, E]), [C] + merge(mro(E(O)), mro(F(O)), [E, F]), [B, C])
= [A] + merge([B] + merge([D, O], [E, O], [D, E]), [C] + merge([E, O], [F, O], [E, F]), [B, C])
= [A] + merge([B] + [D, E, 0], [C] + [E, F, O], [B, C])
= [A] + merge([B, D, E, 0], [C, E, F, O], [B, C])
	merge([D, O], [E, O], [D, E])
	= [D] + merge([O], [E, O], [E])
	= [D] + [E] + merge([O], [O])
	= [D] + [E] + [O]
	= [D, E, 0]
	merge([E, O], [F, O], [E, F])
	= [E] + merge([O], [F, O], [F])
	= [E] + [F] + merge([O], [O])
	= [E] + [F] + [O]
	= [E, F, O]
= [A] + [B] + merge([D, E, 0], [C, E, F, O], [C])
= [A] + [B] + [D] + merge([E, 0], [C, E, F, O], [C])
= [A] + [B] + [D] + [C] + merge([E, 0], [E, F, O])
= [A] + [B] + [D] + [C] + [E] + merge([0], [F, O])
= [A] + [B] + [D] + [C] + [E] + [F]+ merge([0], [O])
= [A] + [B] + [D] + [C] + [E] + [F]+ [O]
= [A, B, D, C, E, F, O]
# 以上案例的代码测试
class D: pass
class E: pass
class F: pass
class B(D,E): pass
class C(E,F): pass
class A(B,C): pass

print("从A开始查找:")
for s in A.__mro__:
	print(s)

print("从B开始查找:")
for s in B.__mro__:
	print(s)

print("从C开始查找:")
for s in C.__mro__:
	print(s)
'''
从A开始查找:
<class '__main__.A'>
<class '__main__.B'>
<class '__main__.D'>
<class '__main__.C'>
<class '__main__.E'>
<class '__main__.F'>
<class 'object'>
从B开始查找:
<class '__main__.B'>
<class '__main__.D'>
<class '__main__.E'>
<class 'object'>
从C开始查找:
<class '__main__.C'>
<class '__main__.E'>
<class '__main__.F'>
<class 'object'>
'''

规律总结: “从一至顶,有子先出”

4.4.6实战测试:案例二

这里写图片描述

结果参考:

img

相关文章
相关标签/搜索