Python 类型和对象

Shalabh Chaturvedi

Copyright © 2005-2009 Shalabh Chaturvedi
All Rights Reserved.

(小明同学译 wjm251 AT gmail.com

关于本书

解释新式的Python对象(new-style):

新式类型New-style包含在Python2.2及以上,包括3.x。这些版本之间会有细微差异,不过这里涉及到的概念都是无差别的。本书讲的内容通常被称作Python类型系统或Python对象模型Python type system, or the object model.

This revision: 1.24

图索引

示例索引

需要提前了解的

应该注意的一些:

python之旅快乐?Happy pythoneering!

1. 基本概念

深入Object

怎样准确的表达python中object的含义呢? 系统中每个对象是一条规则 - 是一个实体的体现. 可以通过如下特征来确定一个对象:

==== type 和 bases === (如果存在的话) 很重要,因为它们定义了对象和其他对象之间的特定关系. 请记住对象的types 和 bases只是另外一些对象.很快就会看到这一点.

你可能会想到对象有名字但名字不是对象的一部分. 对象名在对象之外的命名空间中存在(比如函数的本地变量) 或者作为另一个对象的属性.

即使对2这样一个简单对象所包含的东西也比我们眼睛看到的要多.

Example1.1. 查看integer对象

>>> two = 2   1
>>> type(two)
<type 'int'> 2
>>> type(type(two))
<type 'type'> 3
>>> type(two).__bases__
(<type 'object'>,) 4
>>> dir(two) 5
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', 
 '__delattr__', '__div__', '__divmod__', '__doc__', '__float__',
 '__floordiv__', '__format__', '__getattribute__', '__getnewargs__',
 '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__',
 '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__',
 '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__',
 '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__',
 '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__',
 '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
 '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__',
 '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__',
 'conjugate', 'denominator', 'imag', 'numerator', 'real']

耐心点先来看我们的第一条规则

规则1
  • 任何东西都是一个object

int 是 object. 这不是说例如2和77这样的数字是object (他们确实是), 而是说确实有另外一个对象叫做 int ,他处于内存中的实际的整数之外的内存中. 实际上所有的整数对象的class属性都指向 int,他们共同的心声是"知我者int也". 在一个object上调用type()方法返回的也正好是这个对象的class属性的值.

我们定义的所有classes都是对象, 当然这些classes的实例也是对象.甚至我们定义的方法和函数也是对象. 并且像我们看到的,这些对象都有些不同.

空白状态

现在我们来从头构建python的对象系统. 我们从一个空白状态开始.

Figure 1.1. 空白状态

http://www.cafepy.com/article/python_types_and_objects/images/clean_slate.png

你可能在想为啥有两天灰色的竖线. 后面慢慢来揭晓. 竖线是来区别不同的区域的. 这个空白图中, 我们来逐渐加上各种对象,画上他们之间的关系,慢慢画满.

这个有助于帮助我们暂且搁置先入为主的各种有关面向对象的类和对象的观念,以对象的方式(我们这里的对象)来理解一切。

对象之间的关系

我们使用两种关系就可以连接我们所引入的这许多不同的对象. 这两种关系是子类-父类关系 (也可叫做特别化或继承, 如"人是个动物", 等等.) 和类-实例关系 (也叫实例化, 比如"Joe是个人", 等等.)。

如果你对这些概念已经熟悉了,可以继续往下看了,否则的话你可能需要先去看看这一部分“面向对象”.

2.引入对象

第一批对象

我们来查看<type 'object'> 和 <type 'type'>这两个对象.

Example 2.1. Examining <type 'object'> and <type 'type'>

>>> object 1
<type 'object'>
>>> type 2
<type 'type'> 
>>> type(object) 3
<type 'type'>
>>> object.__class__ 4
<type 'type'>
>>> object.__bases__ 5
()
>>> type.__class__ 6
<type 'type'>
>>> type.__bases__ 7
(<type 'object'>,)

下面我们来画一下所看到的.

Figure 2.1. 鸡和蛋的关系

http://www.cafepy.com/article/python_types_and_objects/images/chicken_and_egg.png

图中两个对象是python最基本对象. 也可以一次介绍一个,但这里有个鸡和蛋一样的矛盾 - 先介绍哪个好? 这两个对象是互相依赖的 - 每一个本身都不能单独存在,都要借助对方来描述自己.

还是继续我们的python实验吧:

Example 2.2. 关于 <type 'object'> 和 <type 'type'>的更多内容

>>> isinstance(object, object) 1
True
>>> isinstance(type, object) 2
True

如果对上面的证明不太理解, 没关系 - 反正也没什么用.

再看一个新概念 - 类对象type objects. 我们介绍过的对象都是类对象. 所说的类对象是什么呢? 类对象有如下共同特点:

是的. Types和classes在Python中确实是同一个东西 (免责声明: 对老式类和早于2.2版本的Python不适用. 那是 types和classes有些不同但那时很久前的事了,现在已这些不同点已经一致了。因此让过去的事就过去吧,不好吗?). 不要吃惊,使用type()函数和class属性得到的是一样的.

class 这个术语传统是指被class这个关键字创建的class. 内置类型 (比如int和string)通常不和classes联系起来, 这更多是约定俗成的,实际上types和classes是同一个东西.我觉得这点很重要,可以提出一个规则:

Class is Type is Class
  • type和class这两个术语在所有Python >= 2.3的版本中是一样的.

类对象Types 和 (嗯,,让我想个更好的词) 非类对象non-types (呵呵!) 都是对象 但只有types可以子类化. Non-types代表的是具体的值所以子类化并没有意义. 非类对象的两个例子是整数 2 以及字符串 "hello". 想想,2的子类是什么意思,没意思吧?

还对判断类对象和非类对象感觉混淆吗?有一个简单的规则:

类和非类对象判断规则Type Or Non-type Test Rule
  • 如果一个对象是<type 'type'>的实例, 那它就是类对象. 否则是非类对象.

回头看,可以证明这个规则对我们所碰到的所有对象都是成立的, 包括 <type 'type'>,它是自己的实例.

总结一下:

  1. <type 'object'> 是 <type 'type'>的一个实例.

  2. <type 'object'> 不是任何类的子类.

  3. <type 'type'> 是自己的实例.

  4. <type 'type'> 是 <type 'object'>的子类.

  5. python中只有两种对象: 为了清楚叫它们types 和 non-types. Non-types 也可以叫实例, 但实例这个术语也可以指向一个类对象, 因为一个type一定是另一个的type的实例. Types 也被叫做 classes, 并且我经常把他们叫做classes.

更多内置类型

Python中不止这两个对象,这两个基本对象有一堆的兄弟

Figure 2.2. 一些内置类型

http://www.cafepy.com/article/python_types_and_objects/images/builtin_types.png

上面展示了一些内置对象,下面解释一下.

Example 2.3. 查看一些内置类型

>>> list 1
<type 'list'>
>>> list.__class__  2
<type 'type'>
>>> list.__bases__  3
(<type 'object'>,)
>>> tuple.__class__, tuple.__bases__ 4
(<type 'type'>, (<type 'object'>,))
>>> dict.__class__, dict.__bases__ 5
(<type 'type'>, (<type 'object'>,))
>>>
>>> mylist = [1,2,3] 6
>>> mylist.__class__ 7
<type 'list'>

当创建一个tuple或dictionary对象时, 他们是各自对应类型的实例.

怎样创建一个mylist的实例呢? 不能. 因为 mylist对象不是一个type.

子类化产生新对象

内置对象是内置在python中的. 当我们启动python的时候就存在,通常程序结束的时候他们还存在.那我们怎样创建新对象呢?

新对象不会无中生有的出现. 它们必须通过现有的对象来创建.

Example 2.4. 通过子类化构建对象

# Python 2.x:
class C(object): 1
    pass

# Python 3.x, 确定的object基类已经不需要指定了, 默认创建的都是class的基类:
class C: 2
    pass

class D(object):
    pass

class E(C, D): 3
    pass

class MyList(list): 4
    pass 

从上面的例子可知, C.bases 包含 <type 'object'>, 而 MyList.bases 包含 <type 'list'>.

实例化产生新对象

子类化只是产生对象的一种方式,还有另外一种.

Example 2.5. 通过实例化构建对象

obj = object() 1

cobj = C() 2

mylist = [1,2,3] 3

有了上面练习的这些, 我们的对象图就看起来更完美了.

Figure 2.3. 用户定义对象

http://www.cafepy.com/article/python_types_and_objects/images/user_built_objects.png

注意,仅仅通过子类化 <type 'object'>, 类型C 自动成为 <type 'type'>的实例. 可以通过查看C.class来确认这一点. 下节再对此做解释.

其实都是实例化的

对于这一点你可能会冒出下面这些问题,也可能没有,不过我还是打算解答一下:

想一下就会知道多数iqngk下, <type 'object'>的任何子类 (以及子类的子类,等等)都将被赋予<type 'type'>这个类型.

高级材料展望
  • 一些更高级的讨论, 请仔细阅读, 或者跳到下一节.
  • Advanced discussion ahead, tread with caution, or jump straight to the next section.
  • Q: 我可以指定一个type来替代系统默认的机制吗?
  • A: 是的,一个可选方案就是用 metaclass 这个类的属性,如下示例:

  • Example 2.6. 使用class关键字定义类时指定一个type对象

       1 class MyCWithSpecialType(object):
       2     __metaclass__ = SpecialType
    

    3. 总结一下

    Python对象图

    最后一章,我们最终得到一个有各种不同python对象的图

    Figure 3.1. Python对象图

    http://www.cafepy.com/article/python_types_and_objects/images/types_map.png

    这一节我们也将解释图中灰色的竖线是干啥的. 根据人们对不同对象的叫法,灰色竖线将对象图分成三个区域 - 元类,类和实例.

    观察这个图可以得出一些学究式的结论
    1. 虚线穿过了区域边界 (比如,从对象到元对象). 唯一的特例是<type 'type'> (也只能这样了, 否则就需要在它的左边再分一个另一个区域,另一个区域左边还得一个区域等等无穷尽。因为type的type就是自己啊).

    2. 实线不穿过边界. 又有一个例外, <type 'type'> -> <type 'object'> .

    3. 最右边的区域不能有实线. 它们是具体的对象,不能子类化.
    4. 虚线的箭头不能指向最右边区域. 具体的对象不能再实例化.
    5. 左边两个区域包含的是 types. 最右边区域包含的是non-types.
    6. 如果通过子类化<type 'type'>创建一个对象,它应该放在最左边的区域。并且它既是<type 'type'>的实例也是<type 'type'>的子类。

    Also note that <type 'type'> is indeed a type of all types, and <type 'object'> a superclass of all types (except itself).

    概要

    总结一下谈到过的内容:

    好多内置类型还有个名字

    Example 3.1. 更多内置 types

    >>> import types  1
    
    >>> types.ListType is list 2
    True
    >>> def f(): 3
    ...     pass
    ...
    >>> f.__class__ is types.FunctionType 4
    True
    >>>
    >>> class MyList(list): 5
    ...     pass
    ...
    >>> class MyFunction(types.FunctionType): 6
    ...     pass
    ...
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    TypeError: type 'function' is not an acceptable base type
    >>> dir(types) 7
    ['BooleanType', 'DictProxyType', 'DictType', ..]

    这些有什么意义

    我们可以用我们选择的对象间的关系来创建新对象,但这对我们有什么用?

    python中的属性访问机制在本系列的第二本书中讲解:Python Attributes and Methods.

    经典类型Classic Classes

    这是一份关于python中的classic classes 的备忘录. 用一个空的class关键字可以创建老式(早于python2.2)的类.

    >>> class ClassicClass: 1
    ...     pass
    ...
    >>> type(ClassicClass) 2
    <type 'classobj'>
    >>> import types
    >>> types.ClassType is type(ClassicClass) 3
    True
    >>> types.ClassType.__class__ 4
    <type 'type'>
    >>> types.ClassType.__bases__ 5
    (<type 'object'>,)

    types.ClassType这个对象某种程度上可替代<type 'type'>. 该对象的实例(即老式类) 该对象的实例 (老式类) 也是类对象types. 新老式对象之间的属性访问规则是不同的。 types.ClassType 这个对象的存在时为了向后兼容,在将来版本的python中可能就没有了. 本书的其他部分所讲的内容不应被套用到老式类上去。去。

    请在这里评论: discussion page. 感谢反馈!

    到处结束了,朋友们!

    4. 可能在其他地方学过的

    面向对象

    可以略过的章节?Can Skim Section

    用古怪的一节来讲解 类型-实例 和 超类-子类关系

    我们引入很多不同的对象,他们之间只用两种关系就可以概括 ( 4.1. 关系)

          术语: *的子类, *的超类 and 超类-子类.

          术语: *的实例, *的类型, 类型-实例 或 类-实例. 

    注意在简单英语中术语,以上两种关系都可以叫'是一个' . , 蛇是一个爬虫. 不过我们用特定的术语来避免混淆.

    Figure 4.1. 关系

    http://www.cafepy.com/article/python_types_and_objects/images/relationships.png

    换句话说, 虚线箭头一端可以向上移到实线箭头,虚线箭尾可以向下移 ( 如Figure 4.2. ""关系的传递" 中的2a和2b各自表示的).这些性质可以直接从父类-子类的关系定义得到

    Figure 4.2. 关系的传递

    http://www.cafepy.com/article/python_types_and_objects/images/relationships_transitivity.png

    应用 虚线向上规则, 可以从下面第一条描述得到第二条:

    1. Squasher 是蛇的实例 (Squasher的类型是蛇).
    2. Squasher 是爬虫的实例 (Squasher的类型是爬虫).

    之前我们说过每个对象有一个确切的类型.为啥 Squasher有两个? 注意虽然每个说法都是正确的, 其中一个更确切 (确切的说可以归纳另一个).也可以说:

    父类-子类关系也有个类似的规则.

    合并实线规则
    • 若A是B的子类, B是C的子类, 则A也是C的子类

    蛇是一种爬虫, 爬虫是一种动物. 所有蛇也是一种动物. 或者用Python代码表示:

    注意对象可能有不止一个基类.

    相关资料

    版权信息

    This book was written in DocBook XML. The HTML version was produced using DocBook XSL stylesheets and xsltproc. The PDF version was produced using htmldoc. The diagrams were drawn using OmniGraffe [1]. The process was automated using Paver [2].

    Feedback

    请点击这里评论: discussion page 期待反馈!


    反馈

    创建 by -- ZoomQuiet [2010-01-28 16:19:11]