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():passFoo = type('Foo',(Object,),{}) # 类的创建默认由type()完成的

如何指定类由自定义的type创建

class Mytype(object): # 自定义创建类方法passclass Foo(object,metaclass=Mytype): # 查找自定义Mytype# __metaclass__=Mytype python2.xpassFoo = 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.funcFoo.func
'''

3.2、暴露类中的方法给外部访问

__slots__ = (暴露的方法属性名....)

#__slots__ = (暴露的方法属性名....)class Foo(object):__slots__ = ('name') # 只允许name属性被外部访问def __init__(self):self.name = '小明'# self.age = 30obj = 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=salesself.roomTax=rmself.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为类对象,类似于selfprint 'calling class method foo()'foo=classmethod(foo)   #内建函数,将方法转换成类方法

可以通过类或者实例调用这些函数。

tsm=TestStaticMethod()
TestStaticMethod.foo()
tsm.foo()
tcm=TestClassMethod()
TestClassMethod.foo()
tcm.foo()

(2)使用装饰器创建静态方法和类方法

class TestStaticMethod:@staticmethoddef foo():print 'calling static method foo()'
class TestClassMethod:@classmethoddef 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=safeself.iter=iter(data)def __iter__(self):return selfdef next(self,howmany=1):retval=[]for eachItem in range(howmany):try:retval.append(self.iter.next())catch StopIteration:if self.safe:breakelse:raisereturn 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=objdef get(self):return self.__datadef __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 pythonimport os
import pickleclass FileDescr(object):saved=[]def __init__(self,name=None):self.name=namedef __get__(self,obj,typ=None):if self.name not in FileDescr.saved:raise AtrributeError,"%r used before assignment" % self.nametry:f=open(self.name,'r')val=pickle.load(f)f.close()return valexcept(pickle.UnpicklingError,IOError,EOFError,AttributeError,ImportError,IndexError),e:raise AttributeError, "could not read %r" % self.namedef __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.namefinally: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=~xdef get_x(self):return ~self.__xdef set_x(self,x):assert isinstance(x,int),'"x" must be an integer!'self.__x=~xx=property(get_x,set_x)

改进代码==>

class AdvancedHideX(object):def __init__(self,x):assert isinstance(x,int),'"x" must be an integer!'self.__x=~x@propertydef x():def fget(self):return ~self.__xdef fset(self,x):assert isinstance(x,int),'"x" must be an integer!'self.__x=~xreturn locals()

改进后的代码:类名称空间更加简洁;用户不能通过inst.set_x(40)来给属性赋值,只能使用init.x=4。

(6)元类和__metaclass__

元类是一个类(一个类中类),它的实例是其他的类。当创建一个新类时,就是在使用默认的元类,它是一个类型对象(传统类的元类是types.ClassType).当某个类调用type()函数时,就会看到它到底是谁的实例。元类一般用于创建类,在执行类定义时,解释器必须知道类正确的元类,所以解释器会寻找类属性的__metaclass__属性,若没找到,会向上查找父类的__metaclass__属性,如果还没找到,解释器会检查名字为__metaclass__的全局变量,若还不存在,就用types.ClassType作为此类的元类。在类定义时,将检查此类正确的元类,元类通常传递三个参数到构造器:类名,从基类继承数据的元组和(类的)属性字典。通过定义一个元类可以“迫使”程序员按照某种方式实现目标类,这样既可以简化他们的工作,也可以使编写出的程序更符合特定标准。

# coding=gbk#! /usr/bin/env pythonfrom warnings import warnclass 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__=ReqStrSugReprdef __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__=ReqStrSugReprdef __str__(self):return 'Instance of class:',self.__class__.__name__print '*** Defined Bar Class\n'class FooBar(object):__metaclass__=ReqStrSugReprprint '*** Defined FooBar Class\n'

15.其他模块

UserList 提供一个列表对象的封装类
UserDict 提供一个字典对象的封装类
UserString 提供一个字符串对象的封装类
types 定义所有Python对象的类型再标准Python解释器中的名字
operator 标准操作符的函数接口

Python 面向对象相关推荐

  1. python面向对象的优点_Python面向对象编程——总结面向对象的优点

    Python面向对象编程--总结面向对象的优点 一.从代码级别看面向对象 1.在没有学习类这个概念时,数据与功能是分离的 def exc1(host,port,db,charset): conn=co ...

  2. 这可能是Python面向对象编程的最佳实践

    作者 | 崔庆才 来源 | 进击的Coder(ID:FightingCoder) Python 是支持面向对象的,很多情况下使用面向对象编程会使得代码更加容易扩展,并且可维护性更高,但是如果你写的多了 ...

  3. 第八课.Python面向对象(二)

    类的继承和多态 继承和多态与函数有共同的目的,都是为了减少代码的冗余,提高复用的效率: 根据"Python面向对象(一)"的笔记,我现在定义一个Cinema类: #父类 class ...

  4. Python面向对象编程:类继承和其衍生术语

    Python面向对象编程03:类继承和其衍生术语 前面我们讲到过正则表达式字符等,上一篇分享了面向对象编程和类的结构,最后稍微提到了继承. Python面向对象编程:深度认识类class_ Pytho ...

  5. 《Python面向对象编程指南》——1.2 基类中的__init__()方法

    本节书摘来自异步社区<Python面向对象编程指南>一书中的第1章,第1.2节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区"异步社区 ...

  6. Python 面向对象 基础

    编程范式概述: 面向过程 和 面向对象 以及函数式编程 面向过程:(Procedure Oriented)是一种以事件为中心的编程思想. 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实 ...

  7. python面向对象类属性_python面向对象之类属性和类方法案例分析

    本文实例讲述了python面向对象之类属性和类方法.分享给大家供大家参考,具体如下: 目标 类的结构 类属性和实例属性 类方法和静态方法 01. 类的结构 1.1 术语 -- 实例 使用面相对象开发, ...

  8. 关于python面向对象编程中、下列说法中_关于Python面向对象编程的知识点总结

    前言 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程. 接下来我们 ...

  9. python对象编程例子-这是我见过最详细的Python面向对象编程!建议收藏!

    原标题:这是我见过最详细的Python面向对象编程!建议收藏! 面向对象编程和函数式编程(面向过程编程)都是程序设计的方法,不过稍有区别. 面向过程编程: 1. 导入各种外部库 2. 设计各种全局变量 ...

  10. python面向对象编程的优点-Python面向对象编程——总结面向对象的优点

    Python面向对象编程--总结面向对象的优点 一.从代码级别看面向对象 1.在没有学习类这个概念时,数据与功能是分离的 def exc1(host,port,db,charset): conn=co ...

最新文章

  1. 数据预处理+缺失值处理方案+Missing Value+pandas+缺失值填充方法、方案
  2. c语言中数组作为形参
  3. 11座城市,58个.NET最新岗位速览,内推直通面试官!
  4. Solaris 10 ftp,telnet,ssh,sendmail
  5. ASP.NET中的图片缓存
  6. 从Android发展看Meego
  7. 鸿蒙谁法力最强,上古神话中,鸿蒙初开时有九大古神,鸿钧勉强上榜,烛龙位列前三...
  8. AD的PCB文件无法保存问题
  9. java开发工程师面试自我介绍_java程序员面试自我介绍范文
  10. mp3文件怎么压缩大小
  11. 华为手机刷机后显示无服务器,华为手机刷机后,无法开机怎么办?
  12. 用Python如何计算两点间距离
  13. 人员基础信息一体化采集仪_重要!包头退休人员注意!涉及信息采集(附操作步骤)...
  14. cenntos7安装Nginx添加passenger模块
  15. 1+x 云计算平台运维与开发测试题
  16. 【宋红康 MySQL数据库 】【高级篇】【03】MySQL的数据目录
  17. 李嘉诚,原一平的故事
  18. SecureCRT和SecureFX
  19. 在中国,哪个互联网行业职位更有前途?
  20. linux API大全

热门文章

  1. 算力智库2021隐私计算论坛圆满落幕,隐私计算落地会长出怎样的新商业模式?
  2. WPS怎么转换成PDF?这样转换准没错
  3. 图解数据在网络中的传输过程
  4. 处理mysql启动报错Table 'mysql.plugin' doesn't exis
  5. SpringbootApi接口学习笔记
  6. Pycharm下同一目录的py文件不能相互调用的原因分析
  7. html文件用word打开是乱码,Word文档打开是乱码怎么解决
  8. 这几行最简单的代码 ,却改变了世界
  9. Android解析服务器Json数据实例
  10. 【九】【vlc-android】vlc-aout音频流输出端源码分析