1、引言
(1)类和实例:类是对象的定义,实例是“真正的实物”。
定义类:类名通常大写字母打头。
class MyNewObjectType(bases):
'define MyNewObjectType class'
class_suite
bases可以是一个(单继承)或多个(多重继承)用于继承的父类。
object是“所有类之母”。
Python调用类进行实例化,实例化类不使用new关键字。
>>>c=MyNewObjectType()
类有时可以仅作为名称空间。
class MyData(object):
pass
mathobj=MyData()
mathobj.x=4
mathobj.y=5
这些属性是动态的,不需要在构造器中或其他任何地方为它们预先声明或赋值。
(2)方法
定义方法:属性和方法使用驼峰记法。
class MyDataWithMethod(object):
def printFoo(self):
print ....
所有方法都存在self,表示实例对象本身。
静态方法或类方法不需要self。
__init__()方法类似于构造函数,但不同于构造函数,因Python不new。该方法在实例被创建后,实例化调用返回这个实例之前被调用。
2、面向对象编程常用术语
3、类
Python中,一切皆为对象。
下面是类的定义语法。
class ClassName(object):
'class documentation string'
class_suite
Python不支持纯虚函数(像C++)或者抽象方法等。
替代方案:在基类方法中引发NotImplementedError异常。
4、类属性
(1)属性
属性(数据或函数),使用句点属性标识符来访问。
属性本身也是一个对象,也有自己的属性,所以访问属性时会形成一个属性链。
(2)类属性/实例数据属性
实例属性在OOP中用得最多,类属性仅当需要有更加“静态”数据类型时才变得有用,它和任何实例无关,方法是类属性。
Python要求,没有实例,方法不能被调用。方法必须“绑定”到一个实例才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能保证调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。
(3)确定一个类有哪些属性的方法
(4)特殊的类属性
5、实例
Python 2.2中统一了类和类型。
Python通过调用类对象来创建实例。
①__init__()方法
当类被调用,创建实例对象,对象创建后,调用__init__()方法完成特别的操作,执行完返回类对象,实例化结束。
Python没有使用new创建实例,没有定义构造器,由Python创建对象。
②__new__()方法
“构造器”方法,与__init__()方法相比,__new__()更像一个真正的构造器,因为__new__()必须返回一个合法的实例,该实例作为self传给__init__()方法。
__new__()方法会调用父类的__new__()方法来创建对象。
在对内建类型进行派生时,__new__()方法可以实例化不可变对象。
③__del__()方法
“解构器”方法,当实例对象所有的引用都被清除掉后才执行该方法,用于实例释放前进行特殊处理。
6、实例属性
方法严格来说是类属性。实例仅拥有数据属性。
(1)”实例化”实例属性
①在__init__()方法中设置实例属性。
设置实例的属性可以在实例创建后任意时间进行。__init__()方法是设置这些属性的关键点之一。Python能够在“运行时”创建实例属性(Python优秀特性之一)
②默认参数提供默认的实例安装。
class HotelRoomClac(object):
def __init__(self,rt,sales=0.085,rm=0.1):
self.salesTax=sales
self.roomTax=rm
self.roomRate=rt
③__init__()方法应该返回None
__init__()方法不应该返回任何对象,因为实例对象是自动在实例化调用后返回的。
(2)查看实例属性
(3)类属性和实例属性(类似于自动变量和静态变量)
可以采用类来访问类属性,若实例没有同名的属性的话,也可以用实例来访问。
类属性可以通过类或实例来访问,不过只能使用类访问类属性时,才能更新类属性的值。若在实例中更新类属性,将会创建同名的实例属性,“遮蔽”了类属性。当删除同名的实例属性,类属性才起作用。所以,从实例中访问类属性须谨慎。
class C(object): #定义类
version = 1.2#静态成员
>>>c=C()
>>>C.version #通过类来访问
>>>c.version #通过实例来访问
>>>C.version+=0.1 #通过类(只能这样)来更新类属性
>>>c.version =1.3 #任何对实例属性的赋值都会创建一个实例属性,而不是更新类属性
当类属性是可变类型时,并不会创建实例属性,直接操作的是类属性。
class Foo(object):
x={
2003:'poe2'}
>>>foo=Foo()
>>>foo.x[2004]='valid path'
>>>foo.x
{
2003:'poe2',2004:'valid path'}
>>>Foo.x
{
2003:'poe2',2004:'valid path'} #生效了
>>>del foo.x #删除会报错,因为没有遮蔽所以不能删除掉
(4)类属性持久性
类属性,任凭整个实例(及其属性)的如何进展,他都不理不睬(因此独立于实例),类属性的修改会影响到所有的实例。类属性是静态成员。
7、绑定和方法调用
(1)绑定
方法仅仅是类内部定义的函数,意味着方法是类属性而不是实例属性。
方法只有在类拥有实例时,才能被调用。方法被认为是绑定到实例。方法中的变量self表示调用此方法的实例对象。
(2)方法调用
①调用非绑定的方法(不常见):类还未实例化。
class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class'
def __init__(self,nm,ph,em):
AddrBookEntry.__init__(self,nm,ph) #覆盖父类方法
self.empid=em
②调用绑定方法:类已经实例化。
>>>mc=MyClass()
>>>mc.foo()
总结:方法定义于类内部,是类方法;方法绑定到实例,由实例调用;未绑定,由类调用。
8、静态方法和类方法(2.2)
(1)经典类中创建静态方法和类方法的例子
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo=staticmethod(foo) #内建函数,将方法转换成静态方法
class TestClassMethod:
def foo(cls): #cls为类对象,类似于self
print 'calling class method foo()'
foo=classmethod(foo) #内建函数,将方法转换成类方法
可以通过类或者实例调用这些函数。
>>>tsm=TestStaticMethod()
>>>TestStaticMethod.foo()
>>>tsm.foo()
>>>tcm=TestClassMethod()
>>>TestClassMethod.foo()
>>>tcm.foo()
(2)使用函数修饰符创建静态方法和类方法的例子(2.4)
class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'
9、继承
(1)通过继承覆盖方法
子类定义与基类相同的方法时,会覆盖(override)基类方法。
子类可以使用调用非绑定的基类方法的方法调用基类方法。
也可以使用super()内建方法调用基类方法。
当从一个带构造器__init__()的类派生,如果你不去覆盖__init__(),它将会被继承并自动调用,但如果你在子类中覆盖了__init__(),子类被实例化时,基类的__init__()就不会被自动调用。若要调用父类的__init__()方法,需要使用super()。
(2)从标准类型派生
经典类中,不能对标准类型进行子类化。
2.2后,可以对标准类型进行子类化。
子类化Python类型:其中一个是可变类型;另一个是不可变类型。
①子类化不可变类型
class RoundFloat(float):
def __new__(cls,val):
return float.__new__(cls,round(val,2))
所有的__new__()方法都是类方法,所以显式地传入类作为第一个参数。
class RoundFloat(float):
def __new__(cls,val):
return super(RoundFloat,cls).__new__(cls,round(val,2))
通常使用super()内建函数去捕获对应的父类以调用它的__new__()方法。
②子类化可变类型
class SortedKeyDict(dict):
def keys(self):
return sorted(super(SortedKeyDict,self).keys())
(3)多重继承中方法解释顺序(MRO)
2.2之前,算法简单:深度优先,从左至右进行搜索,取得在子类中使用的属性。多重继承取找到的第一个名字。
2.2提出新的MRO,算法思想是根据每个祖先类的继承结构编译出一张列表,包括搜索到的类,按策略删除重复的。
2.3使用新的C3算法替换,采用广度优先。
新式类有__mro__属性,告诉你查找顺序。
新式类使用经典类的MRO会失败。
菱形效应
使用经典类的MRO,当实例化D时,不再得到C.__init__()之结果,而得到object.__init__()之结果。使用新式类,需要出现基类,这样在继承结构中,就形成了一个菱形。
补充:文档字符串不会从基类中继承过来。因为文档字符串对类,函数/方法,还有模块来说都是唯一的。
10、类、实例、其他对象的内建函数
(1)issubclass()
布尔函数,判断一个类是另一个类的子类或子孙类(一个类可视为其自身的子类)。
issubclass(sub,sup)
从2.3开始,第二个参数可以是可能的父类组成的元组。只要sub是其中任何一个的子类都返回True。
(2)isinstance()
布尔函数,判定一个对象是否是另一个给定类的实例。
isinstance(obj1,obj2)
obj1是obj2的一个实例,或是obj2的子类的一个实例时,返回True。
从2.2开始,obj2可以是一个元组,obj1是obj2元组中任何一个候选类型或类的实例时,就返回True。
(3)hasattr(),getattr(),setattr(),delattr()
*attr()系列函数可工作于各种对象,不限于类和实例。
*attr(obj,’attr’….)相当于操作obj.attr。
hasattr()布尔函数,决定一个对象是否有一个特定的属性。
getattr(),setattr()相应地取得和赋值给对象的属性。getattr()会在你试图读取一个不存在的属性时,引发AttributeError异常。
delattr()删除属性。
(4)dir()
可用于实例或者类或者模块。
(5)super()(2.2)
帮助程序员找出相应的父类,然后调用相关属性。
super(type[,obj])返回type的父类,传入obj参数进行父类绑定。
super(MyClass,self).__init__()
(6)vars()
与dir()相似,只是给定的对象参数都必须有一个__dict__属性。如果提供的对象没有一个这样的属性,则会引发一个TypeError异常。
vars()返回一个字典,包含存储于对象__dict__中的属性(键)和值。如果没有为vars()提供参数,将显示一个包含本地名称空间的属性(键)及其值的字典,也就是locals()。
11、用特殊方法定制类
Python特殊方法可以用来扩充类的功能,可以实现:
(1)Python中用来定制类的特殊方法
①基本定制型
②对象(值)比较
③属性
④数值类型:二元操作符
⑤数值类型:二进制操作符
⑥数值类型:一元操作符
⑦数值类型:数值转换
⑧数值类型:基本表示法(String)
⑨数值类型:数值压缩
⑩序列类型
⑪映射类型
(2)简单定制
自己实现init(),str(),repr()等。
print使用str()方法,真正的字符串对象表示使用repr()方法。
#! /usr/bin/env python
class RoundFloatManual(object):
def __init__(self,val):
assert isinstance(val,float),\
"Value must be a float!"
self.value=round(val,2)
def __str__(self):
return '%.2f' % self.value
__repr__=__str__
(3)数值定制
重载__add__()方法,就重载了(+)操作符。
还可以使用__radd__()方法和__iadd__()方法。
def __add__(self,other):
return self.__class__(self.hr+other.hr,self.min+other.min)
覆盖“原位”操作,实现增量赋值(2.0),比如iadd()支持mon+=tue。
(4)定制迭代器
实现类中的__iter__()和next()方法来创建一个迭代器。
#! /usr/bin/env python
class AnyIter(object):
def __init__(self,data,safe=False):
self.safe=safe
self.iter=iter(data)
def __iter__(self):
return self
def next(self,howmany=1):
retval=[]
for eachItem in range(howmany):
try:
retval.append(self.iter.next())
catch StopIteration:
if self.safe:
break
else:
raise
return retval
12、私有化
类中属性默认情况下是“公开的”,类所在模块以及导入类所在模块中的代码都可以访问到。
(1)双下划线
Python使用双下划线(__)来“混淆”属性,不允许直接访问。
混淆后的属性,会在名字前面加上下划线和类名,比如NumStr类中的__num属性,被混淆后,用于访问这个数据值的标识符就变成了self._NumStr__num。混淆操作可以防止在父类或子类中的同名冲突。
(2)单下划线
使用单下划线(_)实现简单的模块级私有化。
13、授权
(1)包装
包装任何类型作为一个类的核心成员,使新对象的行为模仿你想要的数据类型中已经存在的行为,且去掉不希望存在的行为。扩充Python是包装的另一种形式。
(2)实现授权
授权是包装的一个特性。
授权的过程即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。实现授权的关键点是覆盖__getattr__()方法,在代码中包含一个对getattr()内建函数的调用。
class WrapMe(object):
def __init__(self,obj):
self.__data=obj
def get(self):
return self.__data
def __repr__(self):
return 'self.__data'
def __str__(self):
return str(self.__data)
def __getattr__(self,attr):
return getattr(self.__data,attr)
>>>wrappedComplex=WrapMe(3.5+4j)
>>>wrappedComplex.real
访问real属性时,Python解释器将试着在局部名称空间中查找那个名字;若没有找到,会搜索类名称空间,以一个类属性访问;若还没有找到,搜素则对原对象开始授权请求,此时调用__getattr__()方法,__getattr__()方法中调用getattr()方法得到一个对象的默认行为。
总结:通过覆盖__getattr__()方法实现授权。
授权只能访问属性,特殊行为不可以。例如对列表的切片操作,它内建于类型中,不是属性,不能授权访问。
属性可以是数据属性,还可以是函数或者方法。Python所有数值类型,只有复数拥有属性:数据属性和conjugate()内建方法。
>>>wrappedList=WrapMe([123,'foo',45.67])
>>>wrrapedList[3] #会抛出AttributeError
此时可以采用“作弊”的方法来访问实际对象和它的切片能力。
>>>realList=wrappedList.get() #get()方法取得对原对象的访问
>>>realList[3]
14、新式类的高级特性(2.2+)
(1)新式类的通用特性
类型和类的统一,使得可以子类化Python数据类型。同时,所有的Python内建的“casting”或转换函数现在都是工厂函数。例如:int(),long(),float(),complex;str(),unicode();list(),tuple();type()。
另外,还加入了一些新的函数:basestring();dict();bool();set(),frozenset();object();classmethod();staticmethod();super();property();file()。这些类名和工厂函数,不仅能创建这些类名的新对象,还可以用来作为基类,去子类化类型。现在还可以用于isinstance()内建函数,isinstance()函数在obj是一个给定类型的实例或其子类的实例时返回True。
OLD(not as good):
if type(obj)==type(0)...
if type(obj)==types.IntType...
BETTER:
if type(obj) is type(0)...
EVEN BETTER:
if isinstance(obj,int)...
if isinstance(obj,(int,long))...
if type(obj) is int...
(2)__slots__类属性
__dict__属性跟踪所有实例属性。
实例inst,属性foo,那么inst.foo与inst.__dict__[‘foo’]等价。
字典会占用大量内存,为内存上的考虑,可用__slots__属性替代__dict__。
__slots__是一个类变量,由一序列型对象组成。由所有合法标识构成的实例属性的集合来表示。任何试图创建一个其名不在__slots__中的名字的实例属性都将导致AttributeError异常。带__slots__属性的类定义不会存在__dict__属性了。使用__slots__属性的目的是节约内存。使用__slots__属性可以防止用户随心所欲的动态增加实例属性。
class SlottedClass(object):
__slots__=('foo','bar')
>>>c=SlottedClass()
>>>c.foo=42
>>>c.xxx='nihao' #引发AttributeError异常
(3)__getattribute__()特殊方法
Python类有一个__getattr__()的特殊方法,仅当属性不能在实例或类或祖先类的__dict__属性中找到时,才被调用。__getattribute__()与__getattr__()类似,不同在于,当属性被访问时,它就一直可以被调用,而不局限于不能找到的情况。在同时定义了__getattribute__()及__getattr__()方法的类中,除非明确从__getattribute__()方法调用,或者__getattribute__()方法引发了AttributeError异常,否则后者不会被调用。如果将要在__getattribute__()方法中访问这个类或其祖先类的属性,应该总是调用祖先类的同名方法,避免引起无穷递归。
(4)描述符(描述符就是可重用的属性)
可认为描述符是表示对象属性的一个代理,它为属性提供了强大的API。当需要属性时,可以通过描述符来访问它(当然还可以使用常规的句点属性标志法来访问属性)。
__get__(),__set__(),__delete__()特殊方法分别用于得到一个属性的值,对一个属性进行赋值,删除掉某个属性。同时覆盖__get__()和__set__()的类被称作数据描述符。实现了__set__()方法的类被称作非数据描述符,或方法描述符。
__get__(),__set__(),__delete__()的原型如下:
整个描述符系统的心脏是__getattribute__()特殊方法,因为对每个属性的访问都会调用这个特殊的方法。
举例来说,给定类X和实例x:
访问实例属性,x.foo由__getattribute__()转化成:
type(x).__dict__['foo'].__get__(x,type(x))
访问类属性,那么None将作为对象被传入:
X.__dict__['foo'].__get__(None,X)
访问父类属性,super(Y,obj).foo(假设Y为X的子类):
X.__dict__['foo'].__get__(obj,X)
静态方法、类方法、属性,甚至所有的函数都是描述符。Python中函数之间的唯一区别在于调用方式的不同,分为绑定和非绑定狼类,函数描述符可以处理这些问题,描述符会根据函数的类型确定如何“封装”这个函数和函数被绑定的对象,然后返回调用对象。使用描述符的顺序很重要,有一些描述符的级别要高于其他的。描述符是一个类属性,因此所有的类属性皆具有最高的优先级。优先级排序:类属性>数据描述符>实例属性>非数据描述符>默认为__getattr__()。
#! /usr/bin/env python
import os
import pickle
class FileDescr(object):
saved=[]
def __init__(self,name=None):
self.name=name
def __get__(self,obj,typ=None):
if self.name not in FileDescr.saved:
raise AtrributeError,"%r used before assignment" % self.name
try:
f=open(self.name,'r')
val=pickle.load(f)
f.close()
return val
except(pickle.UnpicklingError,IOError,EOFError,AttributeError,ImportError,IndexError),e:
raise AttributeError, "could not read %r" % self.name
def __set__(self,obj,val):
f=open(self.name,'w')
try:
pickle.dump(val,f)
FileDescr.saved.append(self.name)
except(TypeError,pickle.PickingError),e:
raise AttributeError, "could not pickle %r" % self.name
finally:
f.close()
def __delete__(self,obj):
try:
os.unlink(self.name)
FileDescr.saved.remove(self.name)
except(OSError,ValueError),e:
pass
>>>class MyFileValClass(object):
... foo=FileDescr('foo')
... bar=FileDescr('bar')
>>>fvc=MyFileVarClass()
>>>print fvc.foo #引发AttributeError
>>>fvc.bar=42
>>>print fvc.bar #打印42
(5)property()内建函数
属性是一种有用的特殊类型的描述符。属性用来处理所有实例属性的访问。
使用句点符号访问实例属性,其实是在修改实例的__dict__属性。
使用property()访问实例属性,使用的是函数(或方法)。
property(fget=None,fset=None,fdel=None,doc=None)
property()接受一些传进来的函数作为参数,property()是在它所在的类被创建时被调用的,传进来的函数都是非绑定的。
class HideX(object):
def __init__(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
def get_x(self):
return ~self.__x
def set_x(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
x=property(get_x,set_x)
改进代码==>
class AdvancedHideX(object):
def __init__(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
@property
def x():
def fget(self):
return ~self.__x
def fset(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
return locals()
改进后的代码:类名称空间更加简洁;用户不能通过inst.set_x(40)来给属性赋值,只能使用init.x=4。
(6)元类和__metaclass__
元类是一个类(一个类中类),它的实例是其他的类。当创建一个新类时,就是在使用默认的元类,它是一个类型对象(传统类的元类是types.ClassType).当某个类调用type()函数时,就会看到它到底是谁的实例。元类一般用于创建类,在执行类定义时,解释器必须知道类正确的元类,所以解释器会寻找类属性的__metaclass__属性,若没找到,会向上查找父类的__metaclass__属性,如果还没找到,解释器会检查名字为__metaclass__的全局变量,若还不存在,就用types.ClassType作为此类的元类。在类定义时,将检查此类正确的元类,元类通常传递三个参数到构造器:类名,从基类继承数据的元组和(类的)属性字典。通过定义一个元类可以“迫使”程序员按照某种方式实现目标类,这样既可以简化他们的工作,也可以使编写出的程序更符合特定标准。
# coding=gbk
#! /usr/bin/env python
from warnings import warn
class ReqStrSugRepr(type):
def __init__(cls,name,bases,attrd):
super(ReqStrSugRepr,cls).__init__(name,bases,attrd)
if '__str__' not in attrd:
raise TypeError('Class requires overriding of __str__()')
if '__repr__' not in attrd:
warn('Class suggests overriding of __repr__()\n',stacklevel=3)
print '*** Defined ReqStrSugRepr (meta)class \n'
class Foo(object):
__metaclass__=ReqStrSugRepr
def __str__(self):
return 'Instance of class:',self.__class__.__name__
def __repr__(self):
return self.__class__.__name__
print '*** Defined Foo Class \n'
class Bar(object):
__metaclass__=ReqStrSugRepr
def __str__(self):
return 'Instance of class:',self.__class__.__name__
print '*** Defined Bar Class\n'
class FooBar(object):
__metaclass__=ReqStrSugRepr
print '*** Defined FooBar Class\n'
14.其他模块