13.1 介绍 python
类与实例 程序员
类与实例相互关联着:类是对象的定义,而实例是"真正的实物",它存放了类中所定义的对象
的具体信息。 算法
>>> class MyData(): pass >>> mathObj = MyData() >>> mathObj.x = 4 >>> mathObj.y = 5 >>> mathObj.x + mathObj.y 9 >>> mathObj.x * mathObj.y 20这里注意一点是:x,y不是类MyData的属性,它们是实例对象mathObj的独有属性,而且是动态的:你不须要在构造器中,或其它任何地方为它们预先声明或者赋值。
方法 shell
方法为类的属性,除了静态方法(我的理解为静态方法为函数),方法必须有实例来调用。 数据库
>>> class MyDataWithMethod(object): def printFoo(self): print "you invoked printFoo()!" >>> oneObj = MyDataWithMethod() >>> oneObj.printFoo() you invoked printFoo()!特殊的参数:self。参数self偏偏代表了方法是类的属性,方式的调用经过self来关联到类中。
咱们来看一个稍微完整的类的定义及其应用: 编程
类的定义: 数组
>>> class AddrBookEntry(object): "address book entry class" def __init__(self, nm, ph): self.name = nm self.phone = ph print "created instance for:", self.name def updatePhone(self, newph): self.phone = newph print "updated phone# for:", self.name建立实例(实例化)
>>> john = AddrBookEntry("john doe","408-555-1212") created instance for: john doe >>> jane = AddrBookEntry("jane doe","650-555-1212") created instance for: jane doe你会发现,当建立实例的时候,__init__会自动被调用
访问实例属性 安全
>>> john <__main__.AddrBookEntry object at 0x020D8190> >>> john.name 'john doe' >>> john.phone '408-555-1212' >>> jane.name 'jane doe' >>> jane.phone '650-555-1212'方法调用(经过实例)
>>> john.updatePhone("415-555-1212") updated phone# for: john doe >>> john.phone '415-555-1212'建立子类
靠继承来进行子类化是建立和定制新类类型的一种方式,新的类将保持已存在类全部的特性,而不会改动原来类的定义。而且子类能够定制本身的方法。 数据结构
>>> class EmplAddrBookEntry(AddrBookEntry): "employee 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 0x02115FD0> >>> 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 面向对象编程 app
经常使用术语
抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,创建一个相关的子集,能够用于描绘程序结构,从而实现这种模型。抽象不只包括这种模型的数据属性,还定义了这些数据的接口。对某种抽象的实现就是对此数据及与之相关接口的现实化。现实化这个过程对于客户程序应当是透明并且无关的。
封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。因为python中,全部的类属性都是公开的,因此在设计时,对数据提供相应的接口,以避免客户程序经过不规范的操做来存取封装的数据属性。
合成
合成扩充了对类的描述,使得多个不一样的类合成为一个大的类,来解决现实问题。
派生/继承/继承结构
派生描述了子类的建立,新类保留已存类类型中全部须要的数据和行为,但容许修改或者其它的自定义操做,都不会修改原类的定义。继承描述了子类属性从祖先类继承这样一种方式。
泛化/特化
泛化表示全部子类与其父类及祖先类有同样的特定。而特化描述全部子类的自定义,即什么属性让它与其祖先类不一样。
多态
多态的概念指出了对象如何经过他们共同的属性和动做来操做及访问,而不须要考虑他们具体的类。多态代表了动态绑定的存在,容许重载及运行时类型肯定和验证。
自省/反射
代表程序员运行期检查的能力。
13.3 类
13.3.1 建立类
>>> class ClassName(object): "class documentation string" pass基类是一个或多个用于继承的父类的集合;类体由全部声明语句,类成员定义,数据属性和函数组成。类一般在一个模块的顶层进行定义,以便类实例可以在类所定义的源代码文件中的任何地方被建立。
在python中有点特别的重要:声明和定义是一块儿的。
13.4 类属性
属性就是属于另外一个对象的数据或者函数元素,能够经过咱们熟悉的句点属性标识来访问。一些python类型好比复数有数据属性(实部和虚部),而列表和字典,拥有方法(函数属性)
关于属性,有个有趣的地方是:当你访问一个属性时,它同时也是一个对象,拥有它本身的属性,能够访问,这致使一个属性链:
sys.stdout.write("foo") print myModule.myClass.__doc__ myList.extend(map(upper, open("x").readlines()))13.4.1 类的数据属性和方法
数据属性仅仅是所定义的类的变量。它们能够像任何其它变量同样在类建立后被使用,而且被更新。在C++或者Java中就是静态变量。数据属性表示这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。
>>> class C(object): foo = 100 >>> C.foo 100 >>> C.foo += 1 >>> C.foo 101方法仅仅是一个做为类定义一部分定义的函数(这使得方法成为类属性),而且只能经过实例来调用(我并无把静态方法和类方法看成方法,仅仅认为它们只是函数---独立于类对象)
>>> class MyClass(object): def myMethod(self): print "hello world" >>> oneObj = MyClass() >>> oneObj.myMethod() hello world >>> MyClass.myMethod() Traceback (most recent call last): File "<pyshell#69>", line 1, in <module> MyClass.myMethod() TypeError: unbound method myMethod() must be called with MyClass instance as first argument (got nothing instead)13.4.2 决定类的属性
咱们可使用方法来查看类的属性:dir()和__dict__
>>> class MyClass(object): "myclass class definition" myVersion = "1.1" def showMyVersion(self): print MyClass.myVersion >>> 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 0x02114C70>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': 'myclass class definition'})13.4.3 特殊的类属性
C.__name__ 类C的名字(字符串)
C.__doc__ 类C的文档字符串
C.__bases__ 类C的全部父类构成的元组
C.__dict__ 类C的属性
C.__module__ 类C定义所在的模块(1.5 版本新增)
C.__class__ 实例C对应的类(仅新式类中)
>>> MyClass.__name__ 'MyClass' >>> MyClass.__doc__ 'myclass class definition' >>> MyClass.__bases__ (<type 'object'>,) >>> MyClass.__module__ '__main__' >>> MyClass.__class__ <type 'type'>
13.5 实例
若是说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。
13.5.1 初始化:经过调用类对象来建立实例
>>> class MyClass(object): pass >>> mc = MyClass()13.5.2 __init__()“构造器”方法
当类被调用,实例化的第一步是建立实例对象。一旦对象建立了,Python 检查是否实现了__init__()方法。默认状况下,若是没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操做.任何所需的特定操做,都须要程序员实现__init__(),覆盖它的默认行为。若是__init__()没有实现,则返回它的对象,实例化过程完毕。
然而,若是__init__()已经被实现,那么它将被调用,实例对象做为第一个参数(self)被传递进去,像标准方法调用同样。调用类时,传进的任何参数都交给了__init__()。实际中,你能够想像成这样:把建立实例的调用当成是对构造器的调用。
13.5.3 __new__()"构造器"方法
咱们能够经过__new__()来实例化不可变对象。
13.5.4 __del__()"解构器"方法
咱们来经过跟踪实例来理解__del__()的意义:
>>> class InstCt(object): count = 0 def __init__(self): InstCt.count += 1 def __del__(self): InstCt.count -= 1 def howMany(self): return InstCt.count >>> a = InstCt() >>> b = InstCt() >>> b.howMany() 2 >>> a.howMany() 2 >>> del b >>> a.howMany() 1 >>> del a >>> InstCt.count 0
13.6 实例属性
实例仅拥有数据属性(方法严格来讲是类属性),当一个实例被释放后,它的属性同时也被清除了。
13.6.1 “实例化”实例属性(或建立一个更好的构造器)
设置实例的属性能够在实例建立后任意时间进行,也能够在可以访问实例的代码中进行。构造器__init__是设置这些属性的关键点之一。
在构造器中首先设置实例属性
构造器是最先能够设置实例属性的地方,由于__init__()是实例建立后第一个调用的方法。一旦__init__()执行完毕,返回实例对象,即完成了实例化过程。
默认参数提供默认的实例安装,且默认参数必须是不变得对象。
>>> class Person(object): def __init__(self, name = "voler", age = 24): self.name = name self.age = age def show(self): print "name is:%s and age is:%d" % (self.name, self.age) >>> onePerson = Person() >>> onePerson.show() name is:voler and age is:24备注:__init__()返回None
13.6.2 查看实例属性 + 13.6.3 特殊的实例属性
dir(),__dict__,__class__
>>> class C(object): def __init__(self, x = 0, y = 0): self.x = x self.y = y >>> c = C() >>> dir(c) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y'] >>> c.__dict__ {'y': 0, 'x': 0} >>> c.__class__ <class '__main__.C'>13.6.5 实例属性VS类属性
访问类属性
类属性可经过类或实例来访问。固然,经过实例访问一个类属性时,只是创建一个临时的类属性引用,当实例消失后,对应的类属性也消失。
>>> class C(object): version = 1.2 >>> c = C() >>> C.version 1.2 >>> c.version 1.2 >>> C.version += 0.1 >>> C.version 1.3 >>> c.version 1.3 >>> c.version += 0.1 >>> c.version 1.4000000000000001 >>> C.version 1.3可是,若是类属性可变的话,一切都不一样了:
>>> 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 "<pyshell#142>", line 1, in <module> del foo.x AttributeError: 'Foo' object attribute 'x' is read-only >>> del foo因此,并不推荐使用实例来访问类属性。虽然方法也被称为类属性,但方法特殊就特殊在于:它经过self参数绑定到了实例对象去了,因此方法一般要用到实例来调用,用类调用不行-----缘由也挺简单的,方法中操做的是实例数据,通常不多操做类数据属性,因此要用实例来调用。
13.7 从这里开始校对----绑定和方法调用
方法三要点:
首先,方法仅仅是类内部定义的函数(这意味着方法是类属性而不是实例属性)
其次,方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是绑定到那个实例来。没有实例时方法就是未绑定的。
最后,任何一个方法定义中的第一个参数就是变量self,它表示调用此方法的实例对象。
核心笔记:self是什么?
self变量用于在类实例方法中引用方法所绑定的实例。由于方法的实例在任何方法调用中老是做为第一个参数传递的,self被选中用来表明实例。你必须在方法声明中放上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
13.8 静态方法和类方法
并不推荐使用静态方法,通常来讲,咱们可使用模块函数来达到目的。
13.8.1 staticmethod()和classmethod()内建函数
class TestStaticMethod(object): def foo(): print "calling static method foo()" foo = staticmethod(foo) class TestClassMethod(object): def foo(cls): print "calling class method foo()" print "foo() is part of class:", cls.__name__ foo = clasmethod(foo)
>>> 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: TestClassMethod13.8.2 使用函数修饰符(也称为装饰器)
class TestStaticMethod(object): @staticmethod def foo(): print "calling static method foo()" class TestClassMethod(object): @classmethod def foo(cls): print "calling class method foo()" print "foo() is part of class:", cls.__name__
13.9 组合
写在前面:组合优于派生
一个类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去,同其它数据类型及逻辑执行流混合使用。有两种方法能够在你的代码中利用类。第一种是组合,就是让不一样的类混合并加入到其它类中,来增长功能和代码重用性。你能够在一个大点的类中建立你本身的类的实例,显示一些其它的属性和方法来加强原来的类对象。另外一种方法是经过派生。
13.10 子类和派生
当类之间有显著不一样,而且较小的类是较大的类所须要的组件时,组合表现的很好,当当你设计“相同的类但又一些不一样的功能”时,派生就是一个更加合理的选择了。
OOP的更强大方面之一是可以使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响系统中使用现存类的其余代码片断。
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 Parent(object): def __init__(self): self.x = 1 class Child(Parent): pass c = Child() print c.x程序输出:1
13.11.1 __base__类属性
__base__类属性包含其父类的集合的元祖:
class A(object): pass class B(A): pass class C(B): pass class D(B,A): pass程序输出:
>>> A.__bases__ (<type 'object'>,) >>> B.__bases__ (<class '__main__.A'>,) >>> C.__bases__ (<class '__main__.B'>,) >>> D.__bases__ (<class '__main__.B'>, <class '__main__.A'>)这里有点须要注意:D必须先继承B,才能继承A,由于继承是广度优先,深度其次,因此要先继承到小的父类,最后才是大的父类。
13.11.2 经过继承覆盖方法
子类的方法覆盖掉父类的方法很正常,可是当你想调用父类的方法的时候,只要经过父类类名直接调用便可,固然记得传递一个对象进去。
class P(object): def foo(self): print "parent" class C(P): def foo(self): print "child" c = C() print P.foo(c)程序输出:
>>> parent None这里怎么多出一个None呢?很简单,任何一个方法都会返回一个对象(__init__()会返回一个None)。咱们这样修改的话,None就会消失:
class P(object): def foo(self): return "parent" class C(P): def foo(self): return "child" c = C() print P.foo(c)不过一般状况下,咱们会这样编码:
class P(object): def foo(self): print "parent" class C(P): def foo(self): super(C, self).foo() print "child" c = C() c.foo()程序输出;
>>> parent child备注:重写__init__不会自动调用基类的__init__,因此请添加相似下面的代码:
class C(P): def __init__(self): super(C, self).__init__()这里用super的漂亮之处在于:你不须要写出任何基类的名字(当你继承了不少基类的时候你会感谢super的)
13.11.3 从标准类型派生
子类化python类型之不可变类型
咱们定制一个类,继承于float,可是控制小数位为2位:
class RoundFloat(float): def __new__(cls, val): return float.__new__(cls, round(val, 2))固然,用super更好:
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子类化python类型之可变类型
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:", [key for key in d] print "by keys():", d.keys()程序输出:
>>> by iterator: ['zheng-cai', 'xin-yi', 'hui-jun'] by keys(): ['hui-jun', 'xin-yi', 'zheng-cai']13.11.4 多重继承
方法解释顺序(MRO)
在python2.2之前的版本,算法很是简单:深度优先,从左至右进行搜索,取得在子类中使用的属性。
咱们先来看看经典类的MRO:
class P1: def foo(self): print "called P1-foo()" class P2: 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 ==> P1 called P1-foo() >>> gc.bar() # GC ==> C1 ==> P1 ==> P2 called P2-bar()但新式类就不一样了,广度优先,从左至右(将上述代码中继承object便可成为新式类):
>>> gc = GC() >>> gc.foo() # GC ==> C1 ==> C2 ==> P1 called P1-foo() >>> gc.bar() # GC ==> C1 ==> C2 called C2-bar()新式类有一个__mro__属性,告诉你查找顺序是怎样的:
>>> GC.__mro__ (<class '__main__.GC'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)
经典类的MRO问题根源在于:当你深度优先的时候,一旦到达object(全部类的基类)而且找到你想找的(好比__init__()),那么你就会放弃右边的类(从左至右)的方法(可是我写的代码结果竟然和书上的结果不同,可能python2.7进行了优化吧,下面代码中理应不可能有输出的)
class B(object): pass class C(object): def __init__(self): print "the default constructor" class D(B,C): pass
>>> d = D() the default constructor
13.12 类,实例和其余对象的内建函数
13.12.1 issubclass()
issubclass()布尔函数判断一个类是不是另外一个类的子类或子孙类:
issubclass(sub, sup),固然,sup能够为一个父类组成的元祖
>>> issubclass(int, object) True
13.12.2 isinstance()
isinstance()布尔函数在断定一个对象是不是另外一个给定类的实例时,很是有用。
isinstance(obj1, obj2)
>>> 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(4, int) True >>> isinstance(4, str) False >>> isinstance("hello", str) True13.12.3 hasattr(), getattr(), setattr(), delattr()
hasattr()函数是boolean型的,它的目的就是为了决定一个对象是否有一个特定的属性,通常用于访问某属性前先作一个检查。getattr()和setattr()函数相应的取得和赋值给对象的属性,getattr()会在你试图读取一个不存在的属性时,引起AttributeError异常,除非给出那个可选的默认参数。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 "<pyshell#30>", line 1, in <module> getattr(myInst,"bar") AttributeError: 'myClass' object has no attribute 'bar' >>> setattr(myInst, "bar","my attr") >>> dir(myInst) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo'] >>> myInst.__dict__ {'foo': 100, 'bar': 'my attr'} >>> delattr(myInst,"foo") >>> myInst.__dict__ {'bar': 'my attr'}
13.12.4 dir()
我以为dir惟一的好处就是:当你对一个模块不熟悉,又要调用里面的方法时候,能够用到dir.
13.12.5 super()
super()的主要用途就是:查找父类的属性,而且出奇的方便。
13.12.6 vars()
vars()内建函数与dir()类似,只是给定的对象参数都必须有一个__dict__属性。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(4.22) >>> rfm <__main__.RoundFloatManual object at 0x021F8510> >>> print rfm <__main__.RoundFloatManual object at 0x021F8510>因此,咱们得重写__str__属性,毕竟print关系到的是str()。固然,咱们顺便也把__repr__给重写了。
当咱们没有重写__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程序输出:
>>> rfm = RoundFloatManual(4.2234) >>> rfm <__main__.RoundFloatManual object at 0x02078510> >>> print rfm 4.22当咱们重写__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(4.2234) >>> rfm 4.22 >>> print rfm 4.2213.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) __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程序输出:
>>> time1 = Time60(4,5) >>> time2 = Time60(6,7) >>> time1 + time2 10:12 >>> time1 += time2 >>> time1 10:12这里惟一要注意到是:self.__class__的使用,它会实例化self.
最后咱们来看个随机序列迭代器的例子:
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)程序输出:
>>> randObj = RandSeq([1,2,3,4,5,6]) >>> for i in range(10): print randObj.next(), 6 4 2 4 3 3 2 3 2 3
13.13.3 迭代器
上述例子中,__init__()方法执行赋值操做,而__iter__()仅返回self,这就是如何将一个对象声明为迭代器的方式,最后,调用next()来获得迭代器中连续的值。
咱们来编写一个任意项的迭代器:
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(range(10)) >>> for j in range(1,5): print j,":", a.next(j) 1 : [0] 2 : [1, 2] 3 : [3, 4, 5] 4 : [6, 7, 8, 9]出异常的输出:
>>> a.next(14) Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> a.next(14) File "C:\Python27\hello.py", line 11, in next retval.append(self.iter.next()) StopIteration对异常进行处理的输出:
>>> a = AnyIter(range(10), True) >>> a.next(14) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]13.13.4 多类型定制(NumStr)
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) __repr__ = __str__ def __add__(self, other): if isinstance(other, NumStr): return self.__class__(self.__num + other.__num, self.__string + other.__string) else: raise TypeError, "illegal 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,"ilegal 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) >>> a [3::'foo'] >>> a < b True >>> a == a True >>> b * 2 [6::'googoo'] >>> b + c [5::'goofoo'] >>> cmp(a,b) -1 >>> cmp(b,c) 1 >>> cmp(a,a) 0
13.14 私有化
在变量名加双下划线,这是简单的数据私有化方法,不过我的并不推荐。
13.15 受权
13.15.1 包装
对一个已存在的对象进行包装,无论它是数据类型,仍是一段代码,能够是对一个已存在的对象,增长新的,删除不要的,或者修改其它已存在的功能。
13.15.2 实现受权
受权是包装的一个特性,可用于简化处理有关dictating功能,采用已存在的功能以达到最大限度的代码重用。
包装一个类型一般是对已存在的类型的一些定制,这种作法能够新建,修改或删除原有产品的功能。其它的保持原样,或者保留已存功能和行为。受权的过程,便是全部更新功能都是由昕蕾的某部分来处理,但已存在的功能就受权给对象的默认属性。
实现受权的关键点就是覆盖__getattr__()方法,在代码中包含一个对getattr()内建函数的调用。特别地,调用getattr()以获得默认对象属性(数据属性或者方法)并返回它以便访问或调用。特殊方法__getattr__()的工做方式是,当搜索一个属性时,任何局部对象首先被找到(定制的对象)。若是搜索失败了,则__getattr__()会被调用,而后调用getattr()获得一个对象的默认行为。
下面是包装对象的简例:
class WrapMe(object): def __init__(self, obj): self.__data = obj def get(self): return self.__data def __repr__(self): return repr(self.__data) def __str__(self): return str(self.__data) def __getattr__(self, attr): return getattr(self.__data, attr)注意程序的输出:
>>> wrappedComplex = WrapMe(3.5+4.2j) >>> wrappedComplex (3.5+4.2j) >>> wrappedComplex.real 3.5 >>> wrappedComplex.imag 4.2 >>> wrappedComplex.conjugate() (3.5-4.2j) >>> wrappedComplex.get() (3.5+4.2j)这里,咱们调用复数的三种属性,可是这三种属性在类的定义中并不存在。对这些属性的访问,是经过getattr()方法,受权给对象。最终调用get()方法没有受权,由于它是为咱们的对象定义的--它返回包装的真实的数据对象。
>>> wrappedList = WrapMe([123, 'foo', 45.67]) >>> wrappedList.append('bar') >>> wrappedList.append(123) >>> wrappedList [123, 'foo', 45.67, 'bar', 123] >>> wrappedList.index(45.67) 2 >>> wrappedList.count(123) 2 >>> wrappedList.pop() 123 >>> wrappedList [123, 'foo', 45.67, 'bar']可是咱们为何要定义get()函数呢?只是这样咱们就能够取得原对象,进行某些特殊的操做,好比:
>>> wrappedList [123, 'foo', 45.67, 'bar'] >>> wrappedList[3] Traceback (most recent call last): File "<pyshell#35>", line 1, in <module> wrappedList[3] TypeError: 'WrapMe' object does not support indexing >>> wrappedList.get()[3] 'bar'更新简单的包裹类
from time import time, ctime class TimedWrapMe(object): def __init__(self, obj): self.__data = obj self.__ctime = self.__mtime = self.__atime = time() def get(sefl): 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' required" 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 repr(self.__data) def __str__(self): self.__atime = time() return str(self.__data) def __getattr__(self, attr): self.__atime = time() return getattr(self.__data, attr)
>>> timeWrappedObj = TimedWrapMe(932) >>> timeWrappedObj.gettimestr('c') 'Tue Jun 18 19:49:03 2013' >>> timeWrappedObj.gettimestr('m') 'Tue Jun 18 19:49:03 2013' >>> timeWrappedObj.gettimestr('a') 'Tue Jun 18 19:49:03 2013' >>> timeWrappedObj 932 >>> timeWrappedObj.set('time is up!') >>> timeWrappedObj 'time is up!' >>> timeWrappedObj.gettimestr('c') 'Tue Jun 18 19:49:03 2013' >>> timeWrappedObj.gettimestr('m') 'Tue Jun 18 19:49:43 2013' >>> timeWrappedObj.gettimestr('a') 'Tue Jun 18 19:49:57 2013'
13.16 新式类的高级特性
13.16.1 新式类的通用特性
工厂函数的诞生:
int(), long(), float(), complex(),str(), unicode(),list(), tuple(), type(),basestring(), dict(), bool(), set(), frozenset(),object(), classmethod(), staticmethod(), super(), property(), file()
在判断类型的时候:
old:
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
13.16.2 __slots__类属性
字典位于实例的“心脏”。__dict__属性跟踪全部实例属性。好比一个实例inst,它有个属性foo,那使用inst.foo来访问它等价于inst.__dict__["foo"]
字典会占据大量内存,若是你有一个属性数量不多的类,但有不少实例,那么正好是这种状况。为内存上的考虑,用户如今可使用__slots__属性来替代__dict__。
基本上,__slots__是一个类变量,由一序列型对象组成,由全部合法标识构成的实例属性的集合来表示。它能够是一个列表,元组或可迭代对象。也能够是标识实例能拥有的惟一的属性的简单字符串。任何试图建立一个其名不在__slots__中的名字的实例属性都将致使AttributeError 异常:
>>> class SlottedClass(object): __slots__ = ("foo","bar") >>> c = SlottedClass() >>> c.foo = 42 >>> c.xxx = "do not think so" Traceback (most recent call last): File "<pyshell#52>", line 1, in <module> c.xxx = "do not think so" AttributeError: 'SlottedClass' object has no attribute 'xxx'这种特性的主要目的是节约内存,其反作用是某种类型的“安全”,它能防止用户为所欲为的动态增长实例属性。
python类有一个名为__getattr__()的特殊方法,它仅当属性不能在实例的__dict__或它的类(类的__dict__),或者祖先类(其__dict__)中找到时,才被调用。
__getattribute__的做用是:当属性被访问时,它就一直均可以被调用,而不局限于不能找到的状况。
13.16.4 描述符
描述符表示对象属性的一个代理。当须要属性时,可经过描述符或者采用常规方式(句点属性标识法)来访问它。
__get__(),__set__(),__delete__()特殊方法
__getattribute__()特殊方法
使用描述符的顺序很重要,有一些描述符的级别要高于其它的。整个描述符系统的心脏是__getattribute__(),由于对每一个属性的实例都会调用到这个特殊的方法。这个方法被用来查找类的属性,同时也是你的一个代理,调用它能够进行属性的访问等操做。
若是一个实例调用了__get__()方法,这就可能传入了一个类型或类的对象。举例来讲,给定类X 和实例x, x.foo 由__getattribute__()转化成:
type(x).__dict__['foo'].__get__(x, type(x))若是类调用了__get__()方法,那么None 将做为对象被传入(对于实例, 传入的是self):
X.__dict__['foo'].__get__(None, X)最后,若是super()被调用了,好比,给定Y 为X 的子类,而后用super(Y,obj).foo 在obj.__class__.__mro__中紧接类Y 沿着继承树来查找类X,而后调用:
X.__dict__['foo'].__get__(obj, X)而后,描述符会负责返回须要的对象。
优先级别
1. 类属性
2. 数据描述符
3. 实例属性
4. 非数据描述符
5. 默认为__getattr__()
描述符是一个类属性,所以全部的类属性皆具备最高的优先级。你其实能够经过把一个描述符的引用赋给其它对象来替换这个描述符。比它们优先级别低一等的是实现了__get__()和__set__()方法的描述符。若是你实现了这个描述符,它会像一个代理那样帮助你完成全部的工做!
不然,它就默认为局部对象的__dict__的值,也就是说,它能够是一个实例属性。接下来是非数据描述符。
描述符举例:
>>> class DevNull1(object): def __get__(self, obj, typ = None): pass def __set__(self, obj, val): pass >>> class C1(object): foo = DevNull1() >>> c1 = C1() >>> c1.foo = "bar" >>> c1.foo >>> print c1.foo None任何一件事情,刚开始的时候,老是索然无趣,并且简单。后面则慢慢的。。。。。。
>>> class DevNull2(object): def __get__(self, obj, typ = None): print "accessing attribute...ignoring" def __set__(self, obj, val): print "attempt to asign %r...ignoring" % (val) >>> class C2(object): foo = DevNull2() >>> c2 = C2() >>> c2.foo = "bar" attempt to asign 'bar'...ignoring >>> x = c2.foo accessing attribute...ignoring >>> print x None忽然,就这样变得有趣起来了。那么,来点更刺激的吧:
>>> 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 "assigning %r to [%s]...ignoring" % (val, self.name) >>> class C3(object): foo = DevNull3("foo") >>> c3 = C3() >>> c3.foo = "bar" assigning 'bar' to [foo]...ignoring >>> x = c3.foo accessing [foo]...ignoring >>> print x None >>> c3.__dict__["foo"] = "bar" >>> x = c3.foo accessing [foo]...ignoring >>> print x None >>> print c3.__dict__["foo"] bar最后两个输出很特殊,说明了数据描述符比实例属性的优先级高,所赋的值“bar”被隐藏或覆盖了。
一样地,因为实例属性比非数据描述符的优先级高,你也能够将非数据描述符隐藏。这就和你给一个实例属性赋值,将对应类的同名属性隐藏起来是同一个道理:
>>> 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 <bound method FooFoo.foo of <__main__.FooFoo object at 0x01E166F0>> >>> bar.foo() very important foo() method.可是,下面代码可能更能清楚的表达意思:
>>> 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.让咱们来看最后一个例子:使用文件来存储属性
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" % (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.PicklingError),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 MyFileVarClass(object): foo = FileDescr("foo") bar = FileDescr("bar") >>> fvc = MyFileVarClass() >>> print fvc.foo Traceback (most recent call last): File "<pyshell#121>", line 1, in <module> print fvc.foo File "C:\Python27\hello.py", line 10, in __get__ raise AttributeError,"%r used before assignment" % self.name AttributeError: 'foo' used before assignment >>> fvc.foo = 42 >>> fvc.bar = "leanna" >>> print fvc.foo,fvc.bar 42 leanna >>> del fvc.foo >>> print fvc.foo Traceback (most recent call last): File "<pyshell#126>", line 1, in <module> print fvc.foo File "C:\Python27\hello.py", line 10, in __get__ raise AttributeError,"%r used before assignment" % self.name AttributeError: 'foo' used before assignment >>> print fvc.bar leanna
属性和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(12) >>> print inst.x 12 >>> inst.x = 20 Traceback (most recent call last): File "<pyshell#138>", line 1, in <module> inst.x = 20 AttributeError: can't set attribute并无定义set的方法,故最后进行赋值出现异常。
那么,咱们增长set方法吧。
>>> 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 -21 >>> inst.x = -21 >>> print inst.x -21 >>> inst.x = 20 >>> print inst.x 20我有个疑问是:为何初始化为20的时候,会输出21呢???
可是这样会搞乱类的命名空间,书上提供了一种方法,不过不是看得很懂。。。。
1 “借用”一个函数的名字空间
2编写一个用做内部函数的方法做为 property()的(关键字)参数
3 (用 locals())返回一个包含全部的(函数/方法)名和对应对象的字典
4 把字典传入 property(),而后
5去掉临时的名字空间
>>> 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 = ~x return locals()不太理解,为何x()被定义成一个函数而不是一个方法
13.6.5 Metaclasses和__metaclass__
不管发生什么,都要问问本身,当初本身是为了什么而出发的!!!
元类(Metaclasses)是什么?
元类让你来定义某些类是如何被建立的,从根本上说,赋予你如何建立类的控制权。
从根本上说,你能够把元类想成是一个类中类,或是一个类,它的实例是其它的类。实际上,当你建立一个新类时,你就是在使用默认的元类,它是一个类型对象。(对传统的类来讲,它们的元类是types.ClassType.)当某个类调用type()函数时,你就会看到它究竟是谁的实例:
>>> 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__.全部新风格的类若是没有任何父类,会从对象或类型中继承,固然是object。
在执行类定义的时候,将检查此类正确的(通常是默认的)元类,元类(一般)传递三个参数(到构造器):类名,从基类继承数据的元组,和(类的)属性字典。
谁在用元类?
程序员
元类示例1
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"程序输出:
>>> ***welcome to metaclasses! metaclass declaration first class 'foo' declaration next ***created class 'Foo' at:Tue Jun 18 22:11:12 2013 class 'foo' instantiation next ***instantiated class 'Foo' at:Tue Jun 18 22:11:12 2013 Done彷佛明白了什么。
元类示例2
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.18 练习
13-1. 程序设计。请列举一些面向对象编程与传统旧的程序设计形式相比的先进之处。
OOP的好处,我的理解是:解放了程序员编程的负担。
13-2. 函数和方法的比较。函数和方法之间的区别是什么?
方法是类属性,可是函数不是,方法因类而存在。
13-3. 对类进行定制。写一个类,用来将浮点数值转换为金额。在本练习里,咱们使用美国
货币,但读者也能够自选任意货币。
基本任务: 编写一个dollarize()函数,它以一个浮点数值做为输入,返回一个字符串形式的
金额数。好比说:
dollarize(1234567.8901) ==> ‘$1,234,567.89.
dollarize()返回的金额数里应该容许有逗号(好比1,000,000),和美圆的货币符号。若是有负
号,它必须出如今美圆符号的左边。完成这项工做后,你就能够把它转换成一个有用的类,名为
MoneyFmt。
def dollarize(fValue): strValue = str(fValue) isNegative = False if strValue[0] == "-": isNegative = True strValue = strValue[1:] strMoney = strValue.split(".") strTemp = [] while (len(strMoney[0]) - 1) / 3: strTemp.append(strMoney[0][-3:]) strMoney[0] = strMoney[0][:-3] strTemp.append(strMoney[0]) strTemp.reverse() myDoller = ",".join(strTemp) + "." + strMoney[1] if isNegative: myDoller = "-" + myDoller return myDoller class MoneyFmt(object): def __init__(self, value = 0.0): self.value = dollarize(value) def update(self, value = None): self.value = dollarize(value) def __repr__(self): return repr(self.value) def __str__(self): val = "$" if self.value[0] == "-": val = "-$" + self.value[1:] else: val += self.value return val def __nonzero__(self): return bool(self.value)
程序输出:
>>> cash = MoneyFmt(1234567.8901) >>> cash '1,234,567.8901' >>> print cash $1,234,567.8901 >>> cash = MoneyFmt(-1234567.8901) >>> cash '-1,234,567.8901' >>> print cash -$1,234,567.890113-4. 用户注册。创建一个用户数据库(包括登陆名、密码和上次登陆时间戳)类(参考练习7-5和9-12),来管理一个系统,该系统要求用户在登陆后才能访问某些资源。这个数据库类对用户进行管理,并在实例化操做时加载以前保存的用户信息,提供访问函数来添加或更新数据库的信息。在数据修改后,数据库会在垃圾回收时将新信息保存到磁盘。
class PersonDataBase(object): def __init__(self): self.__personDataBase = [("user1",1),("user2",2)] def login(self, userName, userPasswd): if (userName, userPasswd) not in self.__personDataBase: print "sorry, error" return None else: print "ok" oneObj = PersonDataBase() oneObj.login("test",1) oneObj.login("user1",1)没什么心思写这题,由于题目看不懂。。。就是不知道具体要写什么,是写复杂仍是写简单,要达到什么效果等等。。
13-5. 几何. 建立一个由有序数值对(x, y) 组成的Point 类,它表明某个点的X 坐标和Y 坐标。X 坐标和Y 坐标在实例化时被传递给构造器,若是没有给出它们的值,则默认为坐标的原点。
class Point(object): def __init__(self, x, y): self.__x = x self.__y = y def get_xy(self): return (self.__x, self.__y) def set_xy(self, point): self.__x, self.__y = point def __str__(self): return "%d:%d" % (self.__x, self.__y) __repr__ = __str__ point = property(get_xy, set_xy)
程序输出:
>>> p = Point(2,3) >>> p.point = 5,6 >>> p 5:6 >>> p.point = 7,8 >>> print p 7:8
13-6. 几何. 建立一个直线/直线段类。除主要的数据属性:一对坐标值(参见上一个练习)外,
它还具备长度和斜线属性。你须要覆盖__repr__()方法(若是须要的话,还有__str__()方法),使得
表明那条直线(或直线段)的字符串表示形式是由一对元组构成的元组,即,((x1, y1), (x2, y2)).
总结:
__repr__ 将直线的两个端点(始点和止点)显示成一对元组
length 返回直线段的长度 - 不要使用"len", 由于这样令人误解它是整数。
slope 返回此直线段的斜率(或在适当的时候返回None)
import math class Point(object): def __init__(self, x, y): self.x = x self.y = y def get_xy(self): return (self.x, self.y) def set_xy(self, point): self.x, self.y = point def __str__(self): return "%d:%d" % (self.x, self.y) __repr__ = __str__ point = property(get_xy, set_xy) class Line(object): def __init__(self, pointStart, pointEnd): self.pointStart = pointStart self.pointEnd = pointEnd def get_line(self): return (self.pointStart, self.pointEnd) def set_line(self, line): self.pointStart,self.pointEnd = line def __str__(self): return "%s-->%s" % (self.pointStart, self.pointEnd) def length(self): return math.sqrt((self.pointStart.x - self.pointEnd.x) ** 2 + (self.pointStart.y - self.pointEnd.y) ** 2) __repr__ = __str__ line = property(get_line, set_line) p1 = Point(2,3) p2 = Point(4,5) line1 = Line(p1, p2) print line1.length()
程序输出:
>>> 2.82842712475斜率就不写了。这里有个特别注意到点是:Point数据不要写成私有的,不然调用起来特别难,除非你编写一个get函数来获取数据 。
13-7. 数据类。提供一个time 模块的接口,容许用户按照本身给定时间的格式,好比:
“MM/DD/YY,” “MM/DD/YYYY,” “DD/MM/YY,” “DD/MM/ YYYY,” “Mon DD, YYYY,” 或是标准
的Unix 日期格式:“Day Mon DD, HH:MM:SS YYYY” 来查看日期。你的类应该维护一个日期值,并
用给定的时间建立一个实例。若是没有给出时间值,程序执行时会默认采用当前的系统时间。还包
括另一些方法:
update() 按给定时间或是默认的当前系统时间修改数据值
display() 以表明时间格式的字符串作参数,并按照给定时间的格式显示:
'MDY' ==> MM/DD/YY
'MDYY' ==> MM/DD/YYYY
'DMY' ==> DD/MM/YY
'DMYY' ==> DD/MM/YYYY
'MODYY' ==> Mon DD, YYYY
import time class Date(object): def __init__(self, time = time.ctime()): self.time = time def choice(self, choiceTime): date = [] date = self.time.split(" ") dateDict = {} dateDict["MMY"] = date[1] + "/" + date[2] + "/" + date[4][2:] dateDict["MDYY"] = date[1] + "/" + date[2] + "/" + date[4] dateDict["DMY"] = date[2] + "/" + date[1] + "/" + date[4][2:] dateDict["DMYY"] = date[2] + "/" + date[1] + "/" + date[4] dateDict["MODYY"] = date[1] + " " + date[2] + "," + date[4] return dateDict[choiceTime] if __name__ == "__main__": date1 = Date() while True: print "'MMY'-->MM/DD/YY" print "'MDYY' ==> MM/DD/YYYY" print "'DMY' ==> DD/MM/YY" print "'DMYY' ==> DD/MM/YYYY" print "'MODYY' ==> Mon DD, YYYY" choiceTime = raw_input("please enter your choice(q to quit):") if choiceTime.lower() == "q": break print date1.choice(choiceTime)程序输出:
>>> 'MMY'-->MM/DD/YY 'MDYY' ==> MM/DD/YYYY 'DMY' ==> DD/MM/YY 'DMYY' ==> DD/MM/YYYY 'MODYY' ==> Mon DD, YYYY please enter your choice(q to quit):MMY Jun/18/13 'MMY'-->MM/DD/YY 'MDYY' ==> MM/DD/YYYY 'DMY' ==> DD/MM/YY 'DMYY' ==> DD/MM/YYYY 'MODYY' ==> Mon DD, YYYY please enter your choice(q to quit):MDYY Jun/18/2013 'MMY'-->MM/DD/YY 'MDYY' ==> MM/DD/YYYY 'DMY' ==> DD/MM/YY 'DMYY' ==> DD/MM/YYYY 'MODYY' ==> Mon DD, YYYY please enter your choice(q to quit):DMY 18/Jun/13 'MMY'-->MM/DD/YY 'MDYY' ==> MM/DD/YYYY 'DMY' ==> DD/MM/YY 'DMYY' ==> DD/MM/YYYY 'MODYY' ==> Mon DD, YYYY please enter your choice(q to quit):Q13-8. 堆栈类
class Stack(list): def __init__(self, stack): super(Stack, self).__init__() self.stack = stack def push(self, oneElement): self.stack.append(oneElement) def pop(self): return self.stack.pop() def isEmpty(self): return (not len(self.stack)) def peek(self): return self.stack[-1] def __str__(self): return str(self.stack) __repr__ = __str__程序输出:
>>> stk = Stack([1,2,3]) >>> stk [1, 2, 3] >>> stk.push(4) >>> stk [1, 2, 3, 4] >>> stk.pop() 4 >>> stk.pop() 3 >>> stk [1, 2] >>> stk.peek() 2 >>> stk [1, 2] >>> stk.isEmpty() False这个类实际上编写的并不完美,由于根本就没有继承list。不知道如何继承list来编写堆栈类。我记忆中以前写过,用到list的继承后,写的特别的轻松,可是忘记内容在哪里了。
13-9. 队列类
class Queue(object): def __init__(self, queue): self.queue = queue def enqueue(self, element): self.queue.append(element) def dequeue(self): element = self.queue[0] self.queue = self.queue[1:] return element def __str__(self): return str(self.queue) __repr__ = __str__程序输出:
>>> que = Queue([1,2,3,4]) >>> que [1, 2, 3, 4] >>> que.enqueue(5) >>> que.dequeue() 1 >>> que [2, 3, 4, 5] >>> que.dequeue() 2 >>> que [3, 4, 5]这里编写的也不太好,由于并无进行判断是否存在异常的现象等。
13-10. 堆栈和队列。编写一个类,定义一个可以同时具备堆栈(FIFO)和队列(LIFO)操做行为的数据结构。这个类和Perl 语言中数组相像。须要实现四个方法:
shift() 返回并删除列表中的第一个元素,相似于前面的dequeue()函数。
unshift() 在列表的头部"压入"一个新元素
push() 在列表的尾部加上一个新元素,相似于前面的enqueue()和push()方法。
pop() 返回并删除列表中的最后一个元素,与前面的pop()方法彻底同样。
class StackQueue(object): def __init__(self,StackQueue): self.StackQueue = StackQueue def isEmpty(self): return (not len(self.StackQueue)) def shift(self): if self.isEmpty(): print "empty, can not shift" else: element = self.StackQueue[0] self.StackQueue = self.StackQueue[1:] return element def unshift(self, element): self.StackQueue = [element] + self.StackQueue def push(self, element): self.StackQueue.append(element) def pop(self): self.StackQueue.pop() def __str__(self): return str(self.StackQueue) __repr__ = __str__程序输出:
>>> stkque = StackQueue([1,2,3]) >>> stkque [1, 2, 3] >>> stkque.shift() 1 >>> stkque.unshift(6) >>> stkque [6, 2, 3] >>> stkque.push(7) >>> stkque [6, 2, 3, 7] >>> stkque.pop() >>> stkque [6, 2, 3]后面程序愈来愈有点:变态。。。。。。要我用python作出一个QQ来吗?我当时看wxpython,头都看晕了。。
习题13.11,13.12,13.13先跳过(之后可能也不会回头作这三道题。。。)
13-14. DOS. 为DOS 机器编写一个UNIX 操做界面的shell。你向用户提供一个命令行,使得用户能够在那里输入Unix 命令,你能够对这些命令进行解释,并返回相应的输出,例如:“ls”命令调用“dir”来显示一个目录中的文件列表,“more”调用同名命令(分页显示一个文件),“cat” 调用 “type,” “cp” 调用“copy,” “mv” 调用 “ren,” “rm” 调用 “del,” 等.
import os def cmdDir(): dirName = os.getcwd() for i in os.listdir(dirName): print dirName + "\\" + i def cmdmore(cmd): fileName = cmd.split(" ")[1] with open(fileName) as fobj: for line in fobj: print line def cmdtype(cmd): fileName = cmd.split(" ")[1] type(fileName) def cmdcopy(cmd): oldFile = cmd.split(" ")[1] newFile = cmd.split(" ")[2] with open(oldFile,"r") as foldObj: with open(newFile,"w") as fnewObj: for line in foldObj: fnewObj.write(line) def cmdren(cmd): cmdcopy(cmd) delFile = cmd.split(" ")[1] os.remove(delFile) def cmddel(cmd): delFile = cmd.split(" ")[1] os.remove(delFile) def DOS(): while True: cmd = raw_input("-->") if cmd.find("ls") != -1: cmdDir() elif cmd.find("more") != -1: cmdmore(cmd) elif cmd.find("cat") != -1: cmdtype(cmd) elif cmd.find("cp") != -1: cmdcopy(cmd) elif cmd.find("mv") != -1: cmdren(cmd) elif cmd.find("rm") != -1: cmddel(cmd) else: print "sorry, command is wrong.please enter:ls,more,cat,cp,mv or rm" if __name__ == "__main__": DOS()
我惟一不理解的是cat这条命令,用type来表示,我如何type一个文件???
13-15. 受权。示例13.8 的执行结果代表咱们的类CapOpen 能成功完成数据的写入操做。在咱们的最后评论中,提到可使用CapOpen() 或 open()来读取文件中的文本。为何呢?这二者使用起来有什么差别吗?
实际上使用起来没什么差别,只是使用CapOpen()的时候,它重写了write的方法罢了。
13-16. 受权和函数编程。
(a) 请为示例13.8 中的CapOpen 类编写一个writelines()方法。这个新函数将能够一次读入多行文本,而后将文本数据转换成大写的形式,它与write()方法的区别和一般意思上的writelines()与write()方法之间的区别类似。注意:编写完这个方法后,writelines()将再也不由文件对象"代理"。
(b) 在writelines()方法中添加一个参数,用这个参数来指明是否须要为每行文本加上一个换行符。此参数的默认值是False,表示不加换行符。
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 repr(self.file) def write(self, line): self.file.write(line.upper()) def writelines(self, lines, isNewLine = False): for line in lines: self.file.write(line) if not isNewLine: self.file.write("\n") def __getattr__(self, attr): return getattr(self.file, attr) if __name__ == "__main__": fobj = CapOpen("data.txt") print fobj.read() fobj.close() fobj = CapOpen("data.txt","a+") fobj.write("newline\n") fobj.close() fobj = CapOpen("data.txt","a+") lines = ["a\n","new\n","line\n"] fobj.writelines(lines, True) print fobj = CapOpen("data.txt") print fobj.read() fobj.close()程序输出:
>>> hello world i love this world and i love python too hello world i love this world and i love python tooNEWLINE a new line
13-17. 数值类型子类化。在示例13.3 中所看到的moneyfmt.py 脚本基础上修改它,使得它能够扩展Python 的浮点类型。请确保它支持全部操做,并且是不可变的。
class MoneyFmt(float): def __init__(self, value = 0.0): super(MoneyFmt, self).__init__() self.value = float(value) def update(self, value = None): self.value = float(value) def __repr__(self): return repr(self.value) def __str__(self): return "%f" % self.value def __nonzero__(self): return bool(self.value) # def __getattr__(self, attr): # return getattr(self.value, attr) if __name__ == "__main__": fValue = MoneyFmt("123.456") print fValue fValue.update("1234") print fValue newfValue = MoneyFmt("111.222") print newfValue + fValue程序输出:
>>> 123.456000 1234.000000 234.678
这里采起的策略是:直接从float派生。通过测试,虽然有getattr,可是调用float的函数仍是得显式进行调用。好比newfValue.__add__(fValue)
13-19. 映射类型子类化。假设在13.11.3 节中字典的子类,若将keys()方法重写为:
def keys(self):
return sorted(self.keys())
(a) 当方法keys()被调用,结果如何?
(b) 为何会有这样的结果?如何使咱们的原解决方案顺利工做?
class SortedKeyDict(dict): def keys(self): return sorted(self.keys()) d = SortedKeyDict((('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))) print d.keys()结果很是明显:无穷递归。。。。。。
若是不调用父类的keys()方法的话,我不知道怎么改。。。。。。。。。
13-20. 类的定制。改进脚本time60.py,见13.13.2 节,示例13.3.
(a) 容许“空”实例化: 若是小时和分钟的值没有给出,默认为零小时、零分钟。
(b) 用零占位组成两位数的表示形式,由于当前的时间格式不符合要求。以下面的示例,wed
应该输出为“12:05.”
(c)除了用hours (hr) 和minutes (min)进行初始化外,还支持如下时间输入格式:
一个由小时和分钟组成的元组(10, 30)
一个由小时和分钟组成的字典({'hr': 10, 'min': 30})
一个表明小时和分钟的字符串("10:30")
附加题: 容许不恰当的时间字符串表示形式,如 “12:5”.
(d) 咱们是否须要实现__radd__()方法? 为何? 若是没必要实现此方法,那咱们何时可
以或应该覆盖它?
(e) __repr__()函数的实现是有缺陷并且被误导的。咱们只是重载了此函数,这样咱们能够省
去使用print 语句的麻烦,使它在解释器中很好的显示出来。可是,这个违背了一个原则:对于可估
值的Python 表达式,repr()老是应该给出一个(有效的)字符串表示形式。12:05 自己不是一个合法
的Python 表达式,但Time60('12:05')是合法的。请实现它。
(f) 添加六十进制(基数是60)的运算功能。下面示例中的输出应该是19:15,而不是18:75:
>>> thu = Time60(10, 30)
>>> fri = Time60(8, 45)
>>> thu + fri
18:75
class Time60(object): def __init__(self, *args1): if type(args1[0]) is tuple: self.hr = args1[0][0] self.min = args1[0][1] elif type(args1[0]) is dict: self.hr = args1[0]["hr"] self.min = args1[0]["min"] elif type(args1[0]) is str: self.hr = int(args1[0].split(":")[0]) self.min = int(args1[0].split(":")[1]) elif type(args1) is tuple: self.hr = args1[0] self.min = args1[1] def __str__(self): return "%02d:%02d" % (self.hr, self.min) def __repr__(self): return repr("%02d:%02d" % (self.hr, self.min)) def __add__(self, other): hour = self.hr + other.hr min = self.min + other.min if min >= 60: min -= 60 hour += 1 return self.__class__(hour, min) def __radd__(self, other): self.hr += other.hr self.min += other.min if self.min >= 60: self.min -= 60 self.hr += 1 return self def __iadd__(self, other): self.hr += other.hr self.min += other.min if self.min >= 60: self.min -= 60 self.hr += 1 return self thu = Time60(10,30) fri = Time60(8,35) print thu + fri thu = Time60((10,30)) fri = Time60({"hr":8,"min":35}) mon = Time60("10:30") print thu + fri print fri + mon thu += fri print thu程序输出:
>>> 19:05 19:05 19:05 19:05