一、主题:
面向对象
二、目的:
归纳总结,将之前笔记转化为自己能够记住并理解的东西
三、大纲
1、概述
2、详述
四、内容:
概述
面向对象在很多语言中都会涉及,各种百科给出的答案也是十分详细,但站在当前学习的初级阶段,在此概述时,仅涉及面向对象三大基本特性的内容,即:封装、继承、多态。
封装就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承是指这样的一种能力:它可以使用自己当前现有的所有功能,并且在无需编写原来的类的情况下对这些功能进行扩展。
多态是指一个类实例的相同方法在不同情形下有不同表现形式,多态机制使不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,他们可以通过相同的方式予以调用。
详述
一、封装
封装是做什么的前面已经提及,在这里主要说的是为什么要封装以及如何来封装。
1、 为什么要封装
在说明为什么要封装之前,首先要知道什么才需要进行封装,即封装的主体要确定。确定主体最简单的方式就是反推,现在拿一个类来进行分析:
# -*- coding:utf-8 -*- class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talking(self): print("我叫%s,今年%s岁,我是%s生"%(self.name,self.age,self.gender))
只是从之前的知识上来看,上述代码中存在了数据name,age,gender和函数talking,再观察其他的类会发现,存在的也都为此两类,最多只是形式上不同,不会牵扯进其他新的内容。
那么我们就能够了解到,类中存在数据和函数两种情况。
对于数据的封装,考虑最多的还是安全性,是为了更好的保护隐私。
对于函数的封装,隔离复杂度。举个例子的话就是我们在使用各个系统时,不需要知道其内部是怎么运行的,只需要知道在需要某个功能时,将对应的选择框勾选上即可。
PS.对于函数的封装,多数提到的是方法的封装。其实,方法即为类中定义的函数。函数和方法的封装可参考此处说明http://blog.csdn.net/lastinglate/article/details/7295248
2、怎么进行封装
上述提到的代码其实就已经完成了一个最简单的封装。如果在不考虑面向对象的前提下,封装就是将数据和函数放入类下即可。比如:
class Fangzi(): def __init__(self,weizhi,jiage,mianji): self.weizhi=weizhi self.__jiage=jiage self.mianji=mianji def shuoming(self): ''' 方法主要是对此套房子进行说明 :return: 无返回值 ''' print("这套房子位于%s,单价是%s元"%(self.weizhi,self.__jiage)) def maidiao(self): ''' 方法时用来计算当前房子的总价并返回其值 :return: 房子的总价 ''' self.zongjia=int(self.__jiage)*int(self.mianji) return self.zongjia def swimming(self): ''' 此方法没有啥用 :return: ''' print('小明会游泳')
简单介绍下代码内容:
在此代码中,class为创建类的关键词,表示创建的此项内容为类。
P=Fangzi("1",10000,200) shuxingming=P._Fangzi__jiage #具体格式为 实例化对象._(一个横线)类名__(这两个横线一般为私有属性前面的那个横线)属性名 print(shuxingming)
另一方面,加上面向函数概念的话,那么类就需要做到包含某一客观对象存在的共性。因此,在封装成类的时候,就需要考虑封装的数据和方法是否与成类的函数存在关联。比如上述函数,不应该将swimming方法封装到Fangzi这个类中,毕竟房子与游泳完全没有联系,而且后续使用过程中也不会牵扯到相关方面。这样抽象成的类不符合面向对象思想,甚至还会影响到正常的逻辑思维,严重的会干扰后续系统设计的思路,完全把自己带进沟里。
3、特殊的封装
前面说明了如何进行封装,也介绍了进行封装的形式,基本上类可以用这类形式直接构造出来。但事物总是复杂的,用来描述事物的类自然也需要适应这种复杂。那么除了这种普通的封装形式,自然要延伸出特殊的封装形式。
特殊的封装形式在python中主要包含了以下几种:
1)、classmethod 类方法
方式:在类前使用关键词@classmethod,将方法更改为类方法。
与普通类的不同点:类方法只能使用全局下的属性,而不能使用构造方法中的self.属性
举例:
class Person(): name="li" def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender #若在前加@classmethod,则只能使用全局下的name,而不能使用构造函数中的self.name @classmethod def talk(self): print("%s is talking"%self.name)
调用以上代码
d=Person("lu",26,"male") d.talk()
结果为:
若要测试更改为类方法后,是否还可以调用构造方法中的值,可以取一个全局没有的值,比如age
@classmethod def talk(self): print("%s is talking"%self.age)
运行结果为:
在以上测试中,类方法在括号内的值可以改为cls,不用默认的self。这样可以区分类方法与其余方法的不同,类方法在使用关键词classmethod后,自身变为类方法,而类方法默认的第一个参数变为类本身(此处可以理解为就是类这个代码自身),而不是self代表的实例本身。此处仍旧使用self是为了证实第二步调用self.name报错的直观性,不至于误会是因为没有调用self内的name而导致的出错。
2)、property 属性方法
方式: 在类前面使用关键词@property,将方法改为属性方法
与普通方法的不同:在将方法变为一个属性方法后,调用时就不需要加括号了。 直接调用即可。
调用时不使用括号这种形式正跟属性调用类似,而且配合setter装饰器,可以有效的控制重要函数值的传参。
举例:
class Person(): name="li" def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender @property def talk(self): print("%s is talking"%self.name)
#调用如下: d= Person("liu",26,"male") d.talk
现在结合setter装饰器一块说明:
class Person(): @property def description(self): aihao="打麻将" return aihao #调用如下: d=Person() print(d.description)
此结果很明显,会直接输出打麻将
但下面这些代码,尝试修改descrption的值:
class Person(): @property def description(self): aihao="打麻将" return aihao #调用如下: d=Person() d.description="游泳" print(d.description)
就会发现直接出现报错,提示不能设置属性
若要设置description的属性,则需要
class Person(): #设定登录名是从认证函数中调取出来的,不允许私自更改。 @property def description(self): self.__username="wanglei" return self.__username @description.setter def decs(self,value): self.__username=value return self.__username #调用如下: d=Person() d.description="zhangsan" print(d.description)
3)、staticmethod 静态方法
方式: 在类前面使用关键词@staticmethod,将方法改为静态方法
与普通方法的不同:这样写完以后,方法实际上跟类没什么关系了。相当于跟类的关系截断了。只是名义上归类管理,实际上在静态方法里访问不了实例中的属性,只能获取到类中的属性。
举例:
class Person(): #设定登录名是从认证函数中调取出来的,不允许私自更改。 user="laowang" def test(self): self.username="laoli" @staticmethod def statictest(self): print(self.username,",Hello") #调用如下: d=Person() d.statictest(d)
此处在创建函数时仍旧调用了self,但经过测试,即使此处是self,也是无法调用self下的其他值。这样使用只会出现报错,提示无法找到属性username:
如下调用即可正常显示:
class Person(): #设定登录名是从认证函数中调取出来的,不允许私自更改。 user="laowang" @staticmethod def statictest(self): print(self.user,",Hello") #调用如下: d=Person() d.statictest(d)
结果为
4、特殊成员方法
类中除了将方法使用特殊形式封装外,还提供了特殊的成员方法,其形式主要是以为 __方法名__ 这种形式存在,现在主要是介绍下此类特殊成员方法
1)__doc__
作用: 打印这个类的描述信息
示例:
class Person(): __doc__ = "存储了人的行为" def __init__(self,name): self.name=name def damajiang(self): print("%s在打麻将"%self.name) def dushu(self): print("%s在读书"%self.name) d=Person("zhangsan") print(d.__doc__)
2)、__moudile__
作用:表示当前操作的对象在哪个模块中
示例:使用之前写过的一个作业来进行说明。首先开始程序是在一个文件夹下,调用的核心程序是在另一个文件夹下的classset.py文件内,使用__module__就可以直接看到调用了哪个模块的内容。
while True:#开启程序的界面 msg=[] print(display) select=input("请选择:").strip() if select=="1" or select=="管理界面": msg.append("administrator") msg.append(None) adminmgr=AdminMgr(msg) print(adminmgr.__module__) adminmgr.maincore(msg)
#核心程序界面 @denglu def maincore(self): print("欢迎进入选课系统管理界面".center(50,"-")) while True: msg=''' 1.创建校区 2.创建讲师 3.创建课程 4.创建班级 5.查看在职讲师 6.查看在校学生 7.查看开课课程 8.退出 ''' print(msg) select=input("请选择:").strip()
最终得到的结果为:
3)、__class__
作用:表示当前操作的对象的类是什么
示例:
正好可以拿上面的示例来做对比,看下__module__跟__class__得出的结果是否存在不同
while True: msg=[] print(display) select=input("请选择:").strip() if select=="1" or select=="管理界面": msg.append("administrator") msg.append(None) adminmgr=AdminMgr(msg) #将此处代码改为__class__ print(adminmgr.__class__) adminmgr.maincore(msg)
得到的结果为:
可以看出__class__的结果已经精确到了AdminMgr这个类上了。
4)、__init__
作用:构造函数,也成为构造方法
前面已经讲完,不再重复叙述。
5)、__del__ 析构函数 或者是析构方法
作用:当对象在内存中被释放时,自动触发执行。
示例:
可用文字描述下使用场景:比如软件是以server和client的模式存在,当前两者都正常运行,现在server端想要停机维护,如果直接停机的话,可能会引起当前仍旧在线的客户端传输出现问题,那么此时在server在退出时,可以设置析构函数,告诉server在退出时,发送断开信息给客户端,然后再将所有客户端连接断开,这样客户端在收到信息后,可执行自身无法连接至服务器的流程,消除了两者都可能引起崩溃的情况。
6)、__call__
作用:对象后面加扩展,直接执行
示例:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("%s is talking"%self.name) def __call__(self, *args, **kwargs): print("this is %s call"%self.name) d=Person("liu",26,"male") d()
7)、__dict__
作用:查看对象中所有的成员
PS.类.__dict__ 打印类里的所有属性,不包括实例属性
实例.__dict__打印所有实例属性,不包括类里的属性
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("talking") d=Person("liu",26,"male") print(d.__dict__) print(Person.__dict__)
结果为:
8)、__str__
作用:打印对象时,会直接将__str__内的返回值打印出来。
示例:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("talking") def __str__(self): return "123" d=Person("liu",26,"male") print(d)
9)、__getitem__
作用:用于索引操作,表示获取数据
示例:
class Value(): def __init__(self): self.dict={ 1:"A", 2:"B", 3:"C", 4:"D" } def __getitem__(self, item): if item in self.dict.keys(): return self.dict[item] s=Value() print(s[1])
10)、__setitem__
作用:用于索引操作,表示设置数据
示例:
# -*- coding:utf-8 -*- class Value(): def __init__(self): self.dict={ 1:"A", 2:"B", 3:"C", 4:"D" } def valuestr(self): return self.dict def __setitem__(self, key, value): self.dict[key]=value def __getitem__(self, item): if item in self.dict.keys(): return self.dict[item] s=Value() s[1]="qwe" print(s[1]) print(s.valuestr())
最后的结果为:
11)、__delitem__
作用:删除给定键对应的元素。
示例:
class Value(): def __init__(self): self.dict={ 1:"A", 2:"B", 3:"C", 4:"D" } def valuestr(self): return self.dict def __setitem__(self, key, value): self.dict[key]=value def __getitem__(self, item): if item in self.dict.keys(): return self.dict[item] def __delitem__(self, key): del self.dict[key] s=Value() s[1]="qwe" del s[2] print(s.valuestr())
最终结果为:
12)、__iter__
作用:用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
13)、__new__
作用:是在新式类中出现的方法,它作用在构造方法建造实例之前.
__new__()可以决定是否 要使用当前类__init__()方法,因为__new__()同样也可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。
从继承的角度来说,如果类在定义时没有重写__new__()方法,Python默认调用该类的父类__new__()方法来构造该类实例,若父类中也未进行重写,那么就一直追溯至object的__new__()方法,因为object是所有新式类的基类。
MyClass = MetaClass()
MyObject = MyClass()
在此还要提到type创建类的使用方法:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("talking") d=Person("liu",26,"male") print(type(d)) print(type(Person))
在以上函数中,d通过Person成为实例化对象,在此,d跟Person其实都是一个实例。通过打印d 和Person的类型来看,二者都是通过类来创建的。
def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print(" %s is talking"%self.name) def eat(self): print("%s is eat, he/she is %s"%(self.name,self.age)) Person=type("Person",(object,),{"talk":talk,"__init__":__init__}) d=Person("liu",25,"male") d.talk()
这两者创建的类最终结果都一样,因此也就了解到了,type实际上就是一个元类,type就是Python在背后用来创建所有类的元类。
继续说明的话希望借助此处的一篇文章来进行进一步的说明。http://www.maiziedu.com/article/27320/
二、继承
继承的模式如下示例:
class A(): def __init__(self,value): self.value=value def output(self): print("A") class B(A): def __init__(self,value,value1): super(B, self).__init__(value) self.value1=value1 def output1(self): print("B") s=B("1","2") s.output() s.output1()
正如其说明,在B原有的功能上,添加了A中的功能,使得由B创建的对象可以调用A中的output方法。
在这里,需要说明的是__init__中的super关键词。
提到super关键词,就需要说明新式类和经典类。python2.X中,默认采用的是经典类,只有显式继承了object的才是新式类。python3.X中,默认采用的都是新式类,不必显式的继承object。
显示继承如下:
class A(object): def __init__(self,value): self.value=value def output(self): print("A")
而super关键词只是用在新式类中的写法,经典类中是使用另外一种来进行书写,写法如下:
A.__init__(self,value)
除了super关键词使用不同以外,还有一个不同点是在继承关系上。
新式类在进行搜索时候,使用的是广度优先搜索,经典类使用的是深度优先搜索。
那下面的例子来进行说明:
class A(): def output(self): print("A") class B(A): def output1(self): print("B") class C(A): def output2(self): print("C") class D(B,C): def output3(self): print("D") s=D()
使用类D创建一个对象,然后调用方法output
那么新式类的搜索方式是在D里先找,找不到的话去B里面找,然后再去C,最后去A。
经典类的顺序则是D---B--A--C。即:“从左至右,深度查找”
三、多态
多态的实例如下:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("%s is talking"%self.name) class Child(Person): def __init__(self,name,age,gender): super(Child, self).__init__(name,age,gender) def talk(self): print("%s is child"%self.name) class Adult(Person): def __init__(self,name,age,gender): super(Adult, self).__init__(name,age,gender) def talk(self): print("%s is adult"%self.name) #此处定义的函数,是独立于类之外的,即为了实现多态,重新定义了一个新的类,让其能够满足一个接口,多种形态。 def talk(obj): obj.talk() d=Child("liu",10,"male") e=Adult("lu",26,"male") talk(d)
结果为:
五、总结
整篇笔记是以面向对象的三大特性为骨架进行填充整理的,基础部分在此多数都已涉及,但此篇归档在__metaclass__和__iter__两个类上尚且存在疑问,后续也会针对相关内容进行查缺补漏。