Python学习笔记13:Python面向对象编程

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)确定一个类有哪些属性的方法

  • 使用内建函数dir()
  • 访问类的字典属性__dict__
  • 内建函数vars()接受类对象作为参数,返回类的__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__属性、vars()
  • 特殊的实例属性: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'
>>>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会失败。
菱形效应
Python学习笔记13:Python面向对象编程_第1张图片
使用经典类的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()(2.2)
帮助程序员找出相应的父类,然后调用相关属性。
super(type[,obj])返回type的父类,传入obj参数进行父类绑定。

  • obj是一个实例,isinstance(obj,type)必须返回True;
  • obj是一个类或类型,issubclass(obj,type)必须返回True。
super(MyClass,self).__init__()

(6)vars()
与dir()相似,只是给定的对象参数都必须有一个__dict__属性。如果提供的对象没有一个这样的属性,则会引发一个TypeError异常。
vars()返回一个字典,包含存储于对象__dict__中的属性(键)和值。如果没有为vars()提供参数,将显示一个包含本地名称空间的属性(键)及其值的字典,也就是locals()。

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) 设置属性
  • 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、新式类的高级特性(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__()的原型如下:

  • __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()内建函数
属性是一种有用的特殊类型的描述符。属性用来处理所有实例属性的访问。
使用句点符号访问实例属性,其实是在修改实例的__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.其他模块

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

你可能感兴趣的:(Python,python,python类,元类,描述符,python面向对象)