您的位置:首页 > 编程语言 > Python开发

Dave Python 练习十五 -- 面向对象编程

2011-09-16 14:13 387 查看
#encoding=utf-8
### ***************  面向对象编程     ********************

#***********  Part 1: 面向对象编程 ***********************
#面向对象编程踩上了进化的步伐,增强了结构化编程,实现了数据与动作的融合:数据层和逻
#辑层现在由一个可用以创建这些对象的简单抽象层来描述。现实世界中的问题和实体完全暴露了本
#质,从中提供的一种抽象,可以用来进行相似编码,或者编入能与系统中对象进行交互的对象中。
#类提供了这样一些对象的定义,实例即是这些定义的实现。二者对面向对象设计(object-oriented
#design,OOD)来说都是重要的,OOD 仅意味来创建你采用面向对象方式架构来创建系统。

## 1.1 面向对象设计与面向对象编程的关系
#面向对象设计(OOD)不会特别要求面向对象编程语言。事实上,OOD 可以由纯结构化语言来实
#现,比如C,但如果想要构造具备对象性质和特点的数据类型,就需要在程序上作更多的努力。当一
#门语言内建OO 特性,OO 编程开发就会更加方便高效。
#
#另一方面,一门面向对象的语言不一定会强制你写OO 方面的程序。例如C++可以被认为“更好
#的C”;而Java,则要求万物皆类,此外还规定,一个源文件对应一个类定义。然而,在Python 中,
#类和OOP 都不是日常编程所必需的。尽管它从一开始设计就是面向对象的,并且结构上支持OOP,但
#Python 没有限定或要求你在你的应用中写OO 的代码。OOP 是一门强大的工具,不管你是准备进入,
#学习,过渡,或是转向OOP,都可以任意支配。

## 1.2 现实世界中的问题
#考虑用OOD 来工作的一个最重要的原因,在于它直接提供建模和解决现实世界问题和情形的途
#径。比如,让你来试着模拟一台汽车维修店,可以让你停车进行维修。我们需要建两个一般实体:
#处在一个“系统”中并与其交互的人类,和一个修理店,它定义了物理位置,用于人类活动。因为
#前者有更多不同的类型,我将首先对它进行描述,然后描述后者。在此类活动中,一个名为Person
#的类被创建以用来表示所有的人。Person 的实例可以包括消费者(Customer),技工(Mechanic),还
#可能是出纳员(Cashier)。这些实例具有相似的行为,也有独一无二的行为。比如,他们能用声音进
#行交流,都有talk()方法,还有drive_car()方法。不同的是,技工有repair_car()方法,而出纳
#有ring_sale()方法。技工有一个repair_certification 属性,而所有人都有一个drivers_license
#属性。
#
#最后,所有这些实例都是一个检查(overseeing)类RepairShop 的参与者,后者具有一个叫
#operating_hours 的数据属性,它通过时间函数来确定何时顾客来修车,何时职员技工和出纳员来上
#班。RepairShop 可能还有一个AutoBay 类,拥有SmogZone,TireBrakeZone 等实例,也许还有一个叫
#GeneralRepair 的实例。
#
#我们所编的RepairShop 的一个关键点是要展示类和实例加上它们的行为是如何用来对现实生活
#场景建模的。同样,你可以把诸如机场,餐厅,晶蕊,医院,其至一个邮订音乐公司想像为类,它
#们完全具备各自的参与者和功能性。

## 1.3 常用术语
#1.3.1 抽象/实现
#抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于
#描绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
#对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户
#程序应当是透明而且无关的。

#1.3.2 封装/接口
#封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端
#直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的
#一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python 中,所有的类属
#性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防
#措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装
#的数据属性。

#1.3.3 合成
#合成扩充了对类的描述,使得多个不同的类合成为一个大的类,来解决现实问题。合成描述了
#一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为,
#所有这些合在一起,彼此是“有一个”的关系。比如,RepairShop“有一个”技工(应该至少有一个
#吧),还“有一个”顾客(至少一个)。
#这些组件要么通过联合关系组在一块,意思是说,对子组件的访问是允许的(对RepairShop 来
#说,顾客可能请求一个SmogCheck,客户程序这时就是与RepairShop 的组件进行交互),要么是聚合
#在一起,封装的组件仅能通过定义好的接口来访问,对于客户程序来说是透明的。继续我的例子,
#客户程序可能会建立一个SmogCheck 请求来代表顾客,但不能够同RepairShop 的SmogZone 部分进
#行交互,因为SmogZone 是由RepairShop 内部控制的,只能通过smogCheckCar()方法调用。Python
#支持上述两种形式的合成。

# 1.3.4 派生/继承/继承结构
#派生描述了子类的创建,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它
#的自定义操作,都不会修改原类的定义。继承描述了子类属性从祖先类继承这样一种方式。从前面
#的例子中,技工可能比顾客多个汽车技能属性,但单独的来看,每个都“是一个”人,所以,不管
#对谁而言调用talk()都是合法得,因为它是人的所有实例共有的。继承结构表示多“代”派生,可
#以描述成一个“族谱”,连续的子类,与祖先类都有关系。

# 1.3.5 泛化/特化
#泛化表示所有子类与其父类及祖先类有一样的特点,所以子类可以认为同祖先类是“是一个”
#的关系,因为一个派生对象(实例)是祖先类的一个“例子”。比如,技工“是一个”人,车“是一
#个”交通工具,等等。在上面我们间接提到的族谱图中,我们可以从子类到祖先类画一条线,表示
#“是一个”的关系。特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。

#1.3.5 多态
#多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的
#类。多态表明了动态(又名,运行时)绑定的存在,允计重载及运行时类型确定和验证。

# 1.3.6 自省/反射
#自省表示给予你,程序员,某种能力来进行像“手工类型检查”的工作,它也被称为反射。这
#个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么
#能力,这样的功能不是很好吗?这是一项强大的特性,在本章中,你会时常遇到。如果Python 不
#支持某种形式的自省功能,dir()和type()内建函数,将很难正常工作。请密切关注这些调用,还
#有那些特殊属性,像__dict__,__name__及__doc__。可能你对其中一些已经很熟悉了!

#***********  Part 2: 类  ***********************
#类是一种数据结构,我们可以用它来定义对象,后者把数据值和行为特性融合在一起。
#类是现实世界的抽象的实体以编程形式出现。实例是这些对象的具体化。

## 2.1 创建类
#类的定义:
#class ClassName(object):
#    'class documentation string' #类文档字符串
#    class_suite #类体
#
#当你创建一个类,你就实际创建了一个你自己的数据类型。尽管类是对象,但正被定义时,它们还不是对象的实现。
#
#类允许派生。你可以创建一个子类,它也是类,而且继续了父类所有的特征和属性。从Python2.2 开始,你也可以从内建类型中派生子类,而不是仅仅从其它类。
#
#注意类的参数,object 是“所有类之母”。如果你的类没有继承任何其他父类,object 将作为默认的父类。它位于所有类继承结构的最上层。

#基类是一个或多个用于继承的父类的集合;类体由所有声明语句,类成员定义,数据属性和函数组成。
#类通常在一个模块的顶层进行定义,以便类实例能够在类所定义的源代码文件中的任何地方被创建.

## 2.2 声明与定义
#对于Python 函数来说,声明与定义类没什么区别,因为他们是同时进行的,定义(类体)紧跟
#在声明(含class 关键字的头行[header line])和可选(但总是推荐使用)的文档字符串后面。同时,
#所有的方法也必须同时被定义。如果对OOP 很熟悉,请注意Python 并不支持纯虚函数(像C++)或
#者抽象方法(如在JAVA 中),这些都强制程序员在子类中定义方法。作为替代方法,你可以简单地
#在基类方法中引发NotImplementedError 异常,这样可以获得类似的效果。

#***********  Part 3: 类属性  ***********************
#属性就是属于另一个对象的数据或者函数元素,可以通过我们熟悉的句点属性
#标识法来访问。一些Python 类型比如复数有数据属性(实部和虚部),而另外一些,像列表和字典,拥有方法(函数属性)。

#有关属性的一个有趣的地方是,当你正访问一个属性时,它同时也是一个对象,拥有它自己的
#属性,可以访问,这导致了一个属性链,比如,myThing,subThing,subSubThing.等等。常见例子如下:
#    sys.stdout.write('foo')
#    print myModule.myClass.__doc__
#    myList.extend(map(upper, open('x').readlines()))

## 3.1 类的数据属性
#数据属性仅仅是所定义的类的变量。它们可以像任何其它变量一样在类创建后被使用,并且,
#要么是由类中的方法来更新,要么是在主程序其它什么地方被更新。即静态变量,或者是静态数据。它们表示这些数据是与它们所
#属的类对象绑定的,不依赖于任何类实例。

#静态成员通常仅用来跟踪与类相关的值。大多数情况下,你会考虑用实例属性,而不是类属性。

#class c1(object):
#    num=100
#
#c2=c1()
#print(c2.num)
##-->100
#c2.num+=1
#print(c2.num)
#-->101

## 3.2 方法
#方法,即类中的函数

#class c1(object):
#    name='dave'
#    def showName(self,name):
#        print('name is:',name)
#
#c2=c1()
#c2.showName('DMM')
#-->
#name is: DMM

#注意self 参数,它在所有的方法声明中都存在。这个参数代表实例对象本身,当你
#用实例调用方法时,由解释器悄悄地传递给方法的,所以,你不需要自己传递self 进来,因为它是
#自动传入的。举例说明一下,假如你有一个带两参数的方法,所有你的调用只需要传递第二个参数,Python 把self 作为第一个参数传递进来

######### 绑定(绑定及非绑定方法)
#为与OOP 惯例保持一致,Python 严格要求,没有实例,方法是不能被调用的。这种限制即Python所描述的绑定概念(binding),
#在此,方法必须绑定(到一个实例)才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能确保调用成功。
#然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的.

## 3.3 决定类的属性
#要知道一个类有哪些属性,有两种方法。最简单的是使用dir()内建函数。另外是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。

#class c1(object):
#    name='dave'
#    def showName(self,name):
#        print('name is:',name)

#print(dir(c1))
#-->
#['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__',
#'__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__',
#'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
#'__str__', '__subclasshook__', '__weakref__', 'name', 'showName']
#注意dir 返回的是列表

#print(c1.__dict__)
#-->
#{'__module__': '__main__', 'name': 'dave', '__dict__': <attribute '__dict__' of 'c1' objects>,
#'__weakref__': <attribute '__weakref__' of 'c1' objects>, '__doc__': None,
#'showName': <function showName at 0x00000000025280C8>}
#注意__dict__ 返回的是字典

## 3.4 特殊的类属性
#C.__name__ 类C的名字(字符串)
#C.__doc__ 类C的文档字符串
#C.__bases__ 类C的所有父类构成的元组
#C.__dict__ 类C的属性
#C.__module__ 类C定义所在的模块(1.5 版本新增)
#C.__class__ 实例C对应的类(仅新式类中)

#class c1(object):
#    def showName(self,name):
#        print('name is:',name)
#
#class c2(object):
#    'This is the name'
#    def showName(self,name):
#        print('name is:',name)

#__name__是给定类的字符名字。它适用于那种只需要字符串(类对象的名字),而非类对象本身
#的情况。甚至一些内建的类型也有这个属性,我们将会用到其中之一来展示__name__字符串的益处。

#__doc__是类的文档字符串,与函数及模块的文档字符串相似,必须紧随头行(header line)
#后的字符串。文档字符串不能被派生类继承,也就是说派生类必须含有它们自己的文档字符串。

#c3=c1()
#c4=c2()
#print(c3.__doc__)
#print(c4.__doc__)
#-->
#None  #c1 类我们没有定义文档字符串,所以显示为None
#This is the name

#Python 支持模块间的类继承

#***********  Part 4: 实例  ***********************
#如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。
#实例是那些主要用在运行期时的对象,类被实例化得到实例,该实例的类型就是这个被实例化的类。

## 4.1 初始化:通过调用类对象来创建实例
#实例化的实现,可以使用函数操作符:
#class c1(object):
#    def showName(self,name):
#        print('name is:',name)

#c3=c1()

## 4.2 __init__() "构造器"方法
#当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python 检查是否实现了__init__()方法。
#默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作.任何所需的特定操作,
#都需要程序员实现__init__(),覆盖它的默认行为。如果__init__()没有实现,则返回它的对象,实例化过程完毕。
#
#然而,如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递
#进去,像标准方法调用一样。调用类时,传进的任何参数都交给了__init__()。实际中,你可以想
#像成这样:把创建实例的调用当成是对构造器的调用。

#__init__()是很多为类定义的特殊方法之一。其中一些特殊方法是预定义的,缺省情况下,不
#进行任何操作,比如__init__(),要定制,就必须对它进行重载,还有些方法,可能要按需要去实现.

## 4.3 __new__() “构造器”方法
#与__init__()相比,__new__()方法更像一个真正的构造器。Python 用户可以对内建类型进行派生,
#因此,需要一种途径来实例化不可变对象,比如,派生字符串,数字,等等

#在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例
#化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。

#__new__()必须返回一个合法的实例,这样解释器在调用__init__()时,就可以把这个实例作为self 传给它。调用父类的__new__()
#来创建对象,正像其它语言中使用new 关键字一样。__new__()和__init__()在类创建时,都传入了(相同)参数。

## 4.4 __del__() "解构器"方法
#由于Python 具有垃圾对象回收机制(靠引用计数),这个函数要直到该实例对象所有的引用都被清除掉后才会执行。
#Python 中的解构器是在实例释放前提供特殊处理功能的方法,它们通常没有被实现,因为实例很少被显式释放。

#核心笔记:跟踪实例
#Python 没有提供任何内部机制来跟踪一个类有多少个实例被创建了,或者记录这些实例是些什
#么东西。如果需要这些功能,你可以显式加入一些代码到类定义或者__init__()和__del__()中去。

#最好的方式是使用一个静态成员来记录实例的个数。靠保存它们的引用来跟踪实例对象是很危险的,
#因为你必须合理管理这些引用,不然,你的引用可能没办法释放(因为还有其它的引用)!看下面一个例子:

#class InstCt(object):
#    count = 0 # count is class attr count 是一个类属性
#    def __init__(self): # increment count 增加count
#        InstCt.count += 1
#    def __del__(self): # decrement count 减少count
#        InstCt.count -= 1
#    def howMany(self): # return count 返回count
#        print(InstCt.count)
#
#c1=InstCt()
#c2=InstCt()
#
#c1.howMany()
#c2.howMany()
#c1.__del__()
#c2.howMany()
#-->
#2
#2
#1

#***********  Part 5: 实例属性  ***********************
#实例仅拥有数据属性(方法严格来说是类属性),后者只是与某个类的实例相关联的数据值,并
#且可以通过句点属性标识法来访问。这些值独立于其它实例或类。当一个实例被释放后,它的属性同时也被清除了。

## 5.1 “实例化”实例属性(或创建一个更好的构造器)
#设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。构造器__init()__是设置这些属性的关键点之一。

####在构造器中首先设置实例属性
#构造器是最早可以设置实例属性的地方,因为__init__()是实例创建后第一个被调用的方法。
#再没有比这更早的可以设置实例属性的机会了。一旦__init__()执行完毕,返回实例对象,即完成了实例化过程。

####默认参数提供默认的实例安装
#在实际应用中,带默认参数的__init__()提供一个有效的方式来初始化实例。在很多情况下,
#默认值表示设置实例属性的最常见的情况,如果提供了默认值,我们就没必要显式给构造器传值了。
#需要明白一点,默认参数应当是不变的对象;像列表(list)和字典(dictionary)这样的可变对象可以扮演静态数据,
#然后在每个方法调用中来维护它们的内容。

#class c1(object):
#    def __init__(self,name,sex='man'):
#        print('name:%s,sex:%s' %(name,sex))

#c2=c1('dave')
#-->name:dave,sex:man

#注意:__init__()应当返回None
#你也知道,采用函数操作符调用类对象会创建一个类实例,也就是说这样一种调用过程返回的对象就是实例。

#如果定义了构造器,它不应当返回任何对象,因为实例对象是自动在实例化调用后返回的。相
#应地,__init__()就不应当返回任何对象(应当为None);否则,就可能出现冲突,因为只能返回实例。

#class c1(object):
#    def __init__(self,name,sex='man'):
#        print('name:%s,sex:%s' %(name,sex))
#        return 1
#c2=c1('dave')
#-->TypeError: __init__() should return None, not 'int'

## 5.2 查看实例属性
#可是使用dir() 或者类的__dict__属性查看。 这个在3.3 节有示例。

## 5.3 特殊的实例属性
#实例仅有两个特殊属性。对于任意对象I:
#    I.__class__     实例化I 的类
#    I.__dict__      I 的属性

#class C(object): # define class 定义类
#    pass
#
#c = C() # create instance 创建实例
#print(dir(c)) # instance has no attributes 实例还没有属性
#print(c.__dict__) # yep, definitely no attributes 也没有属性
#print(c.__class__) # class that instantiated us 实例化c 的类
#-->
#[]
#{}
#<class '__main__.C'>

#c 现在还没有数据属性,添加一些再来检查__dict__属性,看是否添加成功了:
#c.foo = 1
#c.bar = 'SPAM'
#print(c.__dict__)
#-->
#{'foo': 1, 'bar': 'SPAM'}
#dict__属性由一个字典组成,包含一个实例的所有属性。键是属性名,值是属性相应的数据值。字典中仅有实例属性,没有类属性或特殊属性。

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

#x = 3+0.14j
#print(x.__class__)
#--><class 'complex'>
#print(dir(x))
#-->['__abs__', '__add__', '__bool__', '__class__', '__delattr__', '__divmod__', '__doc__', '__eq__',
#    '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__',
#    '__hash__', '__init__', '__int__', '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__',
#    '__new__', '__pos__', '__pow__', '__radd__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__',
#    '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__',
#    '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']

#print(x.imag)
#-->0.14

#在内建类型中,不存在__dict__属性:
#print(x.__dict__)
#-->AttributeError: 'complex' object has no attribute '__dict__'

## 5.5 实例属性 vs 类属性
#类属性仅是与类相关的数据值,和实例属性不同,类属性和实例无关。这些值像静态成员那样被引用,即使在多次实例化中调用类,
#它们的值都保持不变。不管如何,静态成员不会因为实例而改变它们的值,除非实例中显式改变它
#们的值。(实例属性与类属性的比较,类似于自动变量和静态变量,但这只是笼统的类推。

#类和实例都是名字空间。类是类属性的名字空间,实例则是实例属性的。

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

###从实例中访问类属性须谨慎
#与通常Python 变量一样,任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并
#且对其赋值。如果类属性中存在同名的属性,有趣的副作用即产生。

###类属性持久性
#静态成员,如其名所言,任凭整个实例(及其属性)的如何进展,它都不理不采(因此独立于
#实例)。同时,当一个实例在类属性被修改后才创建,那么更新的值就将生效。类属性的修改会影响
#到所有的实例

#***********  Part 6: 校对----绑定和方法调用  ***********************
#方法仅仅是类内部定义的函数。其次,方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是
#绑定到那个实例了。没有实例时方法就是未绑定的。最后,任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象。

#核心笔记:self 是什么?
#self 变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是
#作为第一个参数传递的,self 被选中用来代表实例。你必须在方法声明中放上self(你可能已经注
#意到了这点),但可以在方法中不使用实例(self)。如果你的方法中没有用到self , 那么请考虑创建
#一个常规函数,除非你有特别的原因。毕竟,你的方法代码没有使用实例,没有与类关联其功能,
#这使得它看起来更像一个常规函数。在其它面向对象语言中,self 可能被称为 this。

## 6.1 调用绑定方法
#方法,不管绑定与否,都是由相同的代码组成的。唯一的不同在于是否存在一个实例可以调用
#此方法。在很多情况下,程序员调用的都是一个绑定的方法。假定现在有一个 MyClass 类和此类的
#一个实例 mc,而你想调用MyClass.foo()方法。因为已经有一个实例,你只需要调用mc.foo()就可
#以。记得self 在每一个方法声明中都是作为第一个参数传递的。当你在实例中调用一个绑定的方法
#时,self 不需要明确地传入了。这算是"必须声明self 作为第一个参数"对你的报酬。当你还没有
#一个实例并且需要调用一个非绑定方法的时候你必须传递self 参数。

## 6.2 调用非绑定方法
#调用非绑定方法并不经常用到。需要调用一个还没有任何实例的类中的方法的一个主要的场景
#是:你在派生一个子类,而且你要覆盖父类的方法,这时你需要调用那个父类中想要覆盖掉的构造方法。
#
#在子类构造器中调用父类的构造器并且明确地传递(父类)构造器所需要的self 参数(因为我们没有一个父类的实例)。
#子类中 __init__() 的第一行就是对父类__init__()的调用。我们通过父类名来调用它,并且传递给它 self 和其他所需要的
#参数。一旦调用返回,我们就能定义那些与父类不同的仅存在我们的(子)类中的(实例)定制。

#***********  Part 7: 静态方法和类方法  ***********************
#静态方法是类中的函数(不需要实例).

## 7.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()
#tsm.foo()
#-->
#calling static method foo()
#calling static method foo()

#tcm = TestClassMethod()
#TestClassMethod.foo()
#tcm.foo()
#-->
#calling class method foo()
#foo() is part of class: TestClassMethod
#calling class method foo()
#foo() is part of class: TestClassMethod

## 7.2 使用函数修饰符
#看到像foo=staticmethod(foo)这样的代码会刺激一些程序员。很多人对这样一个没意义
#的语法感到心烦,即使van Rossum 曾指出过,它只是临时的,有待社区对些语义进行处理。
#
#可以用它把一个函数应用到另个函数对象上, 而且新函数对象依然绑定在原来的变量。我们正是需要它来
#整理语法。通过使用decorators,我们可以避免像上面那样的重新赋值:

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

#***********  Part 8: 组合  ***********************
#一个类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去,
#同其它数据类型及逻辑执行流混合使用。有两种方法可以在你的代码中利用类。第一种是组合
#(composition)。就是让不同的类混合并加入到其它类中,来增加功能和代码重用性。你可以在一个
#大点的类中创建你自已的类的实例,实现一些其它属性和方法来增强对原来的类对象。另一种方法
#是通过派生.

#class NewAddrBookEntry(object): # class definition 类定义
#    'new address book entry class'
#    def __init__(self, nm, ph): # define constructor 定义构造器
#        self.name = Name(nm) # create Name instance 创建Name 实例
#        self.phone = Phone(ph) # create Phone instance 创建Phone 实例
#        print('Created instance for:', self.name)
#
#NewAddrBookEntry 类由它自身和其它类组合而成。

#***********  Part 9: 子类和派生  ***********************
#当类之间有显著的不同,并且(较小的类)是较大的类所需要的组件时,组合表现得很好,但当
#你设计“相同的类但有一些不同的功能”时,派生就是一个更加合理的选择了。

#OOP 的更强大方面之一是能够使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响
#系统中使用现存类的其它代码片段。OOD 允许类特征在子孙类或子类中进行继承。这些子类从基类(或
#称祖先类,超类)继承它们的核心属性。而且,这些派生可能会扩展到多代。在一个层次的派生关
#系中的相关类(或者是在类树图中垂直相邻)是父类和子类关系。从同一个父类派生出来的这些类
#(或者是在类树图中水平相邻)是同胞关系。父类和所有高层类都被认为是祖先。

#####创建子类
#创建子类的语法看起来与普通(新式)类没有区别,一个类名,后跟一个或多个需要从其中派生的父类:
#class SubClassName (ParentClass1[, ParentClass2, ...]):
#    'optional class documentation string'
#    class_suite
#如果你的类没有从任何祖先类派生,可以使用object 作为父类的名字。
#
#经典类的声明唯一不同之处在于其没有从祖先类派生---此时,没有圆括号:
#class ClassicClassWithoutSuperclasses:
#    pass

#class Parent(object): # define parent class 定义父类
#    def parentMethod(self):
#        print('calling parent method')
#
#class Child(Parent): # define child class 定义子类
#    def childMethod(self):
#        print('calling child method')
#
#p = Parent() # instance of parent 父类的实例
#p.parentMethod()
#-->
#calling parent method
#
#c = Child() # instance of child 子类的实例
#c.childMethod() # child calls its method 子类调用它的方法
#c.parentMethod() # calls parent's method 调用父类的方法
#
#-->
#calling child method
#calling parent method

#***********  Part 10: 继承  ***********************
#继承描述了基类的属性如何“遗传”给派生类。一个子类可以继承它的基类的任何属性,不管是数据属性还是方法。

#class Parent(object): # define parent class 定义父类
#    def parentMethod(self):
#        print('calling parent method')
#
#class Child(Parent): # define child class 定义子类
#    def childMethod(self):
#        print('calling child method')
#
#c = Child() # instantiate child 实例化子类
#print(c.__class__) # child "is a" parent 子类“是一个”父类
#--><class '__main__.Child'>
#print(Child.__bases__) # child's parent class(es) 子类的父类
#-->(<class '__main__.Parent'>,)

## 10.1 __bases__类属性
#对任何(子)类,它是一个包含其父类(parent)的集合的元组。注意,我们明确指出“父类”是相对所有基类(它包括了所有祖先类)
#而言的。那些没有父类的类,它们的__bases__属性为空。

#class A(object): pass # define class A 定义类A
#class B(A): pass # subclass of A A 的子类
#class C(B): pass # subclass of B (and indirectly, A) B 的子类(A 的间接子类)
#class D(B,A): pass # subclass of A and B A,B 的子类, 注意,这里B 要写在前面
#
#print(A.__bases__)
#print(C.__bases__)
#print(D.__bases__)
#-->
#(<class 'object'>,)
#(<class '__main__.B'>,)
#(<class '__main__.B'>, <class '__main__.A'>)

## 10.2 通过继承覆盖(Overriding)方法

#创建父类
#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() 方法
#被覆盖。覆盖方法的原因之一是,你的子类可能需要这个方法具有特定或不同的功能.
#
#如果想继续调用那个被我覆盖的基类方法,就需要去调用一个未绑定的基类方法,明确给出子类的实例.

#P.foo(c)
#-->Hi, I am P-foo()

#可以在子类的重写方法里显式地调用基类方法:
#class C(P):
#    def foo(self):
#        P.foo(self)
#        print('Hi, I am C-foo()')

#注意,在这个(未绑定)方法调用中我们显式地传递了self. 一个更好的办法是使用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__
#类似于上面的覆盖非特殊方法,当从一个带构造器 __init()__的类派生,如果你不去覆盖
#__init__(),它将会被继承并自动调用。但如果你在子类中覆盖了__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
#
#Python 使用基类名来调用类方法,对应在JAVA 中,是用关键字super 来实现的,这就是super()
#内建函数引入到Python 中的原因,这样你就可以“依葫芦画瓢”了:
#class C(P):
#    def __init__(self):
#        super(C, self).__init__()
#        print "calling C's constructor"
#使用super()的漂亮之处在于,你不需要明确给出任何基类名字...“跑腿事儿”,它帮你干了!
#使用super()的重点,是你不需要明确提供父类。这意味着如果你改变了类继承关系,你只需要改一
#行代码(class 语句本身)而不必在大量代码中去查找所有被修改的那个类的名字。

#***********  Part 11: 类、实例和其他对象的内建函数  ***********************

## 11.1 issubclass()
#issubclass() 布尔函数判断一个类是另一个类的子类或子孙类。它有如下语法:
#    issubclass(sub, sup)
#issubclass() 返回True 的情况:给出的子类sub 确实是父类sup 的一个子类(反之,则为False)。
#这个函数也允许“不严格”的子类,意味着,一个类可视为其自身的子类,所以,这个函数如果当
#sub 就是sup,或者从sup 派生而来,则返回True。(一个“严格的”子类是严格意义上的从一个类
#派生而来的子类。)
#从Python 2.3 开始,issubclass()的第二个参数可以是可能的父类组成的tuple(元组),这时,
#只要第一个参数是给定元组中任何一个候选类的子类时,就会返回True。

#class P(object):
#    def __init__(self):
#        print("calling P's constructor")
#
#class C(P):
#    def __init__(self):
#        print("calling C's constructor")
#
#print(issubclass(C,P))
#-->True

## 11.2 isinstance()
#isinstance() 布尔函数在判定一个对象是否是另一个给定类的实例时,非常有用。它有如下语法:
#    isinstance(obj1, obj2)
#isinstance()在obj1 是类obj2 的一个实例,或者是obj2 的子类的一个实例时,返回True(反之,则为False).

#class P(object):
#    def __init__(self):
#        print("calling P's constructor")
#
#p1=P()
#print(isinstance(p1,P))
#-->True

#也可以使用isinstance()来检查一个对象obj1 是否是obj2 的类型:
#print(isinstance(4,int))
#-->True

## 11.3 hasattr(), getattr(),setattr(), delattr()
#*attr()系列函数可以在各种对象下工作,不限于类(class)和实例(instances)。
#
#hasattr()函数是Boolean 型的,它的目的就是为了决定一个对象是否有一个特定的属性,一
#般用于访问某属性前先作一下检查。getattr()和setattr()函数相应地取得和赋值给对象的属性,
#getattr()会在你试图读取一个不存在的属性时,引发AttributeError 异常,除非给出那个可选的
#默认参数。setattr()将要么加入一个新的属性,要么取代一个已存在的属性。而delattr()函数会
#从一个对象中删除属性。

#class myClass(object):
#    def __init__(self):
#        self.foo = 100
#
#myInst = myClass()
#print(hasattr(myInst, 'foo'))
#print(getattr(myInst, 'foo'))
#print(hasattr(myInst, 'bar'))
#-->
#True
#100
#False

## 11.4 dir()
#    dir()作用在实例上(经典类或新式类)时,显示实例变量,还有在实例所在的类及所有它
#的基类中定义的方法和类属性。
#    dir()作用在类上(经典类或新式类)时,则显示类以及它的所有基类的__dict__中的内容。
#但它不会显示定义在元类(metaclass)中的类属性。
#    dir()作用在模块上时,则显示模块的__dict__的内容。(这没改动)。
#    dir()不带参数时,则显示调用者的局部变量。(也没改动)。
#    关于更多细节:对于那些覆盖了__dict__或__class__属性的对象,就使用它们;出于向后兼
#容的考虑,如果已定义了__members__和__methods__,则使用它们。

## 11.5 super()
#这个函数的目的就是帮助程序员找出相应的父类,然后方便调用相关的属性。
#一般情况下,程序员可能仅仅采用非绑定方式调用祖先类方法。使用
#super()可以简化搜索一个合适祖先的任务,并且在调用它时,替你传入实例或类型对象。

#对于每个定义的类,都有一个名为__mro__的属性,它是一个元组,按照他们被搜索时的顺序,列出了备搜索的类。语法如下:
#    super(type[, obj])
#给出type,super()“返回此type 的父类”。如果你希望父类被绑定,你可以传入obj 参数(obj必须是type 类型的).
#否则父类不会被绑定。obj 参数也可以是一个类型,但它应当是type 的一个子类。通常,当给出obj 时:
#    如果 obj 是一个实例,isinstance(obj,type)就必须返回True
#    如果 obj 是一个类或类型,issubclass(obj,type)就必须返回True

#事实上,super()是一个工厂函数,它创造了一个super object,为一个给定的类使用__mro__去查找相应的父类。

#super() 的主要用途, 是来查找父类的属性, 比如,super(MyClass,self).__init__()。
#如果你没有执行这样的查找,你可能不需要使用super()。

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

#class C(object):
#    pass
#
#c = C()
#c.foo = 100
#c.bar = 'Python'
#print(c.__dict__)
#print(vars(c))
#-->
#{'foo': 100, 'bar': 'Python'}
#{'foo': 100, 'bar': 'Python'}

#***********  Part 12: 用特殊方法定制类  ***********************
#特殊方法是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() (从2.2 版开始)
#C.__len__(self)                  “长度”(可用于类);内建len()

####对象(值)比较c
#C.__cmp__(self, obj)            对象比较;内建cmp()
#C.__lt__(self, obj) and         小于/小于或等于;对应<及<=操作符
#C.__gt__(self, obj) and         大于/大于或等于;对应>及>=操作符
#C.__eq__(self, obj) and         等于/不等于;对应==,!=及<>操作符
#
#####属性
#C.__getattr__(self, attr)       获取属性;内建getattr();仅当属性没有找到时调用
#C.__setattr__(self, attr, val)  设置属性
#C.__delattr__(self, attr)       删除属性
#C.__getattribute__(self, attr)  获取属性;内建getattr();总是被调用
#C.__get__(self, attr)          (描述符)获取属性
#C.__set__(self, attr, val)     (描述符)设置属性
#C.__delete__(self, attr)       (描述符)删除属性

####数值类型:二进制操作符
#C.__*add__(self, obj)           加;+操作符
#C.__*sub__(self, obj)           减;-操作符
#C.__*mul__(self, obj)           乘;*操作符
#C.__*div__(self, obj)           除;/操作符
#C.__*truediv__(self, obj)       True 除;/操作符
#C.__*floordiv__(self, obj)      Floor 除;//操作符
#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, i1, i2,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)         给定键如果不存在字典中,则提供一个默认值

## 12.1 简单定制(RoundFloat2)

#类的作用:保存浮点数,四舍五入,保留两位小数位。 通过断言来控制输入类型

#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)
#
#    __repr__ = __str__
#
#C.__str__(self)                 可打印的字符输出;内建str()及print 语句
#C.__repr__(self)                运行时的字符串输出;内建repr() 和‘‘ 操作符

#rfm=RoundFloatManual(8.888)
#print(rfm)
#-->8.89

## 12.2 数值定制(Time60)

#class Time60(object):
#    'Time60 - track hours and minutes'
#
#    def __init__(self, hr, min):
#        'Time60 constructor - takes hours and minutes'
#        self.hr = hr
#        self.min = min
#
#    def __str__(self):
#        'Time60 - string representation'
#        return '%d:%d' % (self.hr, self.min)
#
#    __repr__ = __str__
#
#    def __add__(self, other):
#        'Time60 - overloading the addition operator'
#        return self.__class__(self.hr + other.hr,self.min + other.min)
#
#    def __iadd__(self, other):
#        'Time60 - overloading in-place addition'
#        self.hr += other.hr
#        self.min += other.min
#        return self

## 12.3 迭代器(RandSeq 和AnyIter)

## RandSeq
#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)

## 任意项的迭代器(anyIter.py)
#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

## 12.4 *多类型定制(NumStr)
#class NumStr(object):
#    def __init__(self, num=0, string=''):
#        self.__num= num
#        self.__string = string
#
#    def __str__(self): # define for str()
#        return '[%d :: %r]' %(self.__num, self.__string)
#
#    __repr__ = __str__
#
#    def __add__(self, other): # define for s+o
#        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): # define for o*n
#        if isinstance(num, int):
#            return self.__class__(self.__num__ * num,self.__string__ * num)
#        else:
#            raise TypeError('Illegal argument type for built-in operation')
#
#    def __nonzero__(self): # False if both are
#        return self.__num or len(self.__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)
#-->
#[3 :: 'foo']
#[3 :: 'goo']
#[2 :: 'foo']
#[0 :: '']
#[0 :: 'boo']
#[1 :: '']

#***********  Part 13: 私有化  ***********************
#默认情况下,属性在Python 中都是“public”,类所在模块和导入了类所在模块的其他模块的代码都可以访问到。

## 13.1 双下划线(__)
#Python 为类元素(属性和方法)的私有性提供初步的形式。由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许的。
#实际上,会在名字前面加上下划线和类名。比如上面示例中的:self.__num 属性为例,被“混淆”后,用于访问这个数据值的标识就变成了
#self._NumStr__num。把类名加上后形成的新的“混淆”结果将可以防止在祖先类或子孙类中的同名冲突。

#尽管这样做提供了某种层次上的私有化,但算法处于公共域中并且很容易被“击败”。
#这更多的是一种对导入源代码无法获得的模块或对同一模块中的其他代码的保护机制.

#这种名字混淆的另一个目的,是为了保护__XXX 变量不与父类名字空间相冲突。如果在类中有一
#个__XXX 属性,它将不会被其子类中的__XXX 属性覆盖。(回忆一下,如果父类仅有一个XXX 属性,
#子类也定义了这个,这时,子类的XXX 就是覆盖了父类的XXX,这就是为什么你必须使用PARENT.XXX
#来调用父类的同名方法。) 使用__XXX,子类的代码就可以安全地使用__XXX,而不必担心它会影响
#到父类中的__XXX。

## 13.2 单下划线(_)
#简单的模块级私有化只需要在属性名前使用一个单下划线字符。
#这就防止模块的属性用“from mymodule import *”来加载。这是严格基于作用域的,所以这同样适合于函数。

#***********  Part 14: 授权  ***********************

## 14.1 包装
#“包装”在Python 编程世界中经常会被提到的一个术语。它是一个通用的名字,意思是对一
#个已存在的对象进行包装,不管它是数据类型,还是一段代码,可以是对一个已存在的对象,增加
#新的,删除不要的,或者修改其它已存在的功能。
#包装包括定义一个类,它的实例拥有标准类型的核心行为。

## 14.2 实现授权
#授权是包装的一个特性,可用于简化处理有关dictating 功能,采用已存在的功能以达到最大限度的代码重用。

#包装一个类型通常是对已存在的类型的一些定制。我们在前面提到过,这种做法可以新建,修
#改或删除原有产品的功能。其它的则保持原样,或者保留已存功能和行为。授权的过程,即是所有
#更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
#
#实现授权的关键点就是覆盖__getattr__()方法,在代码中包含一个对getattr()内建函数的调
#用。特别地,调用getattr()以得到默认对象属性(数据属性或者方法)并返回它以便访问或调用。
#特殊方法__getattr__()的工作方式是,当搜索一个属性时,任何局部对象首先被找到(定制的对象)。
#如果搜索失败了,则__getattr__()会被调用,然后调用getattr()得到一个对象的默认行为。

#换言之,当引用一个属性时,Python 解释器将试着在局部名称空间中查找那个名字,比如一个
#自定义的方法或局部实例属性。如果没有在局部字典中找到,则搜索类名称空间,以防一个类属性
#被访问。最后,如果两类搜索都失败了,搜索则对原对象开始授权请求,此时,__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)

#因为所有Python 数值类型,只有复数拥有属性:数据属性,及conjugate()内建方法(求共轭复数)。
#属性可以是数据属性,还可以是函数或方法.

wrappedComplex = WrapMe(3.5+4.2j)
#print(wrappedComplex)
#-->(3.5+4.2j)
#print(wrappedComplex.conjugate())
#print(wrappedComplex.get())
#-->
#(3.5-4.2j)
#(3.5+4.2j)

#对这些属性的访问,是通过getattr()方法,授权给对象.最终调用get()方法没有授权,因为它是为我们的对象定义的----它返回包装的真实的数据对象。


-------------------------------------------------------------------------------------------------------
Blog: http://blog.csdn.net/tianlesoftware Weibo: http://weibo.com/tianlesoftware Email: dvd.dba@gmail.com
DBA1 群:62697716(满); DBA2 群:62697977(满) DBA3 群:62697850(满)
DBA 超级群:63306533(满); DBA4 群: 83829929(满) DBA5群: 142216823(满)
DBA6 群:158654907(满) DBA7 群:69087192(满) DBA8 群:172855474
DBA 超级群2:151508914 DBA9群:102954821 聊天 群:40132017(满)
--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: