[PYTHON] 核心编程笔记(13.Python面向对象编程)

13.1 介绍

类与实例:

类与实例相互关联,类是对象的定义,而实例是”真正的实物”,它存放了类中所定义的对象的具体信息

下面的示例展示如何创建一个类:

class MyNewObjectType(bases):

   ‘define MyNewObjectType class’

   class_suite #类体

示例:

>>> class MyData(object): #创建类

…   pass              

>>> mathObj = MyData()   # 类的实例

>>> mathObj.x = 4        

>>> mathObj.y = 5

>>> mathObj.x + mathObj.y

9

>>> mathObj.x * mathObj.y

20

方法

我们改进类的方法之一就是给类添加功能,类的功能就是叫方法

在Python中,方法定义在类定义中,但只能被实例所调用.也就是说,调用一个方法的最终途径必须是:

1.定义类(和方法) 2.创建一个实例 (3)用这个实例调用方法

class MyDataWithMethod(object):

  def printFoo(self):

print ‘You invoked printFoo()!’

现在我们来实例化这个类,然后调用那个方法:

>>> myObj = MyDataWithMethod()

>>> myObj.printFoo()

You invoked printFoo()!

注: __init__()方法在类中创建后,在实例化调用这个类并返回这个实例之前,会调用这个方法,执行一些额外的特定任务或设置

创建一个类(类定义)

>>> class AddrBookEntry(object):  # 类定义

…   ‘address book entry class’  

…   def __init__(self, nm, ph):  # 定义构造器

…     self.name = nm # 设置 name

…     self.phone =ph # 设置 phone

…     print ‘Created instance for: ‘, self.name

…   def updatePhone(self, newph): # 定义方法

…     self.phone = newph

…     print ‘Updated phone# for:’, self.name

在AddrBookEntry类的定义中,定义了两个方法: __init__()和updatePhone()

__init__()在实例化时被调用,即,在AddrBookEntry()被调用时,你可以认为实例化是对__init__()的一种隐式调用,因为传给AddrBookEntry()的参数完全与__init__()接收到的参数是一样的(除了self,它是自动传递的)

当方法在实例中被调用时,self(实例对象)参数自动由解释器传递,所以在上面的__init__()中,需要参数是nm和ph,他们分别表示名字和电话号码,__init__在实例化时,设置这两个属性,以便在实例化调用返回时是可见的

创建实例(实例化)

>>> john = AddrBookEntry(‘John Doe’, ‘408-555-1212’) #为john Doe创建实例

Created instance for:  John Doe

>>> jane = AddrBookEntry(‘Jane Doe’, ‘650-555-1212’) #为john Doe创建实例  

Created instance for:  Jane Doe

这就是实例化调用,他会自动调用__init__(), self把实例对象自动传入__init__()

当对象john被实例化后,它的john.name已经被设置了

访问实例属性:

>>> john

<__main__.AddrBookEntry object at 0x7f093c167a10>

>>> john.name

‘John Doe’

>>> john.phone

‘408-555-1212’

>>> jane.phone

‘650-555-1212’

>>> jane.name

‘Jane Doe’

方法调用(通过实例)

>>> john.updatePhone(‘415-555-1212’) # 更新John Doe的电话

Updated phone# for: John Doe

>>> john.phone

‘415-555-1212’

创建子类:

>>> class EmplAddrBookEntry(AddrBookEntry):          

…   ‘Enployee Address Book Entry class’            

…   def __init__(self,nm,ph,id,em):                

…     addrBookEntry.__init__(self,nm,ph)            

…     self.empid = id                              

…     self.email = em                              

…   def updateEmail(self,newem):                    

…     self.email = newem                            

…     print ‘Updated e-mail address for:’, self.name

在上面的类中,不仅定义了__init__(), updatEmail()方法,而且EmplAddrBookEntry还从AddrBiikEntry中继承了updatePhone()方法

注意,我们要显式传递self实例对象给基类构造器,因为我们而非在其实例而是在其子类实例中调用那个方法,因为我们不是通过实例来调用它,这种非绑定的方法调用需要传递一个适当的实例(self)给方法

使用子类:

>>>

>>> class EmplAddrBookEntry(AddrBookEntry):

…   ‘Enployee Address Book Entry class’                                

…   def __init__(self,nm,ph,id,em):                                    

…     AddrBookEntry.__init__(self,nm,ph)                                

…     self.empid = id                                                  

…     self.email = em                                                  

…   def updateEmail(self,newem):                                        

…     self.email = newem                                                

…     print ‘Updated e-mail address for:’, self.name                    

>>> john = EmplAddrBookEntry(“John Doe”, ‘408-555-1212′, 42,’john@spam.doe’)

Created instance for:  John Doe

>>> john

<__main__.EmplAddrBookEntry object at 0x7f093c167d10>

>>> john.name

‘John Doe’

>>> john.phone

‘408-555-1212’

>>> john.email

‘john@spam.doe’

>>> john.updatePhone(‘415-555-1212’)

Updated phone# for: John Doe

>>> john.phone

‘415-555-1212’

>>> john.updateEmail(‘john@doe.spam’)

Updated e-mail address for: John Doe

>>> john.email

‘john@doe.spam’

13.2 面向对象编程:

13.2.1 面向对象设计与面向对象编程的关系

13.2.2 现实世界中的问题

13.2.3 常用术语

抽象/实现

封装/接口

合成

派生/继承/继承结构

泛化/特化

多态

自省/反射

13.3 类

类是一种数据结构,可以用它来定义对象,后者把数据值和行为特性融合在一起,实例是这些对象的具体化

def functionName(args):

   “function documentation string” #函数文档字符串

   function_suite #函数体

class ClassName(args):

   “Class documentation string” #函数文档字符串

   class_suite #函数体

13.3.1 创建类

class ClassName(bases):

   “Class documentation string” #类文档字符串

   class_suite #类体

13.3.2 声明与定义

13.4 类属性

属性就是属于另一个对象的数据或函数元素,可以通过我们熟悉的句点属性标示法来

访问

当你访问一个属性时,它同时也是一个对象,拥有它自己的属性,可以访问,这导致一个属性链

sys.dtdout.write(‘foo’)

print myModule.myClass.__doc__

myList.extend(map(upper, open(‘x’).readlines()))

13.4.1 类的数据属性

数据属性仅仅是所定义的类的变量,他们可以像任何其他变量一样在类创建后被使用

看下面的例子,使用类数据属性(foo):

>>> class C(object):

…     foo = 100

>>> print C.foo

100

>>> C.foo = C.foo + 1

>>> print C.foo

101

注:上面的代码中,看不到任何类实例的引用

13.4.2 方法

方式是一个作为类定义一部分定义的函数,从而使得方法称为类属性

>>> class MyClass(object):        

…     def myNoActionMethod(self):

…         pass                  

>>> mc = MyClass()                

>>> mc.myNoActionMethod()  

13.4.2 决定类的属性:

知道一个类有哪些属性,有两种方法,最简单的是使用dir()内建函数,另外是通过访问类的字典属性__dict__

class MyClass(object):

…   ‘MyClass class definition’

…   myVersion = ‘1.1’

…   def showMyVersion(self):

…     print MyClass.myVersion

使用dir()和特殊类属性__dict__来查看类的属性:

>>> dir(MyClass)

[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__doc__’, ‘__format__’, ‘__getattribute__’, ‘__hash__’, ‘__init__’, ‘__module__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘myVersion’, ‘showMyVersion’]

>>> MyClass.__dict__

dict_proxy({‘__module__’: ‘__main__’, ‘showMyVersion’: <function showMyVersion at 0x7ffa067f1410>, ‘__dict__’: <attribute ‘__dict__’ of ‘MyClass’ objects>, ‘myVersion’: ‘1.1’, ‘__weakref__’: <attribute ‘__weakref__’ of ‘MyClass’ objects>, ‘__doc__’: ‘MyClass class definition’})

>>> print MyClass.__dict__

{‘__module__’: ‘__main__’, ‘showMyVersion’: <function showMyVersion at 0x7ffa067f1410>, ‘__dict__’: <attribute ‘__dict__’ of ‘MyClass’ objects>, ‘myVersion’: ‘1.1’, ‘__weakref__’: <attribute ‘__weakref__’ of ‘MyClass’ objects>, ‘__doc__’: ‘MyClass class definition’}

dir()返回的仅是对象的属性的一个名字列表,而__dict__返回的是一个字典,它的键(keys)是属性名,键值(values)是相应的属性对象的数据值

13.4.3 特殊的类属性:

C.__name__类C的名字(字符串)

C.__doc__类C的文档字符串

C.__bases__类C的所有父类构成的元祖

C.__dict__类C的属性

C.__module__类C定义所在的模块

C.__class__实例C对应的类

>>> MyClass.__name__

‘MyClass’

>>> MyClass.__doc__

‘MyClass class definition’

>>> MyClass.__bases__

(<type ‘object’>,)

>>> print MyClass.__dict__

{‘__module__’: ‘__main__’, ‘showMyVersion’: <function showMyVersion at 0x7ffa067f1410>, ‘__dict__’: <attribute ‘__dict__’ of ‘MyClass’ objects>, ‘myVersion’: ‘1.1’, ‘__weakref__’: <attribute ‘__weakref__’ of ‘MyClass’ objects>, ‘__doc__’: ‘MyClass class definition’}

>>> MyClass.__module__    

‘__main__’

>>> MyClass.__class__

<type ‘type’>

类型对象是一个内建类型的例子,它有__name__的属性

>>> stype = type(“What is your quest?”)

>>> stype

<type ‘str’>

>>> stype.__name__

‘str’

>>> type(3.14159265)

<type ‘float’>

>>> type(3.14159265).__name__

‘float’

Python支持模块间的类继承,为更清晰的对类进行描述,1.5版本引入了__module__,这样类名就完全由模块名所限定

>>> class C(object):

…     pass

>>> C

<class ‘__main__.C’>

>>> C.__module__

‘__main__’

类C的全名是”__main__.C”,比如,source_module.class_name,如果类C位于一个导入的模块中,如mymod,像下面的:

>>> from mymod import C

>>> C

<class ‘__main__.C at 0x54ea0’>

>>> C.__module__

‘mymod’

13.5 实例:

类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量

13.5.1 初始化:通过调用类对象来创建实例

>>> class MyClass(object): # 定义类

…     pass

>>> mc = MyClass() # 初始化类

13.5.2 __init__()”构造器” 方法

当类被调用,实例化的第一步就是创建实例对象,一旦对象创建了,Python检查是否实现了__init__()方法, 该方法若没有定义,则不会做任何操作.

实际上,你可以理解 创建实例的调用即是对构造器的调用

13.5.3 __new__() “构造器”方法

Python的构造器实例化不可变对象

13.5.14 __del__() “解构器”方法(问题)

Python的解构器是在实例释放前提供特殊处理功能的方法

13.6 实例属性

实例仅拥有数据属性,后者只是与某个类的实例相关联的数据值,并且可以通过句点属性标示法来访问

13.6.1 “实例化” 实例属性(或创建一个更好的构造器)

设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行

在构造器中首先设置实例属性

默认参数提供默认的实例安装

例,使用缺省参数进行实例化

定义个类来计算这个旅馆租房费用, __init__()构造器对一些实例属性进行初始化,calcTotal()方法用来决定是计算每日总的租房费用还是计算所有全部的租房费

# vi hotel.py

————————————

#!/usr/bin/env python

class HotelRoomCalc(object):

   ‘Hotel room rate calculator’

   def __init__(self, rt, sales=0.085, rm=0.1):

       ”’HotelRoomCalc default arguments:

       sales tax = 8.5% and room tax =10%”’

       self.salesTax = sales

       self.roomTax = rm

       self.roomRate = rt

   def calcTotal(self, days=1):

       ‘Calculate total;default to daily rate’

       daily = round((self.roomRate *(1 + self.roomTax + self.salesTax)),2)

       return float(days) * daily

sfo = HotelRoomCalc(299) # 新的实例

print sfo.calcTotal() # 日租金

print sfo.calcTotal(2) # 2天的租金

sea = HotelRoomCalc(189,0.086,0.058) # 新的实例

print sea.calcTotal()

print sea.calcTotal(4)

wasWkDay = HotelRoomCalc(169,0.045,0.02) # 新的实例

wasWkEnd = HotelRoomCalc(119,0.045,0.02) # 新的实例

print wasWkDay.calcTotal(5) + wasWkEnd.calcTotal()  # 7天的租金

————————————

# python hotel.py

—————————

354.31

708.62

216.22

864.88

1026.63

—————————-

__init__()应当返回None

采用函数操作符调用类对象会创建一个类实例,也就是说这样一种调用过程返回的对象就是实例

例:

>>> class MyClass(object):

…     pass

>>> mc = MyClass()

>>> mc

<__main__.MyClass object at 0x7f0adbce3710>

如果定义了构造器,就不应当返回任何对象,应为实例对象是自动在实例化调用后返回的,所以 __init__()就不应当放回任何对象,否则就会出现冲突,试着返回非None的任何其他对象都会导致TypeError异常:

>>> class MyClass:            

…     def __init__(self):

…             print “initialized”

…             return 1

>>> mc = MyClass()

initialized

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

TypeError: __init__() should return None

13.6.2 查看实例属性

内建函数dir()可以显示类属性,同时可以打印所有实例属性:

>>> class C(object):

…   pass

>>> c = C()        

>>> c.foo = ‘roger’

>>> c.bar = ‘shrubber’

>>> dir(c)

[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__doc__’, ‘__format__’, ‘__getattribute__’, ‘__hash__’, ‘__init__’, ‘__module__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘bar’, ‘foo’]

与类相似,实例也有一个__dict__特殊属性,他是实例属性构成的一个字典

>>> c.__dict__

{‘foo’: ‘roger’, ‘bar’: ‘shrubber’}

13.6.3 特殊的实例属性

1.__class__ 实例化1的类

1.__dict__   1的属性

现在使用类C及其实例C来看看这些特殊实例属性

>>> class C(object):

…   pass

>>> c = C()

>>> dir(c)

[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__doc__’, ‘__format__’, ‘__getattribute__’, ‘__hash__’, ‘__init__’, ‘__module__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’]

>>> c.__dict__

{}

>>> c.__class__

<class ‘__main__.C’>

添加一些属性:

>>> c.foo = 1

>>> c.bar = ‘SPAM’

>>> ‘%d can of %s please’ %(c.foo,c.bar)

‘1 can of SPAM please’

>>> c.__dict__

{‘foo’: 1, ‘bar’: ‘SPAM’}

13.6.4 内建类型属性:

内建类型也可以使用dir(),与任何其他对象一样,可以得到一个包含它属性名字的列表:

>>> x = 3 + 0.14j

>>> x.__class__  

<type ‘complex’>

>>> dir(x)

[‘__abs__’, ‘__add__’, ‘__class__’, ‘__coerce__’, ‘__delattr__’, ‘__div__’, ‘__divmod__’, ‘__doc__’, ‘__eq__’, ‘__float__’, ‘__floordiv__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getnewargs__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__int__’, ‘__le__’, ‘__long__’, ‘__lt__’, ‘__mod__’, ‘__mul__’, ‘__ne__’, ‘__neg__’, ‘__new__’, ‘__nonzero__’, ‘__pos__’, ‘__pow__’, ‘__radd__’, ‘__rdiv__’, ‘__rdivmod__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__rfloordiv__’, ‘__rmod__’, ‘__rmul__’, ‘__rpow__’, ‘__rsub__’, ‘__rtruediv__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__sub__’, ‘__subclasshook__’, ‘__truediv__’, ‘conjugate’, ‘imag’, ‘real’]

>>> [type (getattr(x,i)) for i in (‘conjugate’, ‘imag’, ‘real’)]

[<type ‘builtin_function_or_method’>, <type ‘float’>, <type ‘float’>]

>>> x.imag

0.14

>>> x.real

3.0

>>> x.conjugate()

(3-0.14j)

访问__dict__会失败,因为在内建类型中,不存在这个属性:

>>> x.__dict__

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

AttributeError: ‘complex’ object has no attribute ‘__dict__’

13.6.5 实例属性vs类属性

访问类属性

类属性可通过类或实例来访问

>>> class C(object): # 定义类

…   version = 1.2 # 静态成员

>>> c = C() # 实例化

>>> C.version # 通过类访问

1.2

>>> C.version += 0.1 # 通过类来更新

>>> C.version # 类访问

1.3

>>> c.version # 实例访问它,其值已被改变

1.3

从实例中访问类属性须谨慎

>>> class Foo(object):

…   x = 1.5

>>> foo = Foo()

>>> foo.x

1.5

>>> foo.x =1.7 #尝试更新类属性

>>> foo.x # 看起来不错

1.7

>>> Foo.x # 没变,只是创建了一个新的实例属性

1.5

删除这个新的version实例

>>> del foo.x # 删除实例属性

>>> foo.x # 又可以访问到类属性

1.5

更新类属性

>>> foo.x += .2 # 试着增加类属性

>>> foo.x

1.7

>>> Foo.x # 照旧

1.5

注:赋值语句右边的表达式计算出原类的变量,增加0.2,并且把这个值赋给新创建的实例对象,以上更新等价于下面的赋值方式

foo.x = Foo.x +0.2

在类属性可变情况下:

>>> class Foo(object):

…   x = {2003: ‘poe2’}

>>> foo = Foo()

>>> foo.x

{2003: ‘poe2’}

>>> foo.x[2004] = ‘valid path’

>>> foo.x

{2003: ‘poe2’, 2004: ‘valid path’}

>>> Foo.x # 生效了

{2003: ‘poe2’, 2004: ‘valid path’}

>>> del foo.x # 没有遮蔽所以不能删除掉

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

AttributeError: ‘Foo’ object attribute ‘x’ is read-only

类属性持久性:

当一个实例在类属性被修改后才创建,那么更新的值就将生效,类属性的修改该会影响所有的实例:

>>> class C(object):

…   spam = 100 # 类属性

>>> c1 = C() # 创建一个实例

>>> c1.spam # 通过实例访问类属性

100

>>> C.spam += 100 # 更新类属性

>>> C.spam # 查看属性值改变

200

>>> c1.spam # 在实例中验证属性值改变

200

>>> c2 = C() # 创建另一个实例

>>> c2.spam # 验证类属性

200

>>> del c1 # 删除一个实例

>>> C.spam

200

>>> C.spam += 200 # 再次更新类属性

>>> c2.spam # 验证那个属性值改变

400

13.7 从这里开始校对—–绑定和方法调用

方法仅仅是类内部定义的函数,这意味着方法是类属性而不是实例属性

其次方法只有在其所属的类拥有实例时,才被调用.

最后任何一个方法定义中的异地个参数都是变量self,它表示调用此方法的实例

对象

注:self变量用于在类实例方法中引用方法所绑定的实例,因为方法的实例在任何方法调用中总是作为第一个参数传递的,self被选中用来代表实例

13.7.1 调用绑定方法:

13.7.2 调用非绑定方法:

class EmplAddrBookEntry(AddrBookEntry):

  ‘Employee Address Book Entry class’

 def __init__(self,nm,ph,em):

   AddrBookEntry.__init__(self,nm,ph)

   self.empid = id

   self.email = em

EmplAddrBookEntry是AddrBookEntry的子类,我们重载了构造器__init__(),我们想尽可能多的重用代码,而不是从父类构造器中剪贴,黏贴代码

13.8 静态方法和类方法:

静态方法仅仅是类中的函数

13.8.1 staticmethod()和classmethod()内建函数:

在经典类中创建静态方法和类方法的一些例子:

>>> class TestStaticMethod:

…   def foo():

…     print ‘calling static method foo()’

…   foo = staticmethod(foo)

>>> class TestClassMethod:

…   def foo(cls):

…     print ‘calling class method foo()’

…     print ‘foo() is part of class: ‘, cls.__name__

…   foo = classmethod(foo)

对应的内建函数被转换成他们相应的类型,并且重新赋值给列相同的变量名,如果没有调用这两个函数,两者都会在Python编译器中产生错误,显示需要带self的常规方法声明,现在我们可以通过类或者实例调用这些函数:

>>> tsm = TestStaticMethod()

>>> TestStaticMethod().foo()

calling static method foo()

>>> tsm.foo()

calling static method foo()

>>>

>>> tcm = TestClassMethod()

>>> TestClassMethod.foo()

calling class method foo()

foo() is part of class:  TestClassMethod

>>> tcm.foo()

calling class method foo()

foo() is part of class:  TestClassMethod

13.8.2 使用函数修饰符:

替代statcimethod()使用函数修饰符

>>> class TestStaticMethod:

…   @staticmethod

…   def foo():

…     print ‘calling static method foo()’

>>> class TestClassMethod:

…   @classmethod

…   def foo(cls):

…     print ‘calling class method foo()’

…     print ‘foo() is part of class:’, cls.__name__

>>> TestClassMethod.foo()

calling class method foo()

foo() is part of class: TestClassMethod

>>> TestStaticMethod.foo()

calling static method foo()

13.9 组合

>>> class NewAddrBookEntry(object):     # 类定义

…   ‘new address book entry class’  

…   def __init__(self,nm,ph):# 定义构造器

…     self.name = Name(nm) # 创建Name实例

…     self.phone =Phone(ph)# 创建Phone实例

…     print ‘Created instance for:’,self.name

13.10 子类的派生:

13.10.1 创建子类:

>>> class Parent(object):# 定义父类

…   def parentMethod(self):

…     print ‘calling parent method’

>>> class Child(Parent):# 定义子类

…   def childMethod(self):

…     print ‘calling child method’

>>> p = Parent()# 父类的实例

>>> p.parentMethod()

calling parent method

>>> c = Child()# 子类的实例

>>> c.childMethod()#子类调用它的方法

calling child method

>>> c.parentMethod()#调用父类的方法

calling parent method

13.11 继承:

继承描述了基类属性如何”遗传”给派生类,一个子类可以继承它的基类任何属性

例:

>>> class P(object):# 父类

…   pass          

>>> class C(P):# 子类    

…   pass    

>>> c = C()# 实例化子类

>>> c.__class__# 子类”是一个”父类

<class ‘__main__.C’>

>>> C.__bases__# 子类的父类

(<class ‘__main__.P’>,)

因为P没有属性,C没有继承到什么,下面我们给P添加一些属性:

>>> class P:#父类

…   ‘p class’

…   def __init__(self):

…     print ‘created an instance of’,self.__class__.__name__

>>> class C(P):#子类

…   pass

现在所创建的P有文档字符串(__doc__)和构造器,当我们实例化P时,他被执行

>>> p = P()    # 父类实例

created an instance of P

>>> p.__class__ # 显示p所属类名

<class __main__.P at 0x8b281dc>

>>> P.__bases__ # 父类的父类

()

>>> P.__doc__  # 父类的文档字符串

‘p class’

“created an instance” 是由__init__()直接输出的

现在我们实例化C

>>> c = C() # 子类实例

created an instance of C

>>> c.__class__ # 显示c所属类名

<class __main__.C at 0x8b2823c>

>>> C.__bases__ # 子类的父类

(<class __main__.P at 0x8b281dc>,)

>>> C.__doc__ # 子类的文档字符串

C没有声明 __init__()方法,然而在类C的实例c被创建时,还是会有输出信息,原因在于C继承了P的__init__().

13.11.1 __bases__类属性

父类是相对所有基类(它包括了所有祖先类) 那些没有父类的类,__bases属性为空

>>> class A(object): pass      

>>> class B(A): pass    

>>> class C(B): pass    

>>> class D(B, A): pass  

>>> A.__bases__

(<type ‘object’>,)

>>> C.__bases__

(<class ‘__main__.B’>,)

>>> D.__bases__

(<class ‘__main__.B’>, <class ‘__main__.A’>)

13.11.2 通过继承覆盖(Overriding)方法

我们在P中再写一个函数,然后再其子类中对它进行覆盖

>>> class P(object):

…   def foo(self):

…     print ‘Hi,I am P-foo()’

>>> p = P()

>>> p.foo()

Hi,I am P-foo()

现在来创建子类C,从父类P派生:

>>> class C(P):

…   def foo(self):

…     print ‘Hi,I am C-foo()’

>>> c = C()

>>> c.foo()

Hi,I am C-foo()

尽管C继承了P的foo()方法,但因为C定义了它自己的foo()方法,所以P中的foo()方法被覆盖

如何不被覆盖而是用父类的foo()方法:

调用一个未绑定的基类方法,明确给出子类的实例:

>>> P.foo(c)

Hi,I am P-foo()

在子类的重写方法里显式地调用基类方法:

>>> class C(P):

…   def foo(self):

…     P.foo(self)

…     print ‘Hi,I am C-foo()’

>>> c = C()

>>> c.foo()

Hi,I am P-foo()

Hi,I am C-foo()

使用super()内建方法:

>>> class C(P):

…   def foo(self):

…     super(C, self).foo()

…     print ‘Hi,I am C-foo()’

super()不但能找到基类方法,而且还为我们传进self,这样我们只需要调用子类方法,他会帮你完成一切:

>>> c = C()

>>> c.foo()

Hi,I am P-foo()

Hi,I am C-foo()

注: 重写__init__不会自动调用基类的__init__

>>> class P(object):                  

…   def __init__(self):

…     print “calling P’s constructor”

>>> class C(P):

…   def __init__(self):

…     print “calling C’s constructor”

>>> c = C()

calling C’s constructor

如果你还想调用基类的__init__(),你需要使用一个子类的实例去调用基类(未绑定)方法,相应的更新类C

>>> class C(P):

…   def __init__(self):

…     P.__init__(self)

…     print “calling C’s constructor”

>>> c = C()

calling P’s constructor

calling C’s constructor

使用super()内建函数调用基类__init__()

>>> class C(P):

…   def __init__(self):

…     super(C, self).__init__()

…     print “calling C’s constructor”

>>> c = C()

calling P’s constructor

calling C’s constructor

13.11.3 从标准类型派生:

不可变类型的例子:

假定你想在金融应用中,应用一个处理浮点数的子类,每次你得到一个货币值,你都需要通过四舍五入,变为带两位小数位的数字:

>>> class RoundFloat(float):

…   def __new__(cls,val):

…     return float.__new__(cls, round(val,2))

>>> RoundFloat(1.5955)

1.6

使用内建函数super()去捕获对应的父类以调用它的__new__方法

>>> class RoundFloat(float):

…   def __new__(cls,val):

…     return super(RoundFloat,cls).__new__(cls, round(val,2))

>>> RoundFloat(1.5955)

1.6

>>> RoundFloat(1.5945)  

1.59

>>> RoundFloat(-1.9955)

-2.0

可变类型的例子:

创建一个新的字典类型,它的keys()方法会自动排序结果:

>>> class SortedKeyDict(dict):

…   def keys(self):

…     return sorted(super(SortedKeyDict, self).keys())

>>> d = SortedKeyDict(((‘zheng-cai’,67),(‘hui-jun’,68),(‘xin-yi’,2)))

>>> print ‘By iterator:’.ljust(12), [key for key in d]

By iterator: [‘zheng-cai’, ‘xin-yi’, ‘hui-jun’]

>>> print ‘By iterator:’.ljust(12), d.keys()

By iterator: [‘hui-jun’, ‘xin-yi’, ‘zheng-cai’]

注:

ljust用法:string.ljust(s,width[,fillchar])

意思就是如果你的字符串本来长度是5,我要把它变成长度为40,而且可以用字符填充。

>>> import string

>>> s=”hello”

>>> string.ljust(s,40)

‘hello                                   ‘

>>> string.ljust(s,40,’x’)

‘helloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’

如果调用super()过于复杂, 取而代之:

def keys(self):

  return sorted(self.keys())

13.11.4 多重继承

>>> class P1:# 父类1        

…   def foo(self):          

…     print ‘called P1-foo()’

>>> class P2:# 父类2

…   def foo(self):

…     print ‘called P2-foo()’

…   def bar(self):

…     print ‘called P2-bar()’

>>> class C1(P1,P2):#子类1,从P1,P2派生

…   pass

>>> class C2(P1,P2):#子类2,从P1,P2派生

…   def bar(self):

…     print ‘called C2-bar()’

>>> class GC(C1,C2):#定义子孙类

…   pass#从C1,C2派生

图:父类,子类及子孙类的关系图,还有他们各自定义的方法:

(foo)P1     P2(foo,bar)

     \    /

      \  /

       \/

       /\

      /  \

     /    \

    C1     C2(bar)

    \      /

     \    /

      \  /

       \/

       GC

经典类:

>>> gc = GC()

>>> gc.foo()#GC ==> C1 ==> P1

called P1-foo()

>>> gc.bar()    #GC ==> C1 ==> P1 ==> P2

called P2-bar()

当调用foo()时,它首先在当前类(GC)中查找,如果没找到,就像上查找最亲的父类,C1,查找未遂,就继续沿树上访到父类P1,foo()被找到.

对bar()来说,它通过搜索GC,C1,P1然后在P2中找到,因为使用这种解释顺序,C2.bar()根本就不会被搜索

如果需要调用C2的bar()方法,必须调用它的合法全名,采用典型的非绑定方式调用,并且提供一个合法实例:

>>> C2.bar(gc)

called C2-bar()

新式类:

>>> class P1(object):  

…   def foo(self):

…     print ‘called P1-foo()’

>>> class P2(object):

…   def foo(self):

…     print ‘called P2-foo()’

…   def bar(self):

…     print ‘called P2-bar()’

>>> class C1(P1,P2):

…   pass

>>> class C2(P1,P2):

…   def bar(self):

…     print ‘called C2-bar()’

>>> class GC(C1, C2):

…   pass

>>> gc = GC()

>>> gc.foo()# GC ==> C1 ==> C2 ==>P1

called P1-foo()

>>> gc.bar()    # GC ==> C1 ==> C2

called C2-bar()

与沿着继承树一步一步上溯不同,它首先查找同胞兄弟,采用广度优先方式,当查找foo(),它检查GC,然后是C1和C2,然后在P1中找到,如果P1中没有,查找将会到达P2

然而bar(),它搜索GC和C1,紧接着在C2中找到了,这样就不会再继续搜索到祖父

P1和P2

新式类也有一个__mro__属性,告诉你查找顺序是怎样的:

>>> GC.__mro__

(<class ‘__main__.GC’>, <class ‘__main__.C1’>, <class ‘__main__.C2’>, <class ‘__main__.P1’>, <class ‘__main__.P2’>, <type ‘object’>)

13.12 类,实例和其他对象的内建函数:

13.12.1 issubclass()

issubclass() 布尔函数判断一个类是另一个类的子类或子孙类

issubclass(sub, sup)

13.12.2 isinstance()

isinstance()布尔函数判断一个对象是否是另一个给定类的实例:

isinstance(obj1,obj2)

isinstance()在obj1是obj2的一个实例,或者是obj2的子类的一个实例时,返回True,反之False

>>> class C1(object): pass

>>> class C2(object): pass

>>> c1 = C1()

>>> c2 = C2()

>>> isinstance(c1,C1)

True

>>> isinstance(c2,C1)

False

>>> isinstance(c1,C2)

False

>>> isinstance(c2,C2)

True

>>> isinstance(C2,c2)

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

注:第二个参数应当是类,不然会得到一个TypeError

但如果第二个参数是一个类型的对象,则不会出现异常,因为你可以使用instance()来检查一个对象obje1是否是obj2的类型:

>>> isinstance(4,int)

True

>>> isinstance(4,str)

False

>>> isinstance(‘4’,str)

True

13.12.3 hasattr(),getattr(),setattr(),delattr()

hasattr()函数是布尔型的,判断一个对象是否有一个特定的属性,一般用于访问某属性前先作一下检查

getattr()和setattr()为取得和赋值给对象的属性

delattr()函数会从一个对象中删除属性

>>> class myClass(object):

…   def __init__(self):

…     self.foo = 100

>>> myInst = myClass()

>>> hasattr(myInst, ‘foo’)

True

>>> getattr(myInst, ‘foo’)  

100

>>> hasattr(myInst, ‘bar’)  

False

>>> getattr(myInst, ‘bar’)  

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

AttributeError: ‘myClass’ object has no attribute ‘bar’

>>> setattr(myInst, ‘bar’,’my attr’)

>>> getattr(myInst,’bar’)

‘my attr’

>>> delattr(myInst, ‘foo’)            

>>> dir(myInst)

[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__doc__’, ‘__format__’, ‘__getattribute__’, ‘__hash__’, ‘__init__’, ‘__module__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘bar’]

>>> hasattr(myInst,’foo’)

False

13.12.4 dir()

dir()作用在实例上(经典类或新式类),显示实例变量,以及在实例所在的类及所有它的基类中定义的方法和类属性

dir()作用在类上,显示类以及他所有基类的__dict__中的内容,但不会显示定义在元类(metaclass)中的类属性

dir()作用的模块上,显示模块的__dict__内容

dir()不带参数,显示调用者的局部变量

13.12.5 super()

super()函数目的就是帮助找出相应的父类,方便调用相关属性

super(type[, obj])

super()的主要用途是用来查找父类属性:

例如:

super(MyClass,self).__init__()

13.12.6 vars()

vars()与dir()相似,vars()返回一个字典,它包含了对象存储于其__dict__中的属性(键)及值,如果提供的对象没有这个属性,则会引发TypeError异常,如果没有提供对象作为vars()的一个参数,他将显示一个包含本地名字空间的属性(键)及其值的字典,也就是locals()

>>> class C(object):

…   pass

>>> c = C()

>>> c.foo = 100

>>> c.bar = ‘Python’

>>> c.__dict__

{‘foo’: 100, ‘bar’: ‘Python’}

>>> vars(c)

{‘foo’: 100, ‘bar’: ‘Python’}

13.13 用特殊方法定制类:

13.13.1 简单定制(RoundFloat2)

>>> class RoundFloatManual(object):

…   def __init__(self,val):

…     assert isinstance(val, float), “Value must be a float!”

…     self.value = round(val,2)

这个类仅接收一个浮点值,并且将其保存为实例属性

>>> rfm = RoundFloatManual(42)

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

 File “<stdin>”, line 3, in __init__

AssertionError: Value must be a float!

>>> rfm = RoundFloatManual(4.2)

>>> rfm

<__main__.RoundFloatManual object at 0x889064c>

>>> print rfm

<__main__.RoundFloatManual object at 0x889064c>

当你想显示你的对象,实际上是想看到有意义的东西,而不仅仅是通常的Python对象字符串

添加一个__str()__方法,以覆盖默认的行为

>>> 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 str(self.value)

>>> rfm = RoundFloatManual(5.590464)

>>> rfm

<__main__.RoundFloatManual object at 0x889082c>

>>> print rfm

5.59

让__repr__()作为__str__()的一个别名,实现在四舍五入为5.6的基础上还想保留两位小数,这里就同时具备str()和repr()的输出:

>>> 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__

>>> rfm = RoundFloatManual(5.5964)

>>> rfm

<__main__.RoundFloatManual object at 0x8890a4c>

>>> print rfm

5.60

13.13.2 数值定制(Time60)

显示:

>>> class Time60(object):

…   def __init__(self, hr, min):

…     self.hr = hr

…     self.min = min

…   def __str__(self):

…     return ‘%d:%d’ % (self.hr, self.min)

用此类,可以实例化一些对象,在下面的例子,我们启动一个工时表来跟踪对应构造器的计费小时数:

>>> mon = Time60(10, 30)

>>> tue = Time60(11, 15)    

>>> print mon, tue

10:30 11:15

加法:

Python的重载操作符很简单,像加号(+),我们只需要重载__add__()方法,创建另一个对象并填入计算出来的总数:

>>> class Time60(object):                                              

…   def __init__(self, hr, min):                                    

…     self.hr = hr                                                  

…     self.min = min                                                

…   def __str__(self):

…     return ‘%d:%d’ % (self.hr,self.min)

…   def __add__(self, other):

…     return self.__class__(self.hr + other.hr, self.min + other.min)

>>> mon = Time60(10,30)

>>> tue = Time60(11,15)

>>> mon + tue

<__main__.Time60 object at 0x7f717b544c10>

>>> print mon + tue

21:45

原位加法:

__iadd__()用来支持像mon += tue这样的操作符,并把正确的结果赋给mon

>>> class Time60(object):                                

…   def __init__(self, hr, min):                                

…     self.hr = hr                      

…     self.min = min                    

…   def __str__(self):                  

…     return ‘%d:%d’ % (self.hr,self.min)

…   __repr__ = __str__                                

…   def __iadd__(self, other):

…     self.hr += other.hr

…     self.min += other.min

…     return self

>>> mon = Time60(10,30)

>>> tue = Time60(11,15)

>>> mon

10:30

>>> id(mon)

140125377154768

>>> mon += tue

>>> id(mon)

140125377154768

>>> mon

21:45

随机序列迭代器

# vi randSeq.py

————————–

#!/usr/bin/env python

from random import choice

class RandSeq(object):

 def __init__(self, seq):

       self.data = seq

 def __iter__(self):

       return self

 def next(self):

       return choice(self.data)

r = RandSeq([1,2,3])

print r.next()              

————————–

# python randSeq.py

1

# python randSeq.py

2

升华:

中级定制:

# vi Time60.py

————————–

#!/usr/bin/env python

class Time60(object):

  def __init__(self, hr, min):

    self.hr = hr

    self.min = min

  def __str__(self):

    return ‘%d:%d’ % (self.hr, self.min)

  __repr__ =  __str__

  def __add__(self,other):

    return self.__class__(self.hr + other.hr, self.min + other.min)

  def __iadd__(self, other):

    self.hr += other.hr

    self.min += other.min

    return self

wed = Time60(12,5)

thu = Time60(10,30)

fri = Time60(8,45)

print wed

print (thu + fri)

—————————

# python Time60.py

12:5

18:75

13.13.3 迭代器(RandSeq和AnyIter)

RandSeq

from randseq import RandSeq

for eachItem in RandSeq((‘rock’,’paper’,’scissors’)):

  print eachItem

例:任意项的迭代器

# vi anyIter.py

—————————–

#!/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())

         except StopIteration:

               if self.safe:

                 break

               else:

                 raise

       return retval

A = AnyIter([1,2,3])

print A.next(3)

a = AnyIter(range(10))

i = iter(a)

for j in range(1,5):

 print j,’:’,i.next(j)

—————————–

# python anyIter.py

[1, 2, 3]

1 : [0]

2 : [1, 2]

3 : [3, 4, 5]

4 : [6, 7, 8, 9]

因为超出了项的支持量,默认safe=False,所以出现了StopIteration异常

>>>a = AnyIter(range(10))

>>>i =iter(a)

>>>i.next(14)

File “anyIter.py”, line 13, in next

   retval.append(self.iter.next())

StopIteration

如果我们使用”安全(safe)”模式重建迭代器,则会在项失控前得到迭代器所得到的元素

>>>a = AnyIter(range(10),True)

>>>i =iter(a)

>>>print i.next(14)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

13.13.4 多类型定制(NumStr)

初始化,加法,乘法,False值,比较

例:

# vi numstr.py

————————-

#!/usr/bin/env python

class NumStr(object):

   def __init__(self, num=0,string=”):

       self.__num = num

       self.__string = string

   def __str__(self):

       return ‘[%d :: %r]’ % (self.__num,self.__string)

   def __add__(self,other):

       if isinstance(other,NumStr):

           return self.__class__(self.__num + other.__num, \

           self.__string + other.__string)

       else:

           raise TypeError, \

           ‘IIIegal argument type for built-in operation’

   def __mul__(self,num):

       if isinstance(num,int):

           return self.__class__(self.__num * num, self.__string * num)

       else:

           raise TypeError, \

           ‘IIIegal argument type for built-in operation’

   def __nonzero__(self):

       return self.__num or len(self.__string)

   def __norm_cval(self, cmpres):

       return cmp(cmpres,0)

   def __cmp__(self, other):

       return self.__norm_cval(cmp(self.__num,other.__num)) + \

       self.__norm_cval(cmp(self.__string,other.__string))

a = NumStr(3, ‘foo’)

b = NumStr(3, ‘goo’)

c = NumStr(2, ‘foo’)

d = NumStr()

e = NumStr(string=’boo’)

f = NumStr(1)

print a

print b

print c

print d

print e

print f

print a < b

print b < c

print a == a

print b * 2

print a * 3

print b + e

print e + b

if d: print ‘not false’

if e: print ‘not false’

print cmp(a,b)

print cmp(a,c)

print cmp(a,a)

—————————

# python numstr.py

[3 :: ‘foo’]

[3 :: ‘goo’]

[2 :: ‘foo’]

[0 :: ”]

[0 :: ‘boo’]

True

False

True

[6 :: ‘googoo’]

[9 :: ‘foofoofoo’]

[3 :: ‘gooboo’]

[3 :: ‘boogoo’]

not false

-1

1

0

13.14 私有化

默认情况下,属性在Python中都是”public”,类所在模块和导入了没所在模块的其他模块的代码都可以访问到

双下划线(__)

单下划线(_)

13.15 授权

13.15.1 包装

包装在Python中意思是对一个已存在的对象进行包装,增加新的,删除不要的,或者修改其他已存在的功能.

13.15.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)

注:属性可以是数据属性,还可以是函数或者方法

>>> w = WrapMe(3.5+4.2j)

>>> w

self.__data

>>> print w

(3.5+4.2j)

>>> print w.real

3.5

>>> print w.imag

4.2

>>> print w.conjugate()

(3.5-4.2j)

>>> print w.get

<bound method WrapMe.get of self.__data>

>>> print w.get()

(3.5+4.2j)

使用列表对象执行多种操作:

>>> wm = WrapMe([123, ‘foo’, 45.67])

>>> wm.append(‘bar’)

>>> wm.append(‘123’)

>>> print wm

[123, ‘foo’, 45.67, ‘bar’, ‘123’]

>>> print wm.index(45.67)

2

>>> print wm.count(123)

1

>>> print wm.pop()

123

>>> print wm

[123, ‘foo’, 45.67, ‘bar’]

>>> realList = wm.get()

>>> realList[3]

‘bar’

>>> realList = wm.get()[3]

>>> realList

‘bar’

get()方法返回一个对象,随后被索引以得到切片片段

>>> f= WrapMe(open(‘/etc/motd’))

>>> f

self.__data

>>> f.get()

<open file ‘/etc/motd’, mode ‘r’ at 0x9fa0c28>

>>> f.readline()

‘Welcome to Ubuntu 12.04.3 LTS (GNU/Linux 3.8.0-29-generic i686)\n’

>>> f.tell()

64L

>>> f.seek(0)

>>> print f.readline()

Welcome to Ubuntu 12.04.3 LTS (GNU/Linux 3.8.0-29-generic i686)

>>> f.close()

>>> f.get()

<closed file ‘/etc/motd’, mode ‘r’ at 0x9fa0c28>

>>> print “<%s file %s,mode %s at %x>” % \

… (f.closed and ‘closed’ or ‘open’, ‘f.name’,’f.mode’,id(f.get()))

<closed file f.name,mode f.mode at 9fa0c28>

更新简单的包裹类:

例:类定义包装了任何内建类型,增加时间属性:get(),set(),还有字符串表示的方法,并授权所有保留的属性,访问这些标准类型:

# vi twrapme.py

—————————–

#!/usr/bin/env python

from time import time, ctime

class TimedWrapMe(object):

   def __init__(self, obj):

       self.__data = obj

       self.__ctime = self.__mtime = \

       self.__atime = time()

   def get(self):

       self.__atime = time()

       return self.__data

   def gettimeval(self, t_type):

       if not isinstance(t_type,str) or \

           t_type[0] not in ‘cma’:

           raise TypeError, \

               “argument of ‘c’,’m’,or ‘a’ req’d”

       return getattr(self, ‘_%s__%stime’ % \

           (self.__class__.__name__,t_type[0]))

   def gettimestr(self, t_type):

       return ctime(self.gettimeval(t_type))

   def set(self, obj):

       self.__data = obj

       self.__mtime = self.__atime = time()

   def __repr__(self):

       self.__atime =time()

       return ‘self.__data ‘

   def __str__(self):

       self.__atime = time()

       return str(self.__data)

   def __getattr__(self, attr):

       self.__atime = time()

       return getattr(self.__data,attr)

t = TimedWrapMe(932)

print t.gettimestr(‘c’)

print t.gettimestr(‘m’)

print t.gettimestr(‘a’)

print t

t.set(‘time is up!’)

print t.gettimestr(‘m’)

print t

—————————–

改进包装一个特殊对象:

例:包装文件对象:(问题)

# vi capOpen.py

——————————-

#!/usr/bin/env python

class CapOpen(object):

   def __init__(self,fn,mode=’r’,buf=-1):

       self.file = open(fn,mode,buf)

   def __str__(self):

       return str(self.file)

   def __repr__(self):

       return ‘self.file’

   def write(self, line):

       self.file.write(line.upper())

   def __getattr__(self,attr):

       return getattr(self.file, attr)

f = CapOpen(‘/tmp/1′,’r’)

for eachLine in f:

   print eachLine,

——————————-

13.16 新式类的高级特性(Python 2.2+)

13.16.1 新式类的通用特性

这些特性中最重要的是能够子类化Python数据类型

下面是内建函数:

int(),long(),float(),complex()

str(),unicode()

list(),tuple()

type()

新的函数:

basestring()

dict()

bool()

set(), frozenset()

object()

classmethod()

staticmethod()

super()

property()

file()

13.16.2 __slots__类属性:

__dict__属性跟踪所有实例属性,假如你有一个实例inst,它有一个属性foo,那使用inst.foo来访问它与使用inst.__dict__[‘foo’]来访问是一致的

字典会占据大量内存,如果你有一个属性数量很少的类,但有很多实例,那么正好是这种情况,为内存上的考虑,用户现在可以使用__slots__属性来替代__dict__

__slots__是一个类变量,由一序列型对象组成,由所有合法标识构成的实例属性的集合来标识,它可以是一个列表,元祖或可迭代对象,也可以是标识实例能拥有的唯一的属性的简单字符串,任何创建一个其名不在__slots__中的名字的实例属性都会导致AttibuteError异常:

>>> class SlottedClass(object):

…   __slot__ = (‘foo’, ‘bar’)

>>> c = SlottedClass()

>>> c.foo = 42

>>> c.xxx = “don’t think so”

13.16.3 特殊方法__getattribute__()

Python类有一个名为__getattr__()的特殊方法,它仅当属性不能再实例的__dict__或它的类(类的__dict__),或者祖先类(其__dict__)中找到时,才被调用

13.16.4  描述符

描述符是标识对象属性的一个代理,当需要属性时,可根据你遇到的情况,通过描述符或采用常规方式来访问它

__get__(),__set__(),__delete__()特殊方法

__getattribute__()特殊方法(二)

优先级别

例1:

———————————–

>>> class DevNulll(object):

…   def __get__(self, obj, typ=None):

…     pass

…   def __set__(self, obj, val):

…     pass

———————————–

我们建立一个类,这个类使用了这个描述符,给它复制并显示其值:

————————————-

>>> class C1(object):

…   foo = DevNulll()

>>> c1 = C1()

>>> c1.foo = ‘bar’

>>> print ‘c1.foo contains:’, cl.foo

c1.foo contains: None

————————————–

例2,在描述符中写一些输出语句

————————————–

>>> class DevNull2(object):

…   def __get__(self, obj,typ=None):

…     print ‘Accessing attribute… ignoring’

…   def __set__(self, obj,val):

…     print ‘Attempt to assign %r… ignoring’ %(val)

————————————–

修改后的结果

————————————-

>>> class C2(object):

…   foo = DevNull2()

>>> c2 = C2()

>>> c2.foo = ‘bar’

Attempt to assign ‘bar’… ignoring

>>> x = c2.foo

Accessing attribute… ignoring

>>> print ‘c2.foo contains:’,x

c2.foo contains: None

————————————-

例3,我们在描述符所在的类中添加一个占位符,占位符包含有关于这个描述符的有用信息:

—————————————–

>>> class DevNull3(object):

…   def __init__(self, name = None):

…     self.name = name

…   def __get__(self, obj,typ=None):

…     print ‘Accessing [%s]… ignoring’ %(self.name)

…   def __set__(self, obj, val):

…     print ‘Assining %r to [%s]… ignoring’ %(val, self.name)

—————————————–

修改该后的结果:

——————————————-

>>> class C3(object):

…   foo = DevNull3(‘foo’)

>>> c3 = C3()

>>> c3.foo = ‘bar’

Assining ‘bar’ to [foo]… ignoring

>>> x = c3.foo

Accessing [foo]… ignoring

>>> print ‘c3.foo contains:’, x

c3.foo contains: None

>>> print ‘Let us try to sneak it into c3 instance…’

Let us try to sneak it into c3 instance…

>>> c3.__dict__[‘foo’] = ‘bar’

>>> x = c3.foo

Accessing [foo]… ignoring

>>> print ‘c3.foo contains:’, x

c3.foo contains: None

>>> print “c3.__dict__[‘foo’] contains: %r” % \

… c3.__dict__[‘foo’], “… why?!?”

c3.__dict__[‘foo’] contains: ‘bar’ … why?!?

——————————————-

例4:由于实例属性比非数据描述符的优先级高,你可以将非数据描述符隐藏,这就和你给一个实例属性复制,将对应类的同名属性隐藏起来是一个道理:

——————————————-

>>> class FooFoo(object):

…   def foo(self):

…     print ‘Very important foo() method’

>>> bar = FooFoo()

>>> bar.foo()

Very important foo() method

>>> bar.foo = ‘It is no longer here.’

>>> bar.foo

‘It is no longer here.’

>>>

>>> del bar.foo

>>> bar.foo()

Very important foo() method

——————————————-

例5,我们将foo做为一个函数调用,然后又将她作为一个字符串访问,但我们也可以使用另一个函数,而且保持相同的调用机制:

——————————————–

>>> def barBar():

…   print ‘foo() hidden by barBar()’

>>> bar.foo = barBar

>>> bar.foo()

foo() hidden by barBar()

>>> del bar.foo

>>> bar.foo()

Very important foo() method

——————————————–

例: 使用文件来存储属性

# vi descr.py

——————————————

#!/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 AttributeError, \

           “%r used before assignment” % self.name

       try:

           f = open (self.name, ‘r’)

           val = pickle.load(f)

           f.close()

           return val

       except(pickle,InpicklingError, IOError,\

               EOFError, AttributeError, \

               ImportError,IndexError),e:

           raise AttributeError, \

               “could not read %r: %s” % self.name

   def __set__(self, obj, val):

       f = open(self.name, ‘w’)

       try:

           try:

               pickle.dump(val, f)

               FileDescr.saved.append(self.name)

           except (TypeError, pickle.PicklingError),e:

               raise AttributeError, \

               “could not pickle %r” % self.name

       finally:

           f.close()

   def __delete__(self, obj):

       try:

           os.unlink(self.name)

           FileDe

scr.saved.remove(self.name)

       except (OSError,ValueError),e:

           pass

class MyFileVarClass(object):

   foo = FileDescr(‘foo’)

   bar = FileDescr(‘bar’)

fvc = MyFileVarClass()

#print fvc.foo # 会报错

fvc.foo = 42

fvc.bar = ‘leanna’

print fvc.foo, fvc.bar

del fvc.foo

#print fvc.foo, fvc.bar # 会报错

——————————————

描述符总结

属性和property()内建函数

property()内建函数有四个参数:

property(fget=None, fset=None, fdel=None, doc=None)

property()用法是将它卸载一个类定义中,property()接受一些传进来的函数作为参数,实际上,property()实在它所在的类被创建时被调用的,这些传进来的方法是非绑定的,所以这些方法其实就是函数.

下面的一个例子:

在类中建立一个只读的整数属性,用逐位异或操作符将它隐藏起来:

>>> class ProtectAndHideX(object):  

…   def __init__(self,x):        

…     assert isinstance(x, int), \

…     ‘”x” must be an integer!’  

…     self.__x = ~x              

…   def get_x(self):                

…     return ~self.__x                

…   x = property(get_x)

它只保存我们第一次给出的值,而不允许我们对它做第二次修改:

>>> inst = ProtectAndHideX(‘foo’)

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

 File “<stdin>”, line 4, in __init__

AssertionError: “x” must be an integer!

>>> inst = ProtectAndHideX(10)  

>>> print ‘inst.x = ‘, inst.x  

inst.x =  10

>>> inst.x = 20

Traceback (most recent call last):

 File “<stdin>”, line 1, in <module>

AttributeError: can’t set attribute

另一个关于setter的例子:

>>> class HideX(object):            

…   def __init__(self,x):        

…     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)

本示例的输出结果:

>>> inst = HideX(20)

>>> print inst.x

20

>>> inst = HideX(30)

>>> print inst.x    

30

>>> inst.set_x(40)

>>> print inst.x  

40

属性成功保存到x中并显示出来,是因为在调用构造器给x赋初始值前,在getter中已经将~x赋给了self.__x.

例:给属性添加一个文档字符串

>>> from math import pi

>>> def get_pi(dummy):

…   return pl

>>> class PI(object):

…   pi = property(get_pi, doc=’Constant “pi”‘)  

我们在perperty中使用一个函数,调用函数时self作为第一个参数被传入,所以我们必须加一个伪变量把self丢弃,下面是本例的输出:

>>> inst = PI()                                

>>> inst.pi                                    

3.141592653589793

>>> print PI.pi.__doc__

Constant “pi”

你不必写一个描述符类,只要把你写的函数(或方法)全部传递给property()就可

以了

通过使用属性property()来访问属性:

>>> class HideX(object):

…   def __init__(self,x):

…     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

…     return locals()

13.16.5 元类和__metaclass__

元类(Metaclasses)

元类让你来定义某个类是如何被创建的,从根本上说,赋予你如何创建类的控制权.你可以把元类想成一个类中类,或者类,它的实例时其他的类

>>> class C(object):

…   pass

>>> class CC:

…   pass

>>> type(C)

<type ‘type’>

>>> type(CC)

<type ‘classobj’>

>>> import types

>>> type(CC) is types.ClassType

True

什么时候使用元类?

元类一般用于创建类,在执行类定义时,解释器必须要知道这个类的正确的元类

解释器会先寻找类属性__metaclass__,如果属性存在,就将这个属性赋给此类作为他的元类,如果此属性没有定义,他会想上查找父类中的__metaclass__

如果还没发现__metaclass__属性,解释器会检查名字为__metaclass__的全局变量

谁在用元类?

元类何时被创建?

元类示例1,创建一个类,显示时间标签

# vi test34.py

——————————-

#!/usr/bin/env python

from time import ctime

print ‘*** Welcome to Metaclasses!’

print ‘\tMetaclass declaration first.’

class MetaC(type):

   def __init_(cls,name,bases,attrd):

       super(MetaC, cls).__init__(name, bases, attrd)

       print ‘*** Created class %r at: %s’ %(name, ctime())

print ‘\tClass “Foo” declaration next.’

class Foo(object):

   __metaclass__ = MetaC

   def __init__(self):

       print ‘*** Instantiated class %r at: %s’ \

       %(self.__class__.__name__, ctime())

print ‘\tClass “Foo” instantiation next’

f = Foo()

print ‘\tDONE’

——————————-

输出:

# python test34.py

————————————–

*** Welcome to Metaclasses!

       Metaclass declaration first.

       Class “Foo” declaration next.

       Class “Foo” instantiation next

*** Instantiated class ‘Foo’ at: Tue Dec  3 06:29:40 2013

       DONE

—————————————

元素示例2,创建一个元类,在类中提供一个__str__()方法(问题)

# vi meta.py

————————————

#!/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’

————————————

13.17 相关模块和文档:

下面的代码检查传递到foo函数的数据对象是否是一个整数或者一个字符串,不允许其他类型出现(否则会引发一个异常):

>>> def foo(data):                    

…   if isinstance(data, int):      

…     print ‘you entered an integer’

…   elif isinstance(data, str):    

…     print ‘you entered a string’

…   else:

…     raise TypeError, ‘only integers or strings!’

以下代码定义了三个向量, 前两个包含着操作数, 最后一个代表程序员打算对两个操作数进行一系列操作, 最外层循环遍历每个操作运算,而最内层的两个循环用每个操作数向量中的元素组成各种可能的有序数列对

最后 print 语句打印出将当前操作符应用在给定参数上所得的运算结果

>>> from operator import *                          

>>> vec1 = [12,24]                                  

>>> vec2 = [2, 3, 4]                                

>>> opvec = (add, sub, mul,div)                      

>>> for eachOp in opvec:                            

…     for i in vec1:                              

…         for j in vec2:                          

…             print ‘%s(%d, %d) = %d’ % \          

…             (eachOp.__name__, i, j, eachOp(i, j))

add(12, 2) = 14

add(12, 3) = 15

add(12, 4) = 16

add(24, 2) = 26

add(24, 3) = 27

add(24, 4) = 28

sub(12, 2) = 10

sub(12, 3) = 9

sub(12, 4) = 8

sub(24, 2) = 22

sub(24, 3) = 21

sub(24, 4) = 20

mul(12, 2) = 24

mul(12, 3) = 36

mul(12, 4) = 48

mul(24, 2) = 48

mul(24, 3) = 72

mul(24, 4) = 96

div(12, 2) = 6

div(12, 3) = 4

div(12, 4) = 3

div(24, 2) = 12

div(24, 3) = 8

div(24, 4) = 6

    原文作者:showerlee
    原文地址: https://blog.51cto.com/showerlee/1339661
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞