Python 对象模型

1. Python中的旧式类与新式类

  你一定在其它地方见过这样的表述,“在 Python 中,一切皆对象”。
  
  那么,它意味着什么呢?是不是像其它编程语言一样(例如 Java),在 Python 中的一切都是基类的实例?如果是,那么 Python 中的基类是什么?我听说过 Python 中的 object 类,它是那个基类吗?
  
  Python 中这一块的内容相当复杂。Python 有旧式类和新式类两种模型。事实上,在早期 Python 版本中,Python 对于任何对象都没有显示的基类。但从 2.2 版本开始,引入了新式类,我们可以(或者必须)让每个对象都是 object 的子类。
  
  直到 Python 2.1 ,旧式类还一直是用户唯一可用的风格。旧式类与类型的概念无关。如果 x 是旧式类的实例,x.class 将应用 x 所属的类。但 type(x) 并不如此。我们来看一下下面的代码。
  这里写图片描述
  
  新样式类是从 Python 2.2 引入的。 它旨在为 Python 提供一个统一的对象模型。 在新式类中,引入了 object 类。 一切都继承自 object
  这里写图片描述
  
  新式类是通过继承自 object 类来定义的。 与旧式类不同,如果x是新样式类的实例,那么 x.classtype(x) 都指x所属的类。
  这里写图片描述
  
  为了与老版本兼容,Python 中的类仍然默认使用旧式类。 如果我们想使用新式类,我们需要定义类为 object 的子类。

2. 旧式类与新式类的不同

  旧式类与新式类之间的显著不同在于类型系统。
  
  我们来看一下旧式类和新式类处理多重继承。 “多重继承”是指一个类可以继承自多个类。 如果A继承自B,A是B的子类(派生类),B是A的超类(基类或者父类)。

  多重继承允许一个类A可以有很多父类(我个人不喜欢多重继承,当一个类可以有很多父类的时候,它会变得相当奇怪。为了解决这个问题,其他编程语言支持另外的模型,像 Java 中的接口 或者是 Ruby 中的混入。就我个人而言,我非常喜欢Ruby的混入模型)。

  在 Python 对象模型中,每个类都有基类属性,它们按继承中的出现顺序存储所有的父类。
  这里写图片描述
  
  那么,多重继承有什么问题呢?
  
  多重继承的问题是超类的顺序。 为什么继承顺序很重要? 因为当子类的实例访问属性(变量或函数)时。 首先,它会在自己的空间来查找该属性,如果未找到该属性,它将继续查找类空间。 如果仍然找不到,搜索方法继续超类空间。 当一个类有许多超类时,超类的顺序就是搜索方法中的顺序。 在旧式类中,超类的顺序是深度优先,从左到右按照基类列表中的出现顺序。
  这里写图片描述

  旧式类方法的解析相当简单易懂,对吧? 但是,如果我们遵循这种规则,有时我们会在继承时犯错误。 例如,我们创建一个类G继承自A,D和E,而A是D和E的父类。它应该在这个上下文中抛出一个错误,以防止像这样的循环继承。
  
  新式类解决了这个问题。 新式类从 Python 2.3 引入了 MRO(方法解析顺序)。 MRO 的算法可以描述如下 

def mro(cls):
    """
    Return ordering of superclass of cls
    This ordering was used when we want to access class instance atrribute

    `cls`: class type we want to resolve

    @raise `TypeError` if cannot resolution superclass order
    @return `list` of class
    """
    bases_cls = cls.__bases__
    mro_base_lists = [mro(base_cls) for base_cls in bases_cls]
    mro_list = [cls]
    while mro_base_lists:
        # find the good head
        # good head is head of a list which is not is tail of any list in mro_base_lists
        list_head = (x[0] for x in mro_base_lists)
        set_tails = set()
        for x in mro_base_lists:
            set_tails.update(x[1:])

        good_head = None
        for head in list_head:
            if head not in set_tails:
                good_head = head
                break

        # if cannot find good_head, raise TypeError
        if not good_head:
            raise TypeError
        else:
            # add to mro_list
            mro_list.append(good_head)

            # remove good_head in all list and add to mro_list
            for alist in mro_base_lists:
                try:
                    alist.remove(good_head)
                except Exception:
                    pass
            mro_base_lists = [x for x in mro_base_lists if x]
    return mro_list

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
class E(C, B): pass
class F(D, E): pass

def test_mro():
    assert mro(A) == [A]
    print "Test1 passed"

    assert mro(B) == [B, A]
    print "Test2 passed"

    assert mro(C) == [C, A]
    print "Test 3 passed"

    assert mro(D) == [D, B, C, A]
    print "Test 4 passed"

    assert mro(E) == [E, C, B, A]
    print "Test 5 passed"

    try:
        mro(F)
    except Exception as e:
        assert isinstance(e, TypeError)
        print "Test 6 passed"

test_mro()

  
  MRO算法的思想源于基类排序情况:如果B是C的子类,B总是在列表中的C之前。 这就是为什么我们收到一个 TypeErorr 如果我们定义类D(B,C)和E(C,B)之后定义F(D,E)。
  
  想更多的了解 MRO 是如何工作的,你应该参阅 The Python 2.3 Method Resolution Order
  
  新式类和旧式类的不同不仅体现在基类顺序方面。 我会在下一篇文章中介绍这个主题。
 
 原文链接: Python Object Model

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值