Python面向对象开发基础篇

   Limodou@gmail.com
       2013/04/04
编程范式
• 过程:模式、数据结构、过程
• 对象:封装了状态和方法,在对象间通过
  传递消息
• 函数:函数、闭包、递归等
• Python是多范式的编程语言
什么是面向对象
抽象
         基于分类的思考方法
                  Window



       Dialog                      Widget



     MessageBox            Panel            Button

                                                     具体
定义了功能模板,是一
                  类事物的集合,是抽象
                      概念
          Class


derive,            subclass
inherit
          class
类的组成
             Window      名称


               width
              height
            background   属性
• public
• private      color
• protect      font
              draw()
              fresh()
             destroy()   方法
              move()
              resize()
instance = class()

 一个类可以创建多个实例,每个实例可
     以拥有不同的属性值




class 杯子

玻璃杯 = 杯子(材料=玻璃)
红色金属杯 = 杯子(材料=金属,颜色=红
色)
面向对象的三大特点


       多态


  继承

        封装
封装         数据+方法


继承        子类可以使用父类
           的属性和方法
     覆盖   改变父类的属性值和方法实现




多态        同一个父类可以派
           生出不同的子类
为什么需要面向对象

     封装


重用        分解
什么时候使用面向对象



在需要的时候使用
Python的面向对象
import os
class Command(object):
       def __init__(self, name):
              self.name = name
       def __call__(self, *args, **kwargs):
              raise NotImplementedError

class DirCommand(Command):
   def __call__(self, dir):
        os.system('dir %s' % dir)

dir_cmd = DirCommand('dir')
dir_cmd('.')
• 使用class来定义类
• Python的类可以从父类继承,支持多个父类
• 类中可以保存属性和方法
• 属性可以是descriptor,通常的数据类型
• 特殊的方法,如:私有方法(开始有两个
  下划线),特殊方法(前后各有两个下划
  线)
• __init__ 为初始化方法,一般不讲构造方
  法,不是必须的
• obj = Class(*args, **kwargs) =>
  obj = Class().__init__(*args, **kwargs)
• obj.attr 可以访问类中定义的属性
• obj.func()可以调用类中定义的方法
• 属性可以在运行时动态绑定
一切皆对象
  --数据及功能的结合
体
建议使用类和实例来描述,以避
  免与对象的说法冲突。
old style class和new style class
>>> class A:pass
>>> type(A)
<type ‘classobj’>
>>> type(int)
<type ‘type’>
>>> class A1(object):pass
>>> type(A1)
<type ‘type’>
new style class的作用
•   在Python 2.2之前,类与类型是不统一的
•   在Python 2.2实现了统一
•   让内置的类型子类化更方便
•   提供了属性(property)的机制
•   实现了static和class方法
•   提供了metaclass编程
Unifying types and classes in Python 2.2
PEP-252: Making types look more like classes
PEP-253: Subtyping built-in types
类
class A(object):pass
class B(A):
         “””B description”””
         def out_b(self, name):
                 print name
class C(A):
         def out_c(self, name):
                 print name
class D(B, C):pass


>>> d = D()
>>> d.out_b('hello')
hello
• 一个类可以有一个或多个基类,多个基类
  之间使用’,’分隔
• 类下可以定义doc_string
• 如果不存在基类,则为一个old-style类
• 只有当所有基类都是old-style类时,子类才
  是old-style类,否则就是new-style类
• 如果不需要基类,则可以把object作为基类
  来创建new-style类
• 在3.x将不存在old-style类,所有的类都是
  new-style,所以可以省略object
• 在类上定义的属性将在所有实例间共享

class C(object):
      count = 0
c1 = C()
c2 = C()
C.count += 1
print c1.count, c2.count
1, 1
• 在方法中通过 类 变量来使用类属性
• 属性可以动态绑定到类或实例变量上

class C(object):
      x = 20
      def call(self):
            C.x += 2
            print C.x
            print self.__class__
C.y = 21
类的自省
• isinstance() 判断一个实例是否属性某个类
  >>> isinstance(c1, C)
  True
• issubclass() 判断一个类是否是另一个类的子
  类
  >>> issubclass(D, D)
  True
  >>> issubclass(D, C)
  True
• dir(D) 可以列出类的属性名,包括方法
• D.__name__ 可以得到类的名称
  >>> D.__name__
  ‘D’
• D.__module__ 得到类所在的模块
• D.__bases__ 得到所有基类
  >>> D.__bases__
  (<class '__main__.B'>, <class '__main__.C'>)
如何判断一个对象是类
考虑使用types,它定义了许多类型,但是:

>>> import types                     >>> import types
>>> class A:pass                     >>> class B(object):pass
>>> isinstance(A, types.ClassType)   >>> isinstance(B, types.ClassType)
True                                 False
>>> isinstance(A, types.TypeType)    >>> isinstance(B, types.TypeType)
False                                True
>>> import inspect
>>> inspect.isclass(A)
True
>>> inspect.isclass(B)
True
类私有属性
• 以两个下划线开始的属性,python会把它编译
  为:_classname__attribute
• 其实python中不存在真正的私有属性
• 更多是采用约定的方式,如以’_’开始的
>>> class C5(object):
... __private = 23
>>> print C5.__private
AttributeError: class A has no attribute ' private'
>>> print C5. _C5__private
23
static和class方法
>>> class A(object):
... @staticmethod
... def static():
...    print A.__name__
... @classmethod
... def method(cls):
...    print cls.__name__
>>> a = A()
>>> a.static()
‘A’
>>> A.static()
‘A’
>>> a.method()
‘A’
>>> A.method()
‘A’
• 静态方法:类相当于只是起到一个作用域
  限制的作用,可以通过实例,类来访问
• 类方法:第一个参数是类对象,可以通过
  实例,类来访问。与实例无关的方法可以
  定义为类方法。
• 实例方法:第一个参数是实例对象,只能
  通过实例来访问。
为什么实例的第一个参数是self
•   约定俗成
•   类方法第一个参数是 cls
•   实例方法第一个参数是 self
•   用其它的名字也可以
创建实例
• 创建一个实例,只要象写调用一        >>> d = D()
  个函数一样调用类对象。
                        >>> isinstance(d, D)
                        True
• 如果类定义了__init__方法,则创   >>> class E(object):
  建实例时会把参数传给它来执         ... def __init__ (self, n):
  行。
                        ... self.x = n
                        >>> e = E(42)
                        >>> print e.x
                        42
                        >>> e.z = 8
• 可以动态向实例绑定新的属性         >>> print e.z
                        8
动态处理属性
• getattr(obj, name, default=None)
• hasattr(obj, name)
• setattr(obj, name, value)
问题?Python如何获得一个属性
class A(object):
      name = ‘A’
      def __init__(self, name):
            self.name = name
a = A(‘Hello’)

a.name, self.name, A.name的结果分别是什么?
MRO(Method Resolution Order)
•   先查找实例的属性(self.__dict__)
•   再在实例的类中查找(self.__class__.__dict__)
•   再到基类中查找
•   如果存在多个基类,根据mro的顺序

>>> D.mro()
[<class '__main__.D'>, <class
'__main__.B'>, <class '__main__.C'>, <class
'__main__.A'>, <type 'object'>]
实例的自省
• isinstance() 判断一个实例是否属性某个类
  >>> isinstance(c1, C)
  True
• self.__class__ 得到某实例的类对象
关于覆盖
• 在Python的类中,不允许存在多个同名的方
  法,所以无法实现象java那样的方法重定义
• 只能通过可变参数(*args, **kwargs)或不同
  的名字来实现
如何调用父类的方法
class A(object):
   def __init__(self):
     print 'A'
class B(A):
   def __init__(self):
     super(B, self).__init__()
     print 'B'

b = B()
特殊类方法
• 如果方法名前后各有两个下划线,一般为
  特殊的类方法。
• 特殊的类方法有一些是系统已经定义好的,
  有特殊的命名要求
• 特殊的类可以实现特殊的语法效果
__init__
• 实现一个实例的初始化
__str__, __repr__, __unicode__
• 可让一个对象通过print来打印
• print一个对象时,一般先找__str__,再找
  __repr__。
• 如果在运算中存在unicode处理,会尝试调
  用__unicode__来进行转換
__call__
• 可以用来模拟函数调用

class A(object):
      def __call__(self, name)
            print name
a = A()
print a(‘hello’)
__lt__, __le__, __eq__, __ne__, __
           gt__, __ge__

• 用来模拟数学比较
__getattr__, __setattr_, __delattr__

• 用来摸拟属性操作
__getitem__, __setitem__, __delitem_
                 _
• 用来模拟字典操作
更多关于特殊方法的说明
• http://docs.python.org/2/reference/datamod
  el.html#special-method-names
什么时候使用面向对象
• 简化代码,实现封装
• 当遇到可以进行抽象的场合,如:GUI控件
  开发
• 在实现可扩展机制的时候,如:配置文件
  读取,实现不同的配置文件格式的解析
• 以类的定义形式来实现某种简化的定义,
  如:ORM, Form等
• 利用类的特殊方法来实现类函数的闭包调
  用
什么时候不使用面向对象
•   简单情况
•   过程化编程,如:算法
•   不适合的地方
•   。。。
更高级的内容?
• descriptor
• property
• metaclass

Python面向对象开发基础篇