python面向对象开发基础篇
TRANSCRIPT
编程范式
• 过程:模式、数据结构、过程
• 对象:封装了状态和方法,在对象间通过
传递消息
• 函数:函数、闭包、递归等
• Python是多范式的编程语言
什么是面向对象
基于分类的思考方法
Window
MessageBox
Dialog Widget
Panel Button
抽象
具体
Class
class
subclassderive, inherit
定义了功能模板,是一
类事物的集合,是抽象
概念
Window
widthheight
backgroundcolorfont
draw()fresh()
destroy()move()resize()
名称
属性
方法
类的组成
• public
• private
• protect
instance = class()
一个类可以创建多个实例,每个实例可
以拥有不同的属性值
class 杯子
玻璃杯 = 杯子(材料=玻璃)红色金属杯 = 杯子(材料=金属,颜色=红
色)
面向对象的三大特点
封装
继承
多态
封装
继承
多态
数据+方法
子类可以使用父类
的属性和方法
同一个父类可以派
生出不同的子类
覆盖 改变父类的属性值和方法实现
为什么需要面向对象
封装
重用 分解
什么时候使用面向对象
在需要的时候使用
Python的面向对象
import osclass 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.2PEP-252: Making types look more like classesPEP-253: Subtyping built-in types
类
class A(object):passclass B(A):
“””B description”””def out_b(self, name):
print nameclass 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 += 1print 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
>>> class A:pass
>>> isinstance(A, types.ClassType)
True
>>> isinstance(A, types.TypeType)
False
>>> import types
>>> class B(object):pass
>>> isinstance(B, types.ClassType)
False
>>> isinstance(B, types.TypeType)
True
>>> import inspect
>>> inspect.isclass(A)
True
>>> inspect.isclass(B)
True
类私有属性
• 以两个下划线开始的属性,python会把它编译为:_classname__attribute
• 其实python中不存在真正的私有属性• 更多是采用约定的方式,如以’_’开始的
>>> class C5(object):... __private = 23>>> print C5.__privateAttributeError: class A has no attribute ' private'>>> print C5. _C5__private23
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>>> class E(object):... def __init__ (self, n):... self.x = n>>> e = E(42)>>> print e.x42>>> e.z = 8>>> print e.z8
• 创建一个实例,只要象写调用一
个函数一样调用类对象。
• 如果类定义了__init__方法,则创
建实例时会把参数传给它来执
行。
• 可以动态向实例绑定新的属性
创建实例
动态处理属性
• 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/datamodel.html#special-method-names
什么时候使用面向对象
• 简化代码,实现封装
• 当遇到可以进行抽象的场合,如:GUI控件开发
• 在实现可扩展机制的时候,如:配置文件读取,实现不同的配置文件格式的解析
• 以类的定义形式来实现某种简化的定义,如:ORM, Form等
• 利用类的特殊方法来实现类函数的闭包调用
什么时候不使用面向对象
• 简单情况
• 过程化编程,如:算法
• 不适合的地方
• 。。。
更高级的内容?
• descriptor
• property
• metaclass