第十三章: 面向对象编程
object 是“全部类之母”。若是你的类没有继承任何其余父类,object将做为默认的父类。它位于全部类继承结构的最上层。
经典类和新式类
经典类又称旧类,是指没有直接或间接继承object类的类
新式类必须直接或间接的继承object类
示例:
class cc: #经典类
def__init__(self):
pass
class ccN(object):#新式类,继承了object类
def__init__(self):
pass
c1=cc()
c2=ccN()
print c1.__class__
print type(c1)
print 'dir(c1):',dir(c1)
print c2.__class__
print type(c2)
print 'dir(c2):',dir(c2)
result:
__main__.cc
dir(c1): ['__doc__', '__init__', '__module__']
dir(c2): ['__class__', '__delattr__', '__dict__', '__doc__','__format__', '__getattribute__', '__hash__', '__init__','__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__','__weakref__']
类中的方法和self参数。
code:
class MyDataWithMethod(object):
def printFoo(self):
print 'something like this'
类中全部的方法必须带有一个self的参数,这个参数表明实例对象自己,当你用实例调用方法时,由解释器悄悄地传递给方法的,因此,你不须要本身传递self进来,由于它是自动传入的。举例说明一下,假如你有一个带两参数的方法,全部你的调用只须要传递第二个参数,python 把self做为第一个参数传递进来,若是你犯错的话,也没关系。Python将告诉你传入的参数个数有误。总之,你只会犯一次错,下一次———你固然就记得了!
__init__:__init__()相似于类构造器,,Python建立实例后,在实例化过程当中,调用__init__()方法,当一个类被实例化时,就
能够定义额外的行为,好比,设定初始值或者运行一些初步诊断代码———主要是在实例被建立后,
实例化调用返回这个实例以前,去执行某些特定的任务或设置。
咱们不把输入(raw_input())或输出(print)语句放入函数中,除非预期代码体具备输出的特性
类和实例,类和子类的关系
类中的属性和方法,当类被实例化后,实例化的对象会自动调用类中的__init__函数,其余的函数属性和方法必须显式的调用。实例调用类中的方法不须要传递self参数,可是当子类调用父类的方法时必须加上self参数。
类和子类的关系,子类能够直接继承父类全部的方法和属性,除非子类中重写的父类的某个方法。
核心笔记:命名类、属性和方法
类名一般由大写字母打头。这是标准惯例,能够帮助你识别类,特别是在实例化过程当中(有时看起来像函数调用)。还有,数据属性(译者注:变量或常量)听起来应当是数据值的名字,方法名应当指出对应对象或值的行为。另外一种表达方式是:数据值应该使用名词做为名字,方法使用谓词(动加对象)。数据项是操做的对象、方法应当代表程序员想要在对象进行什么操做。在上面咱们定义的类中,遵循了这样的方针,数据值像“name”phone”和“email”,行为如“updatePhone”,“updateEmail”。这就是常说的“混合记法(mixedCase)”或“骆驼记法(camelCase)。。Python规范推荐使用骆驼记法的下划线方式,好比,“update_phone”,“update_email”。类也要细致命名,像“AddrBookEntry”,“RepairShop”等等就是很好的名字
封装/接口
类方法只能有类的实例来调用,类自己没法调用。
决定类的属性:
有两种方法。最简单的是使用dir()内建函数。另外是经过访问类的字典属性__dict__,这是全部类都具有的特殊属性之
class MyClass(object):
'''MyClass classdefinition
'''
myVersion=1.1
defMyNoActionMethod(self):
pass
dir(MyClass)结果:
['MyNoActionMethod',
'__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__', 'myVersion']
dir()返回的仅是对象的属性的一个名字列表,而__dict__返回的是一个字典,它的键(keys)是属性名,键值(values)是相应的属性对象的数据值。
特殊的类属性:
C.__name__ 类C的名字(字符串)
C.__doc__ 类C的文档字符串
C.__bases__ 类C的全部父类构成的元组
C.__dict__ 类C的属性
C.__module__ 类C定义所在的模块(1.5 版本新增)
C.__class__ 实例C对应的类(仅新式类中)
1.print MyClass.__name__: MyClass
2.print MyClass.__doc__: MyClass class definition
3.print MyClass.__bases__: (,)
4.print MyClass.__dict__: 返回这个类的全部属性和属性值得一个字典
5.print MyClass.__module__: '__main__'
6.print MyClass.__class__:
1.__name__ 得到类或者内建类型的名字
例如:b=type(3) b.__name__ 咱们会得到一个int
2.__doc__是类的文档字符串,与函数及模块的文档字符串类似,必须紧随头行(header line)
后的字符串。文档字符串不能被派生类继承,也就是说派生类必须含有它们本身的文档字符串
3.返回该类的全部的父类
4.前述的__dict__属性包含一个字典,由类的数据属性组成。
5.该类所属于的模块名
类型和类的统一,type(class)->result;, type(int)->result:
__init__() "构造器"方法
当类被调用去实例化一个对象时,首先检查类中是否有__init__方法,若是有调用,而且传进来的全部参赛都给了此方法,若是没有实现此方法只什么都不作返回一个实例对象给程序员。你能够想像成这样:把建立实例的调用当成是对构造器的调用。
_new__()。这个方法还没见过,先放着。。。
__del__().删除实例
实例属性:
实例只有数据属性,能够经过(instance.__dict__)来得到实例属性,他是没有方法属性的,并且数据属性经过实例加上dot加上数据属性名字,来给某个特定的实例添加属性,这个数据属性跟类和其余的实例是没有任何关联的。
__init__()应当返回None,若是定义了构造器,它不该当返回任何对象,由于实例对象是自动在实例化调用后返回的。相
应地,__init__()就不该当返回任何对象(应当为None);不然,就可能出现冲突,由于只能返回实
例。试着返回非None 的任何其它对象都会致使TypeError 异常
内建类型中没有__dict__属性。
实例属性 vs 类属性
实例属性:。你可采用类来访问类属性,若是实例没有同名的属性的话,你也能够用实例来访问。
雷属性:类属性通常经过类名加上dot加上属性名去修改其值,实例名是没法修改类属性的,可是这个只限制类属性是不可改变的,若是累属性是list,或者dict,则既能够经过类型去修改类属性又能够经过实例名去修改类属性。
class Foo(object):
x=[1,2,3]
foo=Foo()
print foo.x
foo.x.append(5)
print Foo.x
这个里面foo.x.append(5) 和Foo.x.append(5)效果是同样的。
总的来讲咱们建议使用类名来修改类属性,而不用去区分类属性是可变仍是不可变的了。
//Python 中绑定(binding)的概念。
方法仅仅是类内部定义的函数,方法只有在其所属的类拥有实例时才能被并且仅能被‘实例’调用,当存在一个实例时方法被认为是绑定到那个实例的,没有实例时方法就没有绑定的。最后任何一个方法定义中的第一个参数都是变量self,他表示调用此方法的实例对象。
这个也有一个例外,好比咱们有一个子类,子类中须要重写父类的中的方法,这个时候父类并无实例,可是咱们在子类中用父类的名字加点加方法名去调用了父类的一个方法了,这个是惟一一种没有实例化而方法被调用的时候。
核心笔记:self 是什么?
self 变量用于在类实例方法中引用方法所绑定的实例。由于方法的实例在任何方法调用中老是
做为第一个参数传递的,self 被选中用来表明实例。你必须在方法声明中放上self(你可能已经注
意到了这点),但能够在方法中不使用实例(self)。若是你的方法中没有用到self , 那么建议建立
一个常规函数,除非你有特别的缘由。毕竟,你的方法代码没有使用实例,没有与类关联其功能,
这使得它看起来更像一个常规函数。在其它面向对象语言中,self 可能被称为 this。
//
静态方法和类方法。
要想在类中定义不带self参数的方法,必需要声明这个方法是静态方法或者类方法。
例子:
静态方法:
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo=staticmethod(foo)#等同于
@staticmethod ,不过要放到foo()方法代码上一行去。 tsm=TestStaticMethod()#this is a static method. tsm.foo() 类方法: class TestClassMethod: #@classmethod def foo(cls): print 'foo() is part ofclass:',cls.__name__ foo=classmethod(foo) tsm=TestClassMethod() tsm.foo() 使用函数修饰符:见上面的例子。 使用别人写的类有两种方法,组合,和派生。 核心笔记:重写__init__不会自动调用基类的__init__ 相似于上面的覆盖非特殊方法,当从一个带构造器 __init()__的类派生,若是你不去覆盖 __init__(),它将会被继承并自动调用。但若是你在子类中覆盖了__init__(),子类被调用 2.7版本后均可以对标准类型子类化了,好比子类化int,float、dict等。 多重继承:当使用多重继承时,有两个不一样的方面要记住。首先,仍是要找到合适的属性。另外一个就是当你重写方法时,如何调用对应父类方 法以“发挥他们的做用”,同时,在子类中处理好本身的义务。 方法解释顺序(MRO):使用广度优先。 新式类也有一个__mro__属性,告诉你查找顺序是怎样的: >>> GC.__mro__ (, , , , , ) 总结 经典类,使用深度优先算法。由于新式类继承自object,新的菱形类继承结构出现,问题也就 接着而来了,因此必须新建一个MRO。 类、实例和其余对象的内建函数 issubclass() 布尔函数判断一个类是另外一个类的子类或子孙类。它有以下语法:issubclass(sub,sup),从Python 2.3开始,issubclass()的第二个参数能够是可能的父类组成的tuple(元组),这时,只要第一个参数是给定元组中任何一个候选类的子类时,就会返回True。 isinstance()布尔函数在断定一个对象是不是另外一个给定类的实例时,很是有用。它有以下语法: isinstance(obj1, obj2) isinstance()在obj1 是类obj2 的一个实例,或者是obj2的子类的一个实例时,返回True(反之,则为False)。 hasattr(), getattr(),setattr(), delattr() *attr()系列函数能够在各类对象下工做,不限类与实例,可是在类与实例中用的很频繁,第一个参数是你要处理的对象,第二个参数是这些属性的字符串名字。 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 >>> setattr(myInst,'bar','my attr')# same asmyInst.bar='my attr' >>> dir(myInst) >>> hasattr(myInst,'bar') True >>> getattr(myInst,'bar') # same as myInst.bar 'my attr' >>> delattr(myInst,'foo') >>> hasattr(myInst,'foo') False >>> dir(myInst) 用dir()列出一个模块全部属性的信息。 ??dir()做用在实例上(经典类或新式类)时,显示实例变量,还有在实例所在的类及全部它的基类中定义的方法和类属性。 ??dir()做用在类上(经典类或新式类)时,则显示类以及它的全部基类的__dict__中的内容。但它不会显示定义在元类(metaclass)中的类属性,即:dir(myClass)==myClass.__dict__+object.__dict__ ?? dir()做用在模块上时,则显示模块的__dict__的内容。(这没改动)。 ?? dir()不带参数时,则显示调用者的局部变量。(也没改动)。 super(): 语法以下:super(type[, obj]) 给出type,super()“返回此type 的父类”。若是你但愿父类被绑定,你能够传入obj 参数(obj必须是type类型的).不然父类不会被绑定。obj 参数也能够是一个类型,但它应当是type 的一个子类。一般,当给出obj 时: ?? 若是 obj 是一个实例,isinstance(obj,type)就必须返回True ?? 若是 obj 是一个类或类型,issubclass(obj,type)就必须返回True vars(): vars()内建函数与dir()类似,只是给定的对象参数都必须有一个__dict__属性。vars()返回一个字典,它包含了对象存储于其__dict__中的属性(键)及值。 实例及其它对象的内建函数 内建函数 描述 issubclass(sub, sup) 若是类sub是类sup 的子类,则返回True,反之,为False。 isinstance(obj1, obj2) 若是实例obj1 是类obj2 或者obj2子类的一个实例;或者若是obj1是obj2 的类型,则返回True;反之,为 False。 hasattr(obj, attr) 若是obj 有属性attr(用字符串给出),返回True,反之,返回表13.3类,实例及其它对象的内建函数( 续) 内建函数 描述 getattr(obj, attr[, default]) 获取obj 的attr 属性;与返回obj.attr相似;若是attr不是obj 的属性,若是提供了默认值,则返回默认 值;否则,就会引起一个AttributeError异常。 setattr(obj, attr, val) 设置obj 的attr属性值为val,替换任何已存在的属性值;否则,就建立属性;相似于obj.attr=val delattr(obj, attr) 从obj 中删除属性attr(以字符串给出);相似于del obj.attr。 dir(obj=None) 返回obj的属性的一个列表;若是没有给定obj,dir()则显示局部名字空间空间中的属性,也就是locals ().keys() super(type, obj=None) 返回一个表示父类类型的代理对象;若是没有传入obj,则返 回的super对象是非绑定的;反之,若是obj 是一个type , issubclass(obj,type)必为True ; 不然,isinstance(obj,type)就必为True。 vars(obj=None) 返回obj的属性及其值的一个字典;若是没有给出obj,vars()显示局部名字空间字典(属性及其值),也 就是locals()。 用特殊方法定制类: 特殊方法 描述 基本定制型 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) a 获取属性;内建getattr();老是被调用 C.__get__(self, attr) (描述符)获取属性 C.__set__(self, attr, val) (描述符)设置属性 C.__delete__(self, attr) (描述符)删除属性 示例: 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): returnself.__class__(self.hr+other.hr,self.min+other.min) #加法运算 __repr__=__str__ def__iadd__(self,other): self.hr+=other.hr self.min+=other.min return self #原位加法运算,即time1通过+=后的id(time1)不变 time1=Time60(3,45) time2=Time60(4,33) print time1 print id(time1) time1+=time2 print id(time1) print time1 重载 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): returnself.__class__(self.__num+other.__num,self.__string+other.__string) else: raiseTypeError,'Illegal argument type for built-in operation' def__mul__(self,num): if isinstance(num,int): returnself.__class__(self.__num*num,self.__string*num) else: raiseTypeError,'illegal 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): returnself.__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*6