1、引言
面向对象概述:
python支持两种编程方式,函数式编程、面向对象编程,三大特性:封装、继承、多态。
封装:把功能相同的方法封装到类中、数据封装到对象中;
继承:如果多个类中有相同的方法和数据,避免重复编写,把相同的方法提取出来放在基类中,给子类进行继承使用;
多态:python天生支持多态,对于参数可以传入任何类型的对象,只要保证有所要的send方法即可。
面向对象进阶:
__init__,初始化对象
__new__,创建对象
__call__,对象()
__getattr__,对象.属性
__setattr__,对象.属性 = 'Jsom'
__delattr__,删除属性
__getitem__,对象['']
__setitem__,对象[''] = 'Jsom'
__delitem__,删除属性
__mro__,查找成员顺序,继承关系
__str__,
__repr__,
__iter__,
__enter__,with 运行前调用
__exit__,with 运行结束调用
__dict__,存放实例对象变量、方法字典
__class__,查看实例对象指向的类
__add__,对象+一个对象
metaclass:类的创建方式与自定义创建类的方式
类创建:
class Foo():
pass
Foo = type('Foo',(Object,),{}) # 类的创建默认由type()完成的
如何指定类由自定义的type创建
class Mytype(object): # 自定义创建类方法
pass
class Foo(object,metaclass=Mytype): # 查找自定义Mytype
# __metaclass__=Mytype python2.x
pass
Foo = Mytype('Foo',(object,),{})
默认执行顺序:( __init__ --> __call__ --> __new__ --> __init__)。
PS:在类的继承关系中,基类指定了metaclass的话,子类也会根据基类指定的metaclass创建。
(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。该方法在实例被创建后,实例化调用返回这个实例之前被调用。
(3)栈
.push() 添加
.pop() 弹出第一个,并返回。
.top() 获取第一个
# 栈的管理方式
class Stack(object):
def __init__(self):
self.data = []
def push(self,val):
self.data.append(val)
def pop(self):
return self.data.pop()
def top(self):
return self.data[-1]
_stack = Stack()
_stack.push('小明')
_stack.push('小强')
print(_stack.pop()) # pop取出数据并删除相对应的值
print(_stack.pop())
print(_stack.top()) # top取出数据不删除 输出报错 因为 pop 已经删除了倒数第一个内容
2、面向对象编程常用术语
抽象/实现:建模 现实化
封装/接口
合成:联合、聚合
继承/派生
泛化/特化
多态
自省/反射
3、类
Python中,一切皆为对象,下面是类的定义语法。
class ClassName(object):
'class documentation string'
class_suite
Python不支持纯虚函数(像C++)或者抽象方法等,替代方案:在基类方法中引发NotImplementedError异常。
3.1、执行父类的方法
class Base(object):
def func(self):
print('Base.func')
class Foo(Base):
def func(self):
#方法一:根据__mro__的循序执行类中的方法
#super(Foo,self).func()
#方法二:主动执行Base类的方法
Base.func(self) # 类运行其中的方法必须传入self,对象运行方法自动会识别。
print('Foo.func')
obj = Foo()
obj.Func()
'''
输出:
Base.func
Foo.func
'''
3.2、暴露类中的方法给外部访问
__slots__ = (暴露的方法属性名....)
#__slots__ = (暴露的方法属性名....)
class Foo(object):
__slots__ = ('name') # 只允许name属性被外部访问
def __init__(self):
self.name = '小明'
# self.age = 30
obj = Foo()
print(obj.name)
# print(obj.age)
'''
输出:小明
'''
4、类属性
(1)属性
属性(数据或函数),使用‘.’属性标识符来访问。
属性本身也是一个对象,也有自己的属性,所以访问属性时会形成一个属性链。
(2)类属性/实例数据属性
实例属性在OOP中用得最多,类属性仅当需要有更加“静态”数据类型时才变得有用,它和任何实例无关,方法是类属性。
Python要求,没有实例,方法不能被调用。方法必须“绑定”到一个实例才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能保证调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。
(3)确定一个类有哪些属性的方法
使用内建函数 __dir__()
访问类的字典属性 __dict__
(4)特殊的类属性
C.__name__ 类C的名字(字符串)
C.__doc__ 类C的文档字符串
C.__bases__ 类C的所有父类构成的元组
C.__dict__ 类C的属性
C.__module__ 类C所在的模块(1.5)
C.__class__ 实例C对应的类(新式类)
5、实例
Python 2.2中统一了类和类型。
Python通过调用类对象来创建实例。
①__init__()方法
当类被调用,创建实例对象,对象创建后,调用__init__()方法完成特别的操作,执行完返回类对象,实例化结束。
Python没有使用new创建实例,没有定义构造器,由Python创建对象。
②__new__()方法
“构造器”方法,与__init__()方法相比,__new__()更像一个真正的构造器,因为__new__()必须返回一个合法的实例,该实例作为self传给__init__()方法,__new__()方法会调用父类的__new__()方法来创建对象,在对内建类型进行派生时,__new__()方法可以实例化不可变对象。
③__del__()方法
“解构器”方法,当实例对象所有的引用都被清除掉后才执行该方法,用于实例释放前进行特殊处理,__del__()方法只能被调用一次,使用__del__()方法,不要忘记首先调用父类的__del__()方法,del x不表示调用x.__del__()方法,仅引用计数减少,若存在循环引用,则对象的__del__()方法可能永远不会被执行,__del__()方法未捕获的异常会被忽略掉,除非有必要,否则不去实现__del__()方法,如果定义了__del__()方法,且实例是某个循环的一部分,垃圾回收器将不会终止这个循环,你需要自己显式调用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)查看实例属性
查看实例属性:__dir__()、__dict__属性
特殊的实例属性:I.__class__、I.__dict__
内建类型属性:内建类型可以使用__dir__()方法,不可以访问__dict__特殊属性,因为在内建类型中,不存在这个属性。
(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'
print(foo.x)
# 输出:{2003:'poe2',2004:'valid path'}
print(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、静态方法和类方法
(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)使用装饰器创建静态方法和类方法
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))
# cls 指向当前类。
通常使用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__()
可用于实例或者类或者模块,用于实例,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性。用于类,显示类及它所有基类的__dict__中的内容,但不会显示定义在元类中的类属性,用于模块,显示模块的__dict__的内容。__dir__()不带参数时,显示调用者的局部变量。
(5)super()
super(MyClass,self)不是查找父类,而是根据__mro__的循序开始查找(继承的先后进行查找);
obj是一个实例,isinstance(obj,type)必须返回True;
obj是一个类或类型,issubclass(obj,type)必须返回True;
super(MyClass,self).__init__()。
# super的执行查找循序
class Base(object):
def func(self):
super(Base,self).func()
print('Base.func')
class Bar(object):
def func(self):
print('Bar.func')
class Foo(Base,Bar):
pass
# 示例一
obj = Foo()
obj.func()
print(Foo.__mro__) # Foo.__mro__ 获取类的继承关系
'''
输出:
(<class '__main__.Foo'>, <class '__main__.Base'>, <class '__main__.Bar'>, <class 'object'>)
'''
# 示例二
obj = Base()
obj.func()
'''
输出:报出错误信息,因为super对象中没有 func 属性
'''
11、用特殊方法定制类
Python特殊方法可以用来扩充类的功能,可以实现:模拟标准类型;重载操作符。
(1)Python中用来定制类的特殊方法
①基本定制型
C.__init__(self[,arg1,…]) 构造器(带一些可选的参数)
C.__new__(self[,arg1,…])构造器(带一些可选的参数);通常用在设置不变数据类型的子类
C.__del__(self) 解析器
C.__str__(self) 可打印的字符输出;内建str()及print语句
C.__repr__(self) 运行时的字符串输出;内建repr()和“操作符
C.__unicode__(self) Unicode字符串输出:内建unicode()
C.__call__(self,*args) 表示可调用的实例
C.__nonzero__(self) 为object定义False值;内建bool()
C.__len__(self) “长度”(可用于类);内建len()
②对象(值)比较
C.__cmp__(self,obj) 对象比较:内建cmp()
C.__lt__(self,obj) and C.__le__(self,obj) 小于/小于或等于:对应<及<=操作符
C.__gt__(self,obj) and C.__ge__(self,obj) 大于/大于或等于:对应>及>=操作符
C.__eq__(self,obj) and C.__ne__(self,obj) 等于/不等于:对应==,!=及<>操作符
③属性
C.__getattr__(self,attr) 获取属性:内建getattr(),仅当属性没有找到时调用
C.__setattr__(self,attr,val) 设置属性
# 特殊的两个方法
'''
__getattr__(self,key),__setattr__(self,key,value)的注意事项
类继承基类object中也包含了__getattr__ 和 __setattr__ 方法,如果类中没有自定义这两个方法,会执行object中的方法
'''
class Foo(object):
def __init__(self):
# self是Foo对象,self.storage 表示给对象中的属性storage赋值,这样就会先执行__setattr__
self.storage = {}
object.__setattr__(self,'storage',{})
# 通过父类给属性赋值,这样可以绕开自定义的__setattr__,率先执行创建赋值,执行后就能正常使用了
def __getattr__(self,item):
print(item)
def __setattr__(self,key,value):
'''
注意:在__init__中的属性不能直接在__setattr__中直接引用,因为运行__init__给属性赋
值时,会先运行__setattr__创建属相并赋值,所以在__setattr__获取self.storage会报错
'''
# print(self.storage)
print(key,value)
obj = Foo() # 执行了__init__
obj.xx = 123 # 执行了__setattr__
'''
输出:
storage = {}
xx 123
'''
C.__delattr__(self,attr) 删除属性
C.__getattribute__(self,attr) 获取属性:内建getattr(),总是被调用
C.__get__(self,attr) (描述符)获取属性
C.__set__(self,attr,val) (描述符)设置属性
C.__delete__(self,attr) (描述符)删除属性
④数值类型:二元操作符
C.__*add__(self,obj) 加:+操作符
C.__*sub__(self,obj) 减:-操作符
C.__*mul__(self,obj) 乘:*操作符
C.__*div__(self,obj) 除:/操作符
C.__*truediv__(self,obj) True除:/操作符
C.__*floordiv__(self,obj) Flooor除://操作符
C.__*mod__(self,obj) 取模/取余:%操作符
C.__*divmod__(self,obj) 除和取模:内建divmod()
C.__*pow__(self,obj[,mod]) 乘幂:内建pow(),**操作符
⑤数值类型:二进制操作符
C.__*lshift__(self,obj) 左移位:<<操作符
C.__*rshift__(self,obj) 右移位:>>操作符
C.__*and__(self,obj) 按位与:&操作符
C.__*or__(self,obj) 按位或:|操作符
C.__*xor__(self,obj) 按位与或:^操作符
⑥数值类型:一元操作符
C.__neg__(self) 一元负
C.__pos__(self) 一元正
C.__abs__(self) 绝对值,内建abs()
C.__invert__(self) 按位求反,~操作符
⑦数值类型:数值转换
C.__complex__(self,com) 转为complex(复数),内建complex()
C.__int__(self) 转为int,内建int()
C.__long__(self) 转为long,内建long()
C.__float__(self) 转为float,内建float()
⑧数值类型:基本表示法(String)
C.__oct__(self) 八进制表示,内建oct()
C.__hex__(self) 十六进制表示,内建hex()
⑨数值类型:数值压缩
C.__coerce__(self,num) 压缩成同样的数值类型,内建coerce()
C.__index__(self) 在有必要时,压缩可选的数值类型为整型(比如用于切片索引等)
⑩序列类型
C.__len__(self) 序列中项的数目
C.__getitem__(self,ind) 得到单个序列
C.__setitem__(self,ind,val) 设置单个序列元素
C.__delitem__(self,ind) 删除单个序列元素
C.__getslice__(self,ind1,ind2) 得到序列片段
C.__setslice__(self,ind1,ind2,val) 设置序列片段
C.__delslice__(self,ind1,ind2) 删除序列片段
C.__contains__(self,val) 测试序列成员:内建in关键字
C.__*add__(self,obj) 串联:+操作符
C.__*mul__(self,obj) 重复:*操作符
C.__iter__(self) 创建迭代器:内建iter()
⑪映射类型
C.__len__(self) mapping中项的数目
C.__hash__(self) 散列(hash)函数值
C.__getitem__(self,key) 得到给定键(key)的值
C.__setitem__(self,key,val) 设置给定键(key)的值
C.__delitem__(self,key) 删除给定键(key)的值
C.__missing__(self,key) 给定键如果不存在字典中,则提供一个默认值
(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、新式类的高级特性
(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__()、__getattr__()特殊方法
Python类有一个__getattr__()的特殊方法,仅当属性不能在实例或类或祖先类的__dict__属性中找到时,才被调用。__getattribute__()与__getattr__()类似,不同在于,当属性被访问时,它就一直可以被调用,而不局限于不能找到的情况。在同时定义了__getattribute__()及__getattr__()方法的类中,除非明确从__getattribute__()方法调用,或者__getattribute__()方法引发了AttributeError异常,否则后者不会被调用。如果将要在__getattribute__()方法中访问这个类或其祖先类的属性,应该总是调用祖先类的同名方法,避免引起无穷递归。
(4)描述符(描述符就是可重用的属性)
可认为描述符是表示对象属性的一个代理,它为属性提供了强大的API。当需要属性时,可以通过描述符来访问它(当然还可以使用常规的句点属性标志法来访问属性)。
__get__(),__set__(),__delete__()特殊方法分别用于得到一个属性的值,对一个属性进行赋值,删除掉某个属性。同时覆盖__get__()和__set__()的类被称作数据描述符。实现了__set__()方法的类被称作非数据描述符,或方法描述符。
__get__(),__set__(),__delete__()的原型如下:
__get__(self,obj,typ=None)=>None
__set__(self,obj,val)=>None
__delete__(self,obj)=>None
整个描述符系统的心脏是__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()装饰器
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'
15.其他模块
UserList 提供一个列表对象的封装类
UserDict 提供一个字典对象的封装类
UserString 提供一个字符串对象的封装类
types 定义所有Python对象的类型再标准Python解释器中的名字
operator 标准操作符的函数接口