对象(object)

基本上可以看作数据以及由一系列可以存取、操作这些数据的方法所组成的集合
具有以下优点——

  1. 多态(Polymorphism):可以对不同类的对象使用同样的操作
  2. 封装(Encapsulation):对外部世界隐藏对象的工作细节
  3. 继承(Inheritance):以通用的类为基础建立专门的类对象

多态(Polymorphism)

不知道变量所引用的对象类型是什么,还能对它进行操作,因为它会根据对象(或类)的类型的不同而采取不同的行为

In [2]:
from random import choice    #choice会从参数(序列)中随机取出一个元素并返回
In [3]:
x = choice( [ "hello, world", [1, 2, 'e','e', 4] ] )
#序列里既有字符串又有列表,那么x是字符串还是列表呢?程序会自行判断
In [4]:
x
Out[4]:
'hello, world'

封装(Encapsulation)

向程序的其他部分隐藏对象的具体实现细节
抽象地处理程序组件而不关注实现细节
多态和封装的概念区分—— 多态针对的是对象的类
封装针对的是对象的构建细节

继承(Inheritance)

A类从B类继承方法,在A类上调用方法时,程序会自动从B类调用相应的方法

类和类型

所有属于同一个类的对象,成为类的实例(instance)
大类以下细分为若干小类,小类称为大类的子类,大类成为小类的超类
python中习惯用首字母大写的单词描述一个类
方法:(更专业的叫法是“绑定方法”)与函数不同,它是绑定到某个特定的类上的

创建类

In [6]:
#新式类的语法中,在模块或脚本的开头加入以下赋值语句
__metaclass__ = type
In [7]:
class Person:     #用class语句创建类,Person即是类名
    def setName(self, name):        #类的方法定义跟函数定义形式一致,而且这里的self参数表示的是属于类的实例自身,接受实参时实际上是不包括self的
        self.name = name        #设置对象的name属性
    def getName(self):
        return self.name
    def greet(self):
        print "Hello, world! I'm %s" % self.name
#方法是类内的函数,第一个参数总为self
In [8]:
foo = Person()          #foo是Person类的一个实例
In [9]:
foo.setName("Zk")
In [10]:
foo.getName()
Out[10]:
'Zk'
In [12]:
foo.greet()
Hello, world! I'm Zk

In [13]:
#以上语句也能写成“类.方法(参数)”的形式
#这里的参数self需要指定为实例foo
Person.greet(foo)
Hello, world! I'm Zk

In [16]:
#用变量引用绑定方法
foogreet = foo.greet     #注意此处不能有括号
In [17]:
foogreet()
Hello, world! I'm Zk

私有化

在其他一些语言中,能够将对象的方法和特性变为私有,从外部无法访问
但是python不存在私有化功能
不过却有一种“名字变换术”

In [27]:
class Secretive:
    def __inaccessible(self):       #方法名前加上双下划线,外部将不能访问这个方法
        print "Bet you can't see me..."
    def accessible(self):
        print "The secret message is: "
        self.__inaccessible()          #从内部却可以访问
In [28]:
s = Secretive()
In [29]:
s.__inaccessiable()         #外部不能访问
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-29-7b7e334938e7> in <module>()
----> 1 s.__inaccessiable()         #外部不能访问

AttributeError: 'Secretive' object has no attribute '__inaccessiable'
In [30]:
s.accessible()        #内部可以访问
The secret message is: 
Bet you can't see me...

In [31]:
#事实上,以双下划线为前缀的方法名在内部会自动翻译为   (单下划线_)+“类名”+“带双下划线的方法名”
#也就是说,我们依旧能够访问这个方法
s._Secretive__inaccessible()
Bet you can't see me...

如果不需要使用这种方法,又不想让其他对象访问内部数据,可以使用单下划线
毕竟“from module import *”语句是不会导入以单下划线为前缀的名字的

类命名空间

In [32]:
class MemberCounter():
    members = 0           #为类定义一个变量
    def init(self):
        MemberCounter.members += 1
In [34]:
m1 = MemberCounter()
In [35]:
m1.init()
In [36]:
MemberCounter.members        #通过“类.变量”的形式就可以从外部访问类内部的变量
Out[36]:
1
In [37]:
m2 = MemberCounter()
In [38]:
m2.init()
In [39]:
MemberCounter.members
Out[39]:
2
In [40]:
#也可以通过实例访问这个变量
m1.members
Out[40]:
2
In [41]:
#如果我们给对象设置一个同名变量呢?
m1.members = 'Two'
In [42]:
m1.members           #对象m1内的变量屏蔽了类的变量
Out[42]:
'Two'
In [43]:
m2.members           #对象m2内并没有同名变量
Out[43]:
2
In [44]:
MemberCounter.members
Out[44]:
2
In [45]:
#对象命名空间是类命名空间的子空间
#对象命名空间内的同名变量会屏蔽掉类命名空间的变量
#“对象内变量  和  类内变量” 的关系类似于“局部变量  和  全局变量”的关系

指定超类

In [46]:
class Filter:
    def init(self):
        self.blocked = []
    def filter(self, sequence):
        return [x for x in sequence if x not in self.blocked]
In [47]:
class SPAMFilter(Filter):         #SPAMFliter是Filter的子类
    def init(self):      #重写init方法
        self.blocked = ['SPAM']
In [52]:
#此处Filter是序列过滤的通用类
#SPAMFilter是这个通用类下的一种情况,一个子类,它继承了超类的方法,但是我们重写了init方法
In [49]:
s = SPAMFilter()
In [50]:
s.init()
In [51]:
s.filter(['SPAM', 'SPAM', 'SPAM', 'SPAM', 'eggs', 'bacon', 'SPAM'])
Out[51]:
['eggs', 'bacon']

检查继承

issubclass函数:查看一个类是不是另一个类的子类
isinstance函数:查看一个对象是不是一个类的实例(应少用)
__bases__特性:查看类的超类(们)
__class__特性:查看对象的类

In [54]:
issubclass(SPAMFilter, Filter)       #第一个参数是子类,第二个参数是超类,则返回True
Out[54]:
True
In [55]:
issubclass(Filter, SPAMFilter)
Out[55]:
False
In [57]:
SPAMFilter.__bases__
Out[57]:
(__main__.Filter,)
In [58]:
Filter.__bases__
Out[58]:
(object,)
In [60]:
isinstance(s, SPAMFilter)       #第一个参数是对象,第二个参数是类,返回布尔值
Out[60]:
True
In [61]:
s.__class__
Out[61]:
__main__.SPAMFilter
In [62]:
#新式类的对象还可以使用type函数
type(s)
Out[62]:
__main__.SPAMFilter

多重继承(multiple inheritance)

如非特别熟悉,否则应该尽量避免多重继承

  • 一个类可以同时属于多个超类,同时继承多个超类的特性和方法
    此时如果有重复的特性名或方法名,先列出(在左边)的超类有效
    其实就是参数从右到左传入的原理,右边的超类先传入,左边的超类就会重写重复的特性或者方法
  • 多个超类又共享同一个超类
    此时会有一个查找顺序,称为MRO(Method Resolution Order,方法判定顺序)
    其算法相当复杂,但是能够确保工作顺利进行

接口(或“协议”)

即公开的方法和特性

In [64]:
#hasattr函数:检查方法是否存在,返回布尔值
hasattr(SPAMFilter, 'filter')      #第一个参数为类名或对象名,第二个参数为特性名或方法名(必须为字符串)
Out[64]:
True
In [70]:
#getattr函数:尝试访问特性,如果存在,返回该特性
getattr(s, 'filter', None)    #第一个参数为类名或对象名,第二个参数为特性名或方法名(必须为字符串),第三个参数为默认值
Out[70]:
<bound method SPAMFilter.filter of <__main__.SPAMFilter object at 0x7f48bc502250>>
In [71]:
#如果不存在,返回指定的默认值
getattr(s, 'aaa', 'N/A')     
Out[71]:
'N/A'
In [72]:
#如果没有设置默认值,程序出错
getattr(s, 'aaa')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-72-0fc80992df7a> in <module>()
      1 #如果没有设置默认值,程序出错
----> 2 getattr(s, 'aaa')

AttributeError: 'SPAMFilter' object has no attribute 'aaa'
In [73]:
#getattr函数可以配合callable函数,判断方法能否被调用
callable( getattr(s,'filter', None) )
Out[73]:
True
In [74]:
callable( getattr(s, 'aaa', None) )
Out[74]:
False
In [79]:
#但是callable在python3中已经不能使用
#我们可以检查__call__来代替
hasattr(getattr(s, 'filter', None) , '__call__')
Out[79]:
True
In [80]:
hasattr(pow, '__call__')     #也可以是函数
Out[80]:
True
In [81]:
#setattr函数:为对象设置特性
setattr(s, 'name', 'Zk')      #第一个参数是对象,第二个参数是特性名,第三个参数是特性值
In [82]:
s.name
Out[82]:
'Zk'
In [83]:
#可以通过访问__dict__特性查看对象所有存储值
s.__dict__
Out[83]:
{'blocked': ['SPAM'], 'name': 'Zk'}

面向对象程序设计

简单要点

  • 将属于一类的对象放在一起
    比如,一个函数操作一个全局变量,那么这个函数和这个变量最好在同一个类内作为方法或者特性出现
  • 不要让对象过于亲密
    实例只关心自己的状态
  • 小心继承,尤其是多重继承
  • 简单、易懂、小巧

    设计草图

  • 写出功能描述,关注名词,动词,形容词
  • 名词==>类
  • 动词==>方法
  • 形容词==>特性
  • 将方法和特性分配到类
In []: