Python 面向对象

面向对象概述:

    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、引言 

(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__
内建函数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'
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、静态方法和类方法(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() 

可用于实例或者类或者模块,用于实例,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性。用于类,显示类及它所有基类的__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 属性
'''

(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) 设置属性

# 特殊的两个方法
'''
__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()内建函数 

属性是一种有用的特殊类型的描述符。属性用来处理所有实例属性的访问。 
使用句点符号访问实例属性,其实是在修改实例的__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 标准操作符的函数接口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章