Rendering of reStructured text is not possible, please install Docutils.
:status: 草稿|校对|正式;HuangYi;完成度60%;

.. contents::
  :depth: 3

========
类与实例
========

通常大家说的是"类与对象",不过 python 中万物皆"对象",对象这个词语的含义太宽泛。
实际上我们这里要讨论的只是用户自定义的类和这些类的实例而已,类及其实例都只是对象这个大家族中的一员。

定义类
------

::
  
  >>> class Klass(object):
  ...     ' docstring... '
  ...     def __init__(self, a):
  ...         self.attr_a = a
  ...
  >>> Klass.__doc__
  ' docstring... '
  >>> obj = Klass(1)
  >>> obj.attr_a
  1

关键字 ``class`` 定义一个类, ``Klass`` 是类的名字,括号中 ``object`` 是它的基类,
python 中所有的 ``class`` 都是从 ``object`` 继承而来
(虽然在目前的 python 版本中还存在一类所谓的 old-style class ,他们是不从 ``object`` 继承而来的,不过这只是为了保持向后兼容性并且很快就要在 python3.0 中彻底退出历史舞台,所以我们这里直接忽略它了)。

和许多语言不同的是,python 中构造函数叫做 ``__init__`` ,第一个参数传递的就是将要初始化的实例对象本身,类似许多语言中的 ``this`` 。

和函数一样,class 也可以定义文档字符串,同样是通过 ``__doc__`` 访问。

创建实例对象并不需要学习新的语法,创建实例和函数调用一摸一样。传给 ``Klass`` 的参数最终传给构造函数 ``__init__`` ,而返回值便是新创建的实例对象。

属性
----

在构造函数中的 ``self.attr_a = a`` 便给 ``self`` 这个对象增加了一个名为 ``attr_a`` 的属性,其绑定的对象就是传入的参数 ``a`` 所绑定的对象。

对象的 ``__dict__`` 属性可以让你以字典的方式查看对象的所有属性:::

  >>> obj.__dict__
  {'attr_a': 1}

不光在构造函数中,其实在任何时候你都可以给对象增加属性,你只需要给不存在的属性绑定对象即可,python 会自动创建不存在的属性:::

  >>> obj.attr_b = 1
  >>> obj.attr_b
  1
  >>> obj.__dict__
  {'attr_b': 1, 'attr_a': 1}

讲到属性,需要明确的一个概念就是不光实例对象有属性,类也是对象,类自然也有属性!::

  >>> class Klass1(object):
  ...     ' docstrign... '
  ...     class_attr1 = 'hello'
  ...     def __init__(self, a):
  ...         self.attr_a = a
  ...
  >>> Klass1.class_attr1
  'hello'

我们也可以使用 ``__dict__`` 来查看类对象的所有属性:

  >>> from pprint import pprint
  >>> pprint(dict(Klass1.__dict__))
  {'__dict__': <attribute '__dict__' of 'Klass1' objects>,
   '__doc__': ' docstrign... ',
   '__init__': <function __init__ at 0x00FBAEF0>,
   '__module__': '__main__',
   '__weakref__': <attribute '__weakref__' of 'Klass1' objects>,
   'class_attr1': 'hello'}

class 的属性比较多,为了方便查看我们使用了 ``pprint`` 模块中的 ``pprint`` 方法,其功能是以更可读的方式输出一些复杂数据结构,
具体用法可以查看 python 自带手册。

当访问实例对象的属性时,如果属性不存在,将自动查找其对应的类对象的属性:::

  >>> obj1 = Klass1(1)
  >>> obj1.class_attr1
  'hello'

类属性是和类对象的属性,同一个类对象的所有实例都共享同一份类属性:::

  >>> obj2 = Klass1(2)
  >>> Klass1.class_attr1 = 'changed'
  >>> obj1.class_attr1
  'changed'
  >>> obj2.class_attr1
  'changed'

方法
----

::

  >>> class Klass(object):
  ...     def __init__(self, name):
  ...         self.name = name
  ...     def greet_to(self, name):
  ...         print self.name, 'say hello to', name
  ...
  >>> obj = Klass('HuangYi')
  >>> obj.greet_to('you')
  HuangYi say hello to you
  >>> pprint(dict(Klass.__dict__))
  {'__dict__': <attribute '__dict__' of 'Klass' objects>,
   '__doc__': None,
   '__init__': <function __init__ at 0x010122B0>,
   '__module__': '__main__',
   '__weakref__': <attribute '__weakref__' of 'Klass' objects>,
   'greet_to': <function greet_to at 0x010122F0>}

方法其实也是属性!准确地说还是属于类对象的属性。在 ``Klass`` 中我们定义了两个方法: 构造函数 ``__init__`` 和普通方法 ``greet_to`` 。

方法本质上说其实是对函数的一层简单包装,这层包装的作用就是将调用方法的这个对象当做第一个参数传进去,
所以在定义方法的时候千万别忘了声明第一个参数,按照约定,这个参数我们叫它 ``self`` 。::

  >>> obj.greet_to
  <bound method Klass.greet_to of <__main__.Klass object at 0x0101C550>>
  >>> Klass.__dict__['greet_to']
  <function greet_to at 0x010122F0>

直接通过实例对象 ``obj`` 访问属性 ``greet_to`` 时得到的其实是对这个函数包装后产生的"方法"。
直接通过 ``Klass.__dict__`` 取 ``greet_to`` 属性实际绑定的对象时,你得到的才是这个还没有经过包装的"纯净"的函数!

  >>> func = Klass.__dict__['greet_to']
  >>> func(obj, 'you')
  HuangYi say hello to you

如果你要问这个包装是何时以及如何产生的,这其实又涉及到 python 中另一个相对高级的概念: Descriptors。
如果有兴趣可以查看 Raymond 写的精彩教程(http://users.rcn.com/python/download/Descriptor.htm),本书将不做更多介绍。

不过当我们认识到方法与函数的这一层关系后,我们已经可以利用它来实现一些有意思的功能了,比如在运行时给类添加方法:::

  >>> class Klass(object):
  ...     pass
  ...
  >>> obj = Klass()
  >>> def print_id(obj):
  ...     print id(obj)
  ...
  >>> Klass.print_id = print_id # 等价于 Klass.__dict__['print_id'] = print_id
  >>> obj.print_id() # 等价于 print_id(obj) 
  16892848

特殊属性
--------

所谓特殊属性就是那些名字前后都是两个下划线的属性。这种属性往往都会被 python 特殊处理,用来实现一些特定功能用的。
所以定义你自己的属性时千万记得不要定义这种"风格"的属性。

构造析构函数
============

构造函数我们前面已经见识过了 ``__init__`` 便是,而析构函数叫做 ``__del__`` 。

TODO 介绍析构函数

操作符重载
==========

TODO

自定义属性访问
==============

TODO

继承
----

小结
-----


练习
-----

.. macro:: [[PageComment2(nosmiley=1, notify=1)]]


.. macro:: -- HuangYi [[[DateTime(2007-06-26T05:39:25Z)]]]

ObpLovelyPython/LpyQLearn-5-object (last edited 2009-12-25 07:18:49 by localhost)