1 构造和初始化对象
__init__
方法是Python内建众多魔法方法(什么是魔法方法?)中最常见的一个,通过这个方法我们可以定义一个对象的初始操作。当构造函数被调用的时候的任何参数都会传递给__init__
方法,然后该方法根据这些参数进行对象的初始化工作:
# -- coding: utf-8 --
class Employee(object):
def __init__(self,name):
self.name = name e = Employee('Sunshine') print e.name # Sunshine
与__init__
方法对应的是__del__
析构方法,在对象被垃圾回收前调用,除了在进行套接字、文件IO这些非托管资源操作外,一般情况下很少会用到它。
1.1 正确的初始化子类型
重写是继承机制中一个重要的内容,对于构造方法尤其如此。大多数类型的子类既要初始化自己的部分,也要调用基类的构造方法,因为保证了对象被正确的初始化,如下所示:
# -- coding: utf-8 --
class Bird(object):
def __init__(self):
self.hungry = True def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No. thanks' class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk!' def sing(self):
print self.sound # AttributeError: 'SongBird' object has no attribute 'hungry'
sb = SongBird()
sb.eat()
SongBird
这个子类在调用继承于父类Bird
的eat()
方法时由于其自身的构造函数没有正确的初始化基类的成员hungry
所有就会抛出AttributeError
的异常。正确初始化父类成员的方法有两个:
1.直接调用基类构造方法。这样修改SongBird
类型的定义即可:
class SongBird(Bird):
def __init__(self):
Bird.__init__(self) # 调用基类构造方法
self.sound = 'Squawk!' def sing(self):
print self.sound
2.调用super
函数,可以这样修改SongBird
类型的定义:
class SongBird(Bird):
def __init__(self):
super(SongBird,self).__init__() # 使用super函数
self.sound = 'Squawk!' def sing(self):
print self.sound
一般来讲,使用第一种方式:调用未绑定的基类构造方法,是为了兼容旧的代码,如果不需要兼容旧版本的代码还是推荐使用super
函数来初始化基类的成员。从上面的示例可能觉得二者差别不大,但是为什么还要出现super
函数呢,因为super
函数(其他它是一个类)主要是用来解决多重继承问题的,因为这牵涉到顺序查找(MRO)、重复调用等等一系列的复杂问题。为了保证我们代码的一致性,我们应该同时只使用一种风格来编写代码。
2. 魔法方法
在Python中内建了很多"magic"方法这些方法和其他函数有些细微的不同:
- 从形式上来说名字都是以双下划线(
__
)来开始和结束的(如:__new__
) - 大部分的魔法方法会在特定的情况下被Python自动调用。(比如
__init__
方法会在对象被创建后自动调用)
2.1 通过魔法方法定制类型
使用Python魔法方法最大的优势在于可以创建出自己的拥有和内置类型同样行为的类出来。例如我们可以通过重写下面的魔法方法,来为自己的类型定制自己的比较规则:
-
__eq__(self,other)
—— 定义了等于的行为(=
) -
__ne__(self,other)
—— 定义了不等于的行为(!=
) -
__lt__(self,other)
—— 定义了小于的行为(<
) -
__gt__(self,other)
—— 定义了大于的行为(>
) -
__le__(self,other)
—— 定义了小于等于的行为(<=
) -
__ge__(self,other)
—— 定义了大于等于的行为(>=
)
例如,我们可以重写定义类型的==
等行为,只需要重写__eq__
方法即可:
# -- coding: utf-8 --
class Employee(object):
def __init__(self,name):
self.name = name def __eq__(self,other):
return self.name == other e = Employee('Sun')
print e == 'Sun' # True
Python内置的魔法方法中除了这些用于比较的魔法方法之外,还有关于操作符、类型转换以及序列相关的等等一系列的魔法方法。如果想了解更多,这里有一份很好的文档(注意文档中错别字):Python魔法方法指南
3. 属性
在Python中通过访问器定义的特性被称为属性(property)。有两种定义属性的方式,首先我们来使用property
函数来创建属性:
# --coding:utf-8--
class Employee(object):
def __init__(self):
self.__name = None # set 访问器
def getName(self):
return self.__name # get 访问器
def setName(self,value):
self.__name = value # 析构函数
def delName(self):
del self.__name name = property(getName,setName,delName,'set and get Name property') c = Employee()
c.name = "Bob"
print c.name # output: Bob
另一种方式就是Python 2.6 新增的语法,get
/set
/del
函数都是使用的同一个名字:
# --coding:utf-8--
class Employee(object):
def __init__(self):
self.__name = None def name(self):
return self.__name def name(self,value):
self.__name=value def name(self):
del self.__name c = Employee()
c.name = "jobs"
print c.name # jobs
3.1 控制属性访问
如果需要加强对属性的控制,可以使用Python为我们提供一系列的魔法方法:
-
__getattribute__(self,name)
:当特性name被访问时自动被调用(只能新式类中使用) -
__getattr__(self,name)
:当特性name被访问且对象没有相应的特性时被自动调用 -
__setattr__(self,name)
:给特性name赋值时会被自动调用 -
__delattr__(self,name)
:当删除特性name时被自动调用
我们知道每当属性被赋值的时候,__setattr__()
就会被调用,通过它和__dict__[]
的组合,我们可以通过动态为一个类创建创建特性,如下:
# -- coding: utf-8 --
class Employee(object):
def __init__(self,name):
self.name = name # RuntimeError: maximum recursion depth exceeded
# #每当属性被赋值的时候,__setattr__()会被调用,这样就造成了递归调用。
# def __setattr__(self,name,value):
# self.name = value def __setattr__(self,name,value):
self.__dict__[name] = value e = Employee("sunshine")
print e.name
e.email = "sunshine@gmail.com"
print e.email
如果只能访问特定特性的话其实也很简单,只需要想下面 __setattr__
中加个if
判断即可:
# -- coding: utf-8 --
class AccessCounter(object):
'''一个包含计数器的控制权限的类每当值被改变时计数器会加一''' def __init__(self, val):
super(AccessCounter, self).__setattr__('counter', 0)
super(AccessCounter, self).__setattr__('value', val) def __setattr__(self, name, value):
if name == 'value':
# 通过调用基类的__setattr__方法可以防止递归
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
# 如果不想让其他属性被访问的话,那么可以抛出 AttributeError(name) 异常
super(AccessCounter, self).__setattr__(name, value) def __delattr__(self, name):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
super(AccessCounter, self).__delattr__(name) c = AccessCounter(10)
c.value = 1
c.value = 2
c.value = 3
c.value = 4
c.value = 5
print c.counter # value特性的值改变了5次
4. 定义静态方法和类成员方法
静态方法和类成员方法分别在创建时被装入Staficmethod
类型和Classmethod
类型的对象中。静态方法定义没有任何参数,能够被类本身直接调用。类方法在定义时需要名为cls
的参数(类似于self
参数),且能够被类本身调用。Python 2.4 为我们提供了名为:装饰器(decorators
)的新语法,可以很简单的创建这两种方法:
class MyClass(object): @staticmethod
def smeth():
print 'This is a static method' @classmethod
def cmeth(cls):
print 'This is a class method of',cls MyClass.smeth() # This is a static method
MyClass.cmeth() # This is a class method of <class '__main__.MyClass'>
my = MyClass()
my.cmeth() # This is a class method of <class '__main__.MyClass'>
my.smeth() # This is a static method
5. 协议
协议(protocol
)在Python中的概念和C#中的接口比较类似,协议说明了应该实现何种方法和这些方法应该做什么。比如说,如果想要自定义一个序列,那么只需要遵守序列的协议即可。
5.1 迭代器
迭代器必须遵循迭代器协议,需要有 __iter__
(返回它本身) 和 next。__iter__
方法是迭代器规则(iterator protocol)的基础。__iter__
返回一个具有next
方法的迭代器(iterator
)对象,调用next方法时,迭代器会返回它的下一个值,直到没有值可返回时才会引发一个StopIteration
异常。下面的示例使用迭代器来创建斐波那契数列,如下:
class Fibs(object):
def __init__(self):
self.a = 0
self.b = 1 def next(self):
self.a,self.b = self.b,self.a+self.b
return self.a def __iter__(self):
return self fibs = Fibs() # output: 1 1 2 3 5 8 13 21 34 55
for f in fibs:
if f < 60:
print f,
else:
break
我们可以借助内建函数iter
从可迭代的对象中获得迭代器:
it = iter([1,2,3])
print it.next() #
print it.next() #
print it.next() #
也可以将迭代器转换为一个序列:
# -- coding: utf-8 --
class TestIterator(object):
value = 0
def next(self):
self.value += 1
if self.value >10:raise StopIteration
return self.value def __iter__(self):
return self ti = TestIterator() # 通过list构造方法显示将迭代器转换为列表
print list(ti)
6. 生成器
生成器是用一种普通的函数语法定义的迭代器。任何包含yield
语句的函数都可以称为生成器。当包含yield
语句的函数被调用时,函数体中的代码不会被执行,而是返回一个迭代器对象。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。如下:
# -- coding: utf-8 --
nested = [[1,2],[3,4],[5]] def flatten(nested):
for sublist in nested:
for element in sublist:
yield element # <generator object flatten at 0x0000000001D90168>
print flatten(nested) # 调用时才真正执行
for num in flatten(nested):
print num, # 1 2 3 4 5