python 面向对象

二次信任 提交于 2020-02-28 10:22:52

面向对象变成介绍

面向过程编程

核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。主要应用在一旦完成很少修改的地方,如linux内核、git、apache服务器等

优点:极大的降低了程序的设计复杂度

缺点:可扩展性差,改动一个地方很可能要改多个地方,牵一发而动全身

面向对象编程:不是编程的全部,只是用来解决软件可扩展性的

核心是对象(上帝式思维),对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象就是把计算机程序作为一个对象集合,每隔对象都能接收其他对象传来的消息,计算机程序的执行就是就是一系列消息在各个对象之间传递。主要应用在需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等

优点:解决了程序的可扩展性差的问题,对某一个对象修改会立刻反映到整个程序体系,如lol对一个英雄属性的修改(万年削弱从未被加强)

缺点:可控性差,无法预测程序执行结果,如lol每局游戏的结果

类和对象

对象的概念

对象也叫做实例,是通过类产生的,对象就是数据属性和方法属性的结合体,比如lol中的英雄,一个英雄就是一个对象,具备生命值、蓝条攻击力等数据属性,又具备qwer四个技能属于方法属性。

类的概念

类包含了一组对象的相同属性部分,还是说lol中的英雄,所有的英雄都有名字、标签(坦克战士法师)、生命值、蓝条攻击力等,虽然具体的值不同,但是都可以包含在类中去生成。

在python中,用变量表示是数据属性,用函数表示方法属性。

在现实生活中,先有对象后有类,比如先有的人,后有的人类的概念,而在编程中,是先有的类,后有的对象,类生成对象。

声明一个类:和函数的定义类似

定义函数:通过def关键字

1 def functionName(args):
2      '函数文档字符串'
3       函数体 

定义类:通过cless关键字,类的名字一般开头大写,为了和函数区分

 
1 '''
2 class 类名:
3     '类的文档字符串'
4     类体
5 '''
6 #我们创建一个类Data类,
7 class Data:
8     pass 
 

示例:定义一个中国人的类

首先分析中国人的特征,首先国籍必须是属于中国,然后还要是个人,那么人都会做什么(吃喝拉撒睡说话)等等,另外每个人还有自己特有的属性,比如名字身高生日等

伪代码分析(只是分析):

 
chinese
# 例如共有属性有
country='china' #国籍中国
language='chinese'  #语言为中文,地方语言、少数民族语言可列为特有属性
# 会的技能
def talk(self):
    print('is talking')
def eat(self):
    print('is eating')
def sleep(self):
    print('is eating')
def work(self):
    print('is working')
# 等一系列人类含有的技能#特有属性name=nameage=agesex=sex
 

代码定义:__init__函数和self会在下面说明

 
 1 class Chinese:
 2     # 共同的特征:国籍和语言
 3     country = 'China'
 4     language = 'chinese'
 5     # __init__(p1, 'zhangsan', 'man', 73)
 6     def __init__(self,name,sex,age):
 7         #只用于初始化的活,不能有返回值,用于定义特有的属性
 8         self.name=name #p1.name='zhangsan'
 9         self.sex=sex #p1.sex='man'
10         self.age=age #p1.age=73
11     # 共同的技能
12     def talk(self):
13         print('is talking')
14     def eat(self):
15         print('is eating')
16     def sleep(self):
17         print('is eating')
18     def work(self):
19         print('is working')
 

类的属性引用:

 
1 print(Chinese.__dict__)         #以字典的方式返回Chinese的所有属性,或者用dir(类名)查询
2 print(Chinese.language)         #查看Chinese的language属性
3 print(Chinese.work)         #查看Chinese的work属性
4 Chinese.language='putonghua'    #修改类的language属性
5 Chinese.complexion='yellow'     #增加一条肤色属性
6 del Chinese.complexion          #删除肤色属性
 

实例化(生成实例):__init__函数和self说明

1 p1=Chinese('zhangsan','man',73)     #生成一个实例,name='zhangsan'  sex='man'  age=73
2 print(p1.__dict__)       #以字典的方式返回p1的属性,不包含类的共同属性,只有name/sex/age

类名加上括号就是一个实例化的过程,以上生成了一个叫做p1的对象

1 print(p1.__dict__,type(p1))
2 #输出结果为
3 {'name': 'zhangsan', 'sex': 'man', 'age': 73} <class '__main__.Chinese'>

p1.__dict__输出的结果为__init__函数所执行后的结果,而__init__函数的位置参数有四个self,name,sex,age,实例化的过程中传入了'zhangsan','man',73三个参数对应name,sex,age,而self也是一个位置参数,函数部分我们知道位置参数必须要传入值,这里代码自动会把值p1传入,也就是实例的名字p1。

而p1的类型为<class '__main__.Chinese'>,如果查看Chinese的类型会发现和p1的一样,也就是说定义一个类也就是定义了一个类型。

对象的属性引用:

 
1 prit(p1.language)      #p1本身并没有language属性,但是可以查到,自己本身虽然没有,但是从类里可以拿到
2 # print(p1.run())       #报错,因为自己和类里都没有这个属性
3 print(p1.work())
4 p1.city='beijing'       #增加一条属性,只是针对与对象本身,会增加到p1的属性字典里,不会对Chinese有影响
5 del p1.city             #删除一条属性
 

结论:

类的数据属性可以增删查改

对象的数据属性可以增删查改

对象本身并没有函数属性,只有自有的数据属性(__init__函数初始化的属性或者手动添加的属性),但是可以通过类调用到,也就是共有的特征和属性

对象里通用的数据和函数是引用类的名称空间

对象的属性,优先从自己的__dict__字典里找,如果自己的字典里没有,访问类里的,如果类里也没有,报错,自己定义的属性对其他引用了类的相同属性没有影响

对象的用法:

 
class Chinese:
    obj_list=[]
    count=0
    country = 'China'
    language = 'chinese'
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
        self.obj_list.append(name)  #每次实例化,实例都往pbj_list中添加一个名字
        self.count+=1          #每次实例化,实例都将count+1
    def sleep(self):
        print('is eating')
    def work(self):
        print('is working')
p1=Chinese('bob','man',18)
p2=Chinese('natasha','woman',28)
p3=Chinese('hurry','man',10)
print(p1.obj_list,p1.__dict__)
print(p2.obj_list,p2.__dict__)
print(p3.obj_list,p3.__dict__)
print(Chinese.obj_list)
#######分割线君#######
print(p1.count,id(p1.count))
print(p2.count,id(p2.count))
print(p3.count,id(p3.count))
print(Chinese.count,id(Chinese.count))

输出结果:
['bob', 'natasha', 'hurry'] {'name': 'bob', 'sex': 'man', 'age': 18, 'count': 1}
['bob', 'natasha', 'hurry'] {'name': 'natasha', 'sex': 'woman', 'age': 28, 'count': 1}
['bob', 'natasha', 'hurry'] {'name': 'hurry', 'sex': 'man', 'age': 10, 'count': 1}
['bob', 'natasha', 'hurry']
1 1818604608
1 1818604608
1 1818604608
0 1818604576
 

结果发现:每个实例都没有obj_list,但是却有count,一个是可变,一个是不可变,所以类里可变的数据obj_list可以直接被修改,内存id不会改变,而不可变的数据count只能被重新计算,开辟新的内存空间进行引用,是建立在对象的属性中,而非类的属性

 
 1 class Chinese:
 2     obj_list=[]
 3     count=0
 4     country = 'China'
 5     language = 'chinese'
 6     def __init__(self,name,sex,age):
 7         self.name=name
 8         self.sex=sex
 9         self.age=age
10         Chinese.obj_list.append(name)  #每次实例化,Chinese类都往obj_list中添加一个实例名字
11         Chinese.count+=1    #每次实例化,Chinese都将count+1
12     def sleep(self):
13         print('is eating')
14     def work(self):
15         print('is working')
16 p1=Chinese('bob','man',18)
17 p2=Chinese('natasha','woman',28)
18 p3=Chinese('hurry','man',10)
19 print(p1.obj_list,p1.__dict__)
20 print(p2.obj_list,p2.__dict__)
21 print(p3.obj_list,p3.__dict__)
22 print(Chinese.obj_list)
23 #######分割线君#######
24 print(p1.count,id(p1.count))
25 print(p2.count,id(p2.count))
26 print(p3.count,id(p3.count))
27 print(Chinese.count,id(Chinese.count))
28 
29 输出结果
30 ['bob', 'natasha', 'hurry'] {'name': 'bob', 'sex': 'man', 'age': 18}
31 ['bob', 'natasha', 'hurry'] {'name': 'natasha', 'sex': 'woman', 'age': 28}
32 ['bob', 'natasha', 'hurry'] {'name': 'hurry', 'sex': 'man', 'age': 10}
33 ['bob', 'natasha', 'hurry']
34 3 1818604672
35 3 1818604672
36 3 1818604672
37 3 1818604672
 

结果发现:在实例化的过程中,类本身做的属性修改操作,不会对对象造成任何影响,所有的对象都不包含count属性和obj_list属性,都是通过类去调用的。

 
 1 class Chinese:
 2     country = 'China'
 3     language = 'chinese'
 4     def __init__(self,name,sex,age):
 5         self.name=name
 6         self.sex=sex
 7         self.age=age
 8     def sleep(self):
 9         print('%s is eating' %self.name)
10     def work(self):
11         print('%s is working' %self.name)
12 p1=Chinese('bob','man',18)
13 p2=Chinese('natasha','woman',28)
14 # Chinese.work()    #抛出TypeError
15 p1.work()
16 p2.work()
17 #输出结果
18 bob is working
19 natasha is working
 

结果发现:类里面定义的函数只是给对象用的,类本身无法使用这些函数。类定义的只是方法,而方法是给对象绑定的

类的继承与派生

经典类和新式类

在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类)

没有继承object类的子类成为经典类(在python2中,没有继承object的类,以及它的子类,都是经典类)

 
1 class People:
2     pass
3 class Animal:
4     pass
5 class Student(People,Animal): #People、Animal称为基类或父类,Student为子类,Student继承了People和Animal的所有属性
6     pass
7 print(Student.__bases__)    #__bases__方法,查看继承的类的元组
8 print(People.__bases__)
9 print(Animal.__bases__)
 

输出结果:

1 (<class '__main__.People'>, <class '__main__.Animal'>)    #继承了两个父类
2 (<class 'object'>,)    #默认继承了object类
3 (<class 'object'>,)

继承

继承是为了减少代码重用的问题,以减少代码冗余。

继承是一种是什么是什么的关系,例如老师类是人类,而非老师类是生日类

继承类示例:

 
 1 class People:    #定义父类People
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5     def walk(self):
 6         print('%s is walking' %self.name)
 7 
 8 #Teacher类和Student类无任何属性
 9 class Teacher(People):    #Teacher类继承People类的属性
10     pass
11 class Student(People):    #Student类继承People类的属性
12     pass
 

引用测试:

 
 1 t=Teacher('bob',18)    #实例化一个Teacher对象,而非People对象,Student子类同理
 2 print(type(t)) 
 3 print(t.name,t.age)
 4 print(t.__dict__)
 5 t.walk()    #Teacher子类继承了People的属性,使得Teacher子类的对象能够调用到父类的属性
 6 
 7 输出结果:
 8 <class '__main__.Teacher'>
 9 bob 18
10 {'name': 'bob', 'age': 18}
11 bob is walking
 

派生

派生是在子类继承父类的基础上, 定义子类独有的属性,例如Teacher可以有教师等级的划分、有教学课程的划分,但是继承父类People类是没有等级和课程的划分的。

示例:

 
 1 #定义父类People
 2 class People:
 3     def __init__(self, name, age,sex):
 4         self.name = name
 5         self.age = age
 6         self.sex=sex
 7     def walk(self):
 8         print('%s is walking' % self.name)
 9     def test(self):
10         print('test class from father class %s' %self.name)
11 #定义Teacher子类
12 class Teacher(People):
13     school = 'jialidun'
14     def __init__(self, name, age,sex,level,salary):
15         People.__init__(self,name,age,sex)    #继承父类的初始化内容,实例化时候接收的参数name、age、sex会传给People.__init__
16         self.level=level    #派生的独有属性
17         self.salary=salary    #派生的独有属性
18     def teach(self):    #派生的独有属性
19         print('%s is teaching' %self.name)
20     def test(self):    #派生父类的已有属性,对象在进行属性引用的时候会优先引用实例化过程中用到的类
21         People.test(self)
22         print('from teacher')
23 #定义Student子类
24 class Student(People):
25     def __init__(self, name, age,sex,group):
26         People.__init__(self, name, age, sex)
27         self.group=group
28     def study(self):
29             print('%s is studying' %self.name)
 

测试验证:

1 t=Teacher('natasha',18,'male',10,3000) #__init__(t,'natasha',18,'male',10,3000)
2 print(Teacher.__bases__)
3 print(Teacher.__dict__)
4 t.test()

组合

不同于继承,组合是包含的意思,表示一种什么有什么的关系,也是为了减少重复代码的

示例:还是People、Teacher和Student的例子,只是加上了一个Birthday生日类

 
 1 #Birthday类,需要传入年月日
 2 class Birthday:
 3     def __init__(self,year,mon,day):
 4         self.year=year
 5         self.mon=mon
 6         self.day=day
 7     def tell_birth(self):
 8         print('出生于<%s>年 <%s>月 <%s>日' % (self.year,self.mon,self.day))
 9 #People类,需要接受名字年龄年月日,年月日传给Birthday类
10 class People:
11     def __init__(self, name, age, year, mon, day):
12         self.name = name
13         self.age = age
14         #__init__接收的year, mon, day传给Birthday类
15         self.birth = Birthday(year, mon, day)   #包含Birthday类,生日不只是人类才有,其他动物也可以有生日,不同于继承
16     def walk(self):
17         print('%s is walking' % self.name)
18 #Teacher类
19 class Teacher(People):
20     def __init__(self, name, age, year, mon, day,level,salary):
21         #__init__接收的name, age, year, mon, day传给People类
22         People.__init__(self,name,age,year,mon,day)
23         self.level=level
24         self.salary=salary
25     def teach(self):
26         print('%s is teaching' %self.name)
27 #Student类
28 class Student(People):
29     def __init__(self, name, age, year, mon, day,group):
30         People.__init__(self,name,age,year,mon,day)
31         self.group=group
32     def study(self):
33         print('%s is studying' %self.name)
 

测试验证:

 
1 t=Teacher('hurry',18,1990,2,33,10,3000)    #传入的值为Teacher类接收的值
2 print(t.name,t.age)    #对象t的名字和年龄
3 print(t.birth)    #输出的是一个类对象,因为父类People定义的birth属性就是一个类Birthday
4 t.birth.tell_birth()    #查看对象t所继承的People类的birth属性(Birthday类)的tell_birth()属性
5 print(t.birth.year)
6 print(t.birth.mon)
7 print(t.birth.day)
 

接口和抽象类

接口

接口是一组功能的入口,要调用某一组功能,需要通过接口来进行调用,而不需要关注这组功能是如何实现的,要的只是结果。

在类里,接口是提取了一群类共同的函数,可以把接口当做一个函数的集合。

python模仿接口示例:

 
 1 #模仿Linux内文件读写的接口,Linux不管是文本,还是磁盘还是进程都是通过文件去实现的,只不过方法不同,但是没关系
 2 class File:    #定义一个接口类,提供read和write方法,但是一定是pass没有处理过程的,因为功能的实现具体靠的是子类
 3     def read(self): #定接口函数read
 4         pass
 5     def write(self): #定义接口函数write
 6         pass
 7 #定义子类实现读写功能
 8 #文本文件的读写
 9 class Txt(File): #文本,具体实现read和write
10     def du(self):     #注意并不是read
11         print('文本数据的读取方法')
12     def xie(self):    #注意并不是write
13         print('文本数据的写入方法')
14 #硬盘数据的读写
15 class Sata(File): #磁盘,具体实现read和write
16     def read(self):
17         print('硬盘数据的读取方法')
18     def write(self):
19         print('硬盘数据的写入方法')
20 #进程数据的读写
21 class Process(File):
22     def read(self):
23         print('进程数据的读取方法')
24     def write(self):
25         print('进程数据的写入方法')
 

测试验证:硬盘和进程一样,所以制作文本和硬盘的测试即可

硬盘读写测试:

 
1 disk=Sata()    #实例化一个硬盘读写对象
2 disk.read()    #硬盘读
3 disk.write()    #硬盘写
4 
5 输出结果:
6 硬盘数据的读取方法
7 硬盘数据的写入方法
 

文本读写测试:执行后会发现没有任何输出,那是因为txt对象实际上访问的read和write属性并非子类Txt所提供的属性,Txt所提供的属性只是du和xie,但是txt对象有read和write属性,别忘了Txt类是继承了父类File的属性,所以实际上txt对象的read和write属性是父类File提供的

1 txt=Txt()
2 txt.read()
3 txt.write()

正确的做法是将Txt类的du和xie方法改成read和write方法,这么做的意义为归一化

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

抽象类

抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。

抽象类介于接口和归一化中间,用于实现接口的归一化

当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法。即父类限制:

  1、子类必须要有父类的方法

  2、子类实现的方法必须跟父类的方法的名字一样

python的抽象类通过abc模块实现。

接口归一化示例:

 
 1 import abc
 2 class File(metaclass=abc.ABCMeta):  #metaclass指的是元类,边会讲,现在只需记住这个词
 3     @abc.abstractmethod     #抽象方法,即一个装饰器装饰read属性
 4     def read(self):
 5         pass
 6     @abc.abstractmethod      #抽象方法,即一个装饰器装饰write属性
 7     def write(self):
 8         pass
 9 # # 当继承File类时候,如果没有read和write方法,会提示出错TypeError: Can't instantiate abstract class Txt with abstract methods read, write
10 # class Txt(File):
11 #     def du(self):
12 #         print('文本数据的读取方法')
13 #     def xie(self):
14 #         print('文本数据的写入方法')
15 #定义子类具体实现文本的读写操作
16 class Txt(File):
17     def read(self):
18         print('文本数据的读取方法')
19     def write(self):
20         print('文本数据的写入方法')
21 #定义子类具体实现硬盘的读写操作
22 class Sata(File):
23     def read(self):
24         print('硬盘数据的读取方法')
25     def write(self):
26         print('硬盘数据的写入方法')
27 #定义子类具体实现进程的读写操作
28 class Process(File):
29     def read(self):
30         print('进程数据的读取方法')
31     def write(self):
32         print('进程数据的写入方法')
 

测试验证:

 
 1 t=Txt()
 2 t.read()
 3 t.write()
 4 s=Sata()
 5 s.read()
 6 s.write()
 7 输出结果:
 8 文本数据的读取方法
 9 文本数据的写入方法
10 硬盘数据的读取方法
11 硬盘数据的写入方法
 

 

 

面向对象变成介绍

面向过程编程

核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。主要应用在一旦完成很少修改的地方,如linux内核、git、apache服务器等

优点:极大的降低了程序的设计复杂度

缺点:可扩展性差,改动一个地方很可能要改多个地方,牵一发而动全身

面向对象编程:不是编程的全部,只是用来解决软件可扩展性的

核心是对象(上帝式思维),对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象就是把计算机程序作为一个对象集合,每隔对象都能接收其他对象传来的消息,计算机程序的执行就是就是一系列消息在各个对象之间传递。主要应用在需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等

优点:解决了程序的可扩展性差的问题,对某一个对象修改会立刻反映到整个程序体系,如lol对一个英雄属性的修改(万年削弱从未被加强)

缺点:可控性差,无法预测程序执行结果,如lol每局游戏的结果

类和对象

对象的概念

对象也叫做实例,是通过类产生的,对象就是数据属性和方法属性的结合体,比如lol中的英雄,一个英雄就是一个对象,具备生命值、蓝条攻击力等数据属性,又具备qwer四个技能属于方法属性。

类的概念

类包含了一组对象的相同属性部分,还是说lol中的英雄,所有的英雄都有名字、标签(坦克战士法师)、生命值、蓝条攻击力等,虽然具体的值不同,但是都可以包含在类中去生成。

在python中,用变量表示是数据属性,用函数表示方法属性。

在现实生活中,先有对象后有类,比如先有的人,后有的人类的概念,而在编程中,是先有的类,后有的对象,类生成对象。

声明一个类:和函数的定义类似

定义函数:通过def关键字

1 def functionName(args):
2      '函数文档字符串'
3       函数体 

定义类:通过cless关键字,类的名字一般开头大写,为了和函数区分

 
1 '''
2 class 类名:
3     '类的文档字符串'
4     类体
5 '''
6 #我们创建一个类Data类,
7 class Data:
8     pass 
 

示例:定义一个中国人的类

首先分析中国人的特征,首先国籍必须是属于中国,然后还要是个人,那么人都会做什么(吃喝拉撒睡说话)等等,另外每个人还有自己特有的属性,比如名字身高生日等

伪代码分析(只是分析):

 
chinese
# 例如共有属性有
country='china' #国籍中国
language='chinese'  #语言为中文,地方语言、少数民族语言可列为特有属性
# 会的技能
def talk(self):
    print('is talking')
def eat(self):
    print('is eating')
def sleep(self):
    print('is eating')
def work(self):
    print('is working')
# 等一系列人类含有的技能#特有属性name=nameage=agesex=sex
 

代码定义:__init__函数和self会在下面说明

 
 1 class Chinese:
 2     # 共同的特征:国籍和语言
 3     country = 'China'
 4     language = 'chinese'
 5     # __init__(p1, 'zhangsan', 'man', 73)
 6     def __init__(self,name,sex,age):
 7         #只用于初始化的活,不能有返回值,用于定义特有的属性
 8         self.name=name #p1.name='zhangsan'
 9         self.sex=sex #p1.sex='man'
10         self.age=age #p1.age=73
11     # 共同的技能
12     def talk(self):
13         print('is talking')
14     def eat(self):
15         print('is eating')
16     def sleep(self):
17         print('is eating')
18     def work(self):
19         print('is working')
 

类的属性引用:

 
1 print(Chinese.__dict__)         #以字典的方式返回Chinese的所有属性,或者用dir(类名)查询
2 print(Chinese.language)         #查看Chinese的language属性
3 print(Chinese.work)         #查看Chinese的work属性
4 Chinese.language='putonghua'    #修改类的language属性
5 Chinese.complexion='yellow'     #增加一条肤色属性
6 del Chinese.complexion          #删除肤色属性
 

实例化(生成实例):__init__函数和self说明

1 p1=Chinese('zhangsan','man',73)     #生成一个实例,name='zhangsan'  sex='man'  age=73
2 print(p1.__dict__)       #以字典的方式返回p1的属性,不包含类的共同属性,只有name/sex/age

类名加上括号就是一个实例化的过程,以上生成了一个叫做p1的对象

1 print(p1.__dict__,type(p1))
2 #输出结果为
3 {'name': 'zhangsan', 'sex': 'man', 'age': 73} <class '__main__.Chinese'>

p1.__dict__输出的结果为__init__函数所执行后的结果,而__init__函数的位置参数有四个self,name,sex,age,实例化的过程中传入了'zhangsan','man',73三个参数对应name,sex,age,而self也是一个位置参数,函数部分我们知道位置参数必须要传入值,这里代码自动会把值p1传入,也就是实例的名字p1。

而p1的类型为<class '__main__.Chinese'>,如果查看Chinese的类型会发现和p1的一样,也就是说定义一个类也就是定义了一个类型。

对象的属性引用:

 
1 prit(p1.language)      #p1本身并没有language属性,但是可以查到,自己本身虽然没有,但是从类里可以拿到
2 # print(p1.run())       #报错,因为自己和类里都没有这个属性
3 print(p1.work())
4 p1.city='beijing'       #增加一条属性,只是针对与对象本身,会增加到p1的属性字典里,不会对Chinese有影响
5 del p1.city             #删除一条属性
 

结论:

类的数据属性可以增删查改

对象的数据属性可以增删查改

对象本身并没有函数属性,只有自有的数据属性(__init__函数初始化的属性或者手动添加的属性),但是可以通过类调用到,也就是共有的特征和属性

对象里通用的数据和函数是引用类的名称空间

对象的属性,优先从自己的__dict__字典里找,如果自己的字典里没有,访问类里的,如果类里也没有,报错,自己定义的属性对其他引用了类的相同属性没有影响

对象的用法:

 
class Chinese:
    obj_list=[]
    count=0
    country = 'China'
    language = 'chinese'
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
        self.obj_list.append(name)  #每次实例化,实例都往pbj_list中添加一个名字
        self.count+=1          #每次实例化,实例都将count+1
    def sleep(self):
        print('is eating')
    def work(self):
        print('is working')
p1=Chinese('bob','man',18)
p2=Chinese('natasha','woman',28)
p3=Chinese('hurry','man',10)
print(p1.obj_list,p1.__dict__)
print(p2.obj_list,p2.__dict__)
print(p3.obj_list,p3.__dict__)
print(Chinese.obj_list)
#######分割线君#######
print(p1.count,id(p1.count))
print(p2.count,id(p2.count))
print(p3.count,id(p3.count))
print(Chinese.count,id(Chinese.count))

输出结果:
['bob', 'natasha', 'hurry'] {'name': 'bob', 'sex': 'man', 'age': 18, 'count': 1}
['bob', 'natasha', 'hurry'] {'name': 'natasha', 'sex': 'woman', 'age': 28, 'count': 1}
['bob', 'natasha', 'hurry'] {'name': 'hurry', 'sex': 'man', 'age': 10, 'count': 1}
['bob', 'natasha', 'hurry']
1 1818604608
1 1818604608
1 1818604608
0 1818604576
 

结果发现:每个实例都没有obj_list,但是却有count,一个是可变,一个是不可变,所以类里可变的数据obj_list可以直接被修改,内存id不会改变,而不可变的数据count只能被重新计算,开辟新的内存空间进行引用,是建立在对象的属性中,而非类的属性

 
 1 class Chinese:
 2     obj_list=[]
 3     count=0
 4     country = 'China'
 5     language = 'chinese'
 6     def __init__(self,name,sex,age):
 7         self.name=name
 8         self.sex=sex
 9         self.age=age
10         Chinese.obj_list.append(name)  #每次实例化,Chinese类都往obj_list中添加一个实例名字
11         Chinese.count+=1    #每次实例化,Chinese都将count+1
12     def sleep(self):
13         print('is eating')
14     def work(self):
15         print('is working')
16 p1=Chinese('bob','man',18)
17 p2=Chinese('natasha','woman',28)
18 p3=Chinese('hurry','man',10)
19 print(p1.obj_list,p1.__dict__)
20 print(p2.obj_list,p2.__dict__)
21 print(p3.obj_list,p3.__dict__)
22 print(Chinese.obj_list)
23 #######分割线君#######
24 print(p1.count,id(p1.count))
25 print(p2.count,id(p2.count))
26 print(p3.count,id(p3.count))
27 print(Chinese.count,id(Chinese.count))
28 
29 输出结果
30 ['bob', 'natasha', 'hurry'] {'name': 'bob', 'sex': 'man', 'age': 18}
31 ['bob', 'natasha', 'hurry'] {'name': 'natasha', 'sex': 'woman', 'age': 28}
32 ['bob', 'natasha', 'hurry'] {'name': 'hurry', 'sex': 'man', 'age': 10}
33 ['bob', 'natasha', 'hurry']
34 3 1818604672
35 3 1818604672
36 3 1818604672
37 3 1818604672
 

结果发现:在实例化的过程中,类本身做的属性修改操作,不会对对象造成任何影响,所有的对象都不包含count属性和obj_list属性,都是通过类去调用的。

 
 1 class Chinese:
 2     country = 'China'
 3     language = 'chinese'
 4     def __init__(self,name,sex,age):
 5         self.name=name
 6         self.sex=sex
 7         self.age=age
 8     def sleep(self):
 9         print('%s is eating' %self.name)
10     def work(self):
11         print('%s is working' %self.name)
12 p1=Chinese('bob','man',18)
13 p2=Chinese('natasha','woman',28)
14 # Chinese.work()    #抛出TypeError
15 p1.work()
16 p2.work()
17 #输出结果
18 bob is working
19 natasha is working
 

结果发现:类里面定义的函数只是给对象用的,类本身无法使用这些函数。类定义的只是方法,而方法是给对象绑定的

类的继承与派生

经典类和新式类

在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类)

没有继承object类的子类成为经典类(在python2中,没有继承object的类,以及它的子类,都是经典类)

 
1 class People:
2     pass
3 class Animal:
4     pass
5 class Student(People,Animal): #People、Animal称为基类或父类,Student为子类,Student继承了People和Animal的所有属性
6     pass
7 print(Student.__bases__)    #__bases__方法,查看继承的类的元组
8 print(People.__bases__)
9 print(Animal.__bases__)
 

输出结果:

1 (<class '__main__.People'>, <class '__main__.Animal'>)    #继承了两个父类
2 (<class 'object'>,)    #默认继承了object类
3 (<class 'object'>,)

继承

继承是为了减少代码重用的问题,以减少代码冗余。

继承是一种是什么是什么的关系,例如老师类是人类,而非老师类是生日类

继承类示例:

 
 1 class People:    #定义父类People
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5     def walk(self):
 6         print('%s is walking' %self.name)
 7 
 8 #Teacher类和Student类无任何属性
 9 class Teacher(People):    #Teacher类继承People类的属性
10     pass
11 class Student(People):    #Student类继承People类的属性
12     pass
 

引用测试:

 
 1 t=Teacher('bob',18)    #实例化一个Teacher对象,而非People对象,Student子类同理
 2 print(type(t)) 
 3 print(t.name,t.age)
 4 print(t.__dict__)
 5 t.walk()    #Teacher子类继承了People的属性,使得Teacher子类的对象能够调用到父类的属性
 6 
 7 输出结果:
 8 <class '__main__.Teacher'>
 9 bob 18
10 {'name': 'bob', 'age': 18}
11 bob is walking
 

派生

派生是在子类继承父类的基础上, 定义子类独有的属性,例如Teacher可以有教师等级的划分、有教学课程的划分,但是继承父类People类是没有等级和课程的划分的。

示例:

 
 1 #定义父类People
 2 class People:
 3     def __init__(self, name, age,sex):
 4         self.name = name
 5         self.age = age
 6         self.sex=sex
 7     def walk(self):
 8         print('%s is walking' % self.name)
 9     def test(self):
10         print('test class from father class %s' %self.name)
11 #定义Teacher子类
12 class Teacher(People):
13     school = 'jialidun'
14     def __init__(self, name, age,sex,level,salary):
15         People.__init__(self,name,age,sex)    #继承父类的初始化内容,实例化时候接收的参数name、age、sex会传给People.__init__
16         self.level=level    #派生的独有属性
17         self.salary=salary    #派生的独有属性
18     def teach(self):    #派生的独有属性
19         print('%s is teaching' %self.name)
20     def test(self):    #派生父类的已有属性,对象在进行属性引用的时候会优先引用实例化过程中用到的类
21         People.test(self)
22         print('from teacher')
23 #定义Student子类
24 class Student(People):
25     def __init__(self, name, age,sex,group):
26         People.__init__(self, name, age, sex)
27         self.group=group
28     def study(self):
29             print('%s is studying' %self.name)
 

测试验证:

1 t=Teacher('natasha',18,'male',10,3000) #__init__(t,'natasha',18,'male',10,3000)
2 print(Teacher.__bases__)
3 print(Teacher.__dict__)
4 t.test()

组合

不同于继承,组合是包含的意思,表示一种什么有什么的关系,也是为了减少重复代码的

示例:还是People、Teacher和Student的例子,只是加上了一个Birthday生日类

 
 1 #Birthday类,需要传入年月日
 2 class Birthday:
 3     def __init__(self,year,mon,day):
 4         self.year=year
 5         self.mon=mon
 6         self.day=day
 7     def tell_birth(self):
 8         print('出生于<%s>年 <%s>月 <%s>日' % (self.year,self.mon,self.day))
 9 #People类,需要接受名字年龄年月日,年月日传给Birthday类
10 class People:
11     def __init__(self, name, age, year, mon, day):
12         self.name = name
13         self.age = age
14         #__init__接收的year, mon, day传给Birthday类
15         self.birth = Birthday(year, mon, day)   #包含Birthday类,生日不只是人类才有,其他动物也可以有生日,不同于继承
16     def walk(self):
17         print('%s is walking' % self.name)
18 #Teacher类
19 class Teacher(People):
20     def __init__(self, name, age, year, mon, day,level,salary):
21         #__init__接收的name, age, year, mon, day传给People类
22         People.__init__(self,name,age,year,mon,day)
23         self.level=level
24         self.salary=salary
25     def teach(self):
26         print('%s is teaching' %self.name)
27 #Student类
28 class Student(People):
29     def __init__(self, name, age, year, mon, day,group):
30         People.__init__(self,name,age,year,mon,day)
31         self.group=group
32     def study(self):
33         print('%s is studying' %self.name)
 

测试验证:

 
1 t=Teacher('hurry',18,1990,2,33,10,3000)    #传入的值为Teacher类接收的值
2 print(t.name,t.age)    #对象t的名字和年龄
3 print(t.birth)    #输出的是一个类对象,因为父类People定义的birth属性就是一个类Birthday
4 t.birth.tell_birth()    #查看对象t所继承的People类的birth属性(Birthday类)的tell_birth()属性
5 print(t.birth.year)
6 print(t.birth.mon)
7 print(t.birth.day)
 

接口和抽象类

接口

接口是一组功能的入口,要调用某一组功能,需要通过接口来进行调用,而不需要关注这组功能是如何实现的,要的只是结果。

在类里,接口是提取了一群类共同的函数,可以把接口当做一个函数的集合。

python模仿接口示例:

 
 1 #模仿Linux内文件读写的接口,Linux不管是文本,还是磁盘还是进程都是通过文件去实现的,只不过方法不同,但是没关系
 2 class File:    #定义一个接口类,提供read和write方法,但是一定是pass没有处理过程的,因为功能的实现具体靠的是子类
 3     def read(self): #定接口函数read
 4         pass
 5     def write(self): #定义接口函数write
 6         pass
 7 #定义子类实现读写功能
 8 #文本文件的读写
 9 class Txt(File): #文本,具体实现read和write
10     def du(self):     #注意并不是read
11         print('文本数据的读取方法')
12     def xie(self):    #注意并不是write
13         print('文本数据的写入方法')
14 #硬盘数据的读写
15 class Sata(File): #磁盘,具体实现read和write
16     def read(self):
17         print('硬盘数据的读取方法')
18     def write(self):
19         print('硬盘数据的写入方法')
20 #进程数据的读写
21 class Process(File):
22     def read(self):
23         print('进程数据的读取方法')
24     def write(self):
25         print('进程数据的写入方法')
 

测试验证:硬盘和进程一样,所以制作文本和硬盘的测试即可

硬盘读写测试:

 
1 disk=Sata()    #实例化一个硬盘读写对象
2 disk.read()    #硬盘读
3 disk.write()    #硬盘写
4 
5 输出结果:
6 硬盘数据的读取方法
7 硬盘数据的写入方法
 

文本读写测试:执行后会发现没有任何输出,那是因为txt对象实际上访问的read和write属性并非子类Txt所提供的属性,Txt所提供的属性只是du和xie,但是txt对象有read和write属性,别忘了Txt类是继承了父类File的属性,所以实际上txt对象的read和write属性是父类File提供的

1 txt=Txt()
2 txt.read()
3 txt.write()

正确的做法是将Txt类的du和xie方法改成read和write方法,这么做的意义为归一化

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

抽象类

抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。

抽象类介于接口和归一化中间,用于实现接口的归一化

当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法。即父类限制:

  1、子类必须要有父类的方法

  2、子类实现的方法必须跟父类的方法的名字一样

python的抽象类通过abc模块实现。

接口归一化示例:

 
 1 import abc
 2 class File(metaclass=abc.ABCMeta):  #metaclass指的是元类,边会讲,现在只需记住这个词
 3     @abc.abstractmethod     #抽象方法,即一个装饰器装饰read属性
 4     def read(self):
 5         pass
 6     @abc.abstractmethod      #抽象方法,即一个装饰器装饰write属性
 7     def write(self):
 8         pass
 9 # # 当继承File类时候,如果没有read和write方法,会提示出错TypeError: Can't instantiate abstract class Txt with abstract methods read, write
10 # class Txt(File):
11 #     def du(self):
12 #         print('文本数据的读取方法')
13 #     def xie(self):
14 #         print('文本数据的写入方法')
15 #定义子类具体实现文本的读写操作
16 class Txt(File):
17     def read(self):
18         print('文本数据的读取方法')
19     def write(self):
20         print('文本数据的写入方法')
21 #定义子类具体实现硬盘的读写操作
22 class Sata(File):
23     def read(self):
24         print('硬盘数据的读取方法')
25     def write(self):
26         print('硬盘数据的写入方法')
27 #定义子类具体实现进程的读写操作
28 class Process(File):
29     def read(self):
30         print('进程数据的读取方法')
31     def write(self):
32         print('进程数据的写入方法')
 

测试验证:

 
 1 t=Txt()
 2 t.read()
 3 t.write()
 4 s=Sata()
 5 s.read()
 6 s.write()
 7 输出结果:
 8 文本数据的读取方法
 9 文本数据的写入方法
10 硬盘数据的读取方法
11 硬盘数据的写入方法
 

 

 

类的继承与派生

经典类和新式类

在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类)

没有继承object类的子类成为经典类(在python2中,没有继承object的类,以及它的子类,都是经典类)

 
1 class People:
2     pass
3 class Animal:
4     pass
5 class Student(People,Animal): #People、Animal称为基类或父类,Student为子类,Student继承了People和Animal的所有属性
6     pass
7 print(Student.__bases__)    #__bases__方法,查看继承的类的元组
8 print(People.__bases__)
9 print(Animal.__bases__)
 

输出结果:

1 (<class '__main__.People'>, <class '__main__.Animal'>)    #继承了两个父类
2 (<class 'object'>,)    #默认继承了object类
3 (<class 'object'>,)

继承

继承是为了减少代码重用的问题,以减少代码冗余。

继承是一种是什么是什么的关系,例如老师类是人类,而非老师类是生日类

继承类示例:

 
 1 class People:    #定义父类People
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5     def walk(self):
 6         print('%s is walking' %self.name)
 7 
 8 #Teacher类和Student类无任何属性
 9 class Teacher(People):    #Teacher类继承People类的属性
10     pass
11 class Student(People):    #Student类继承People类的属性
12     pass
 

引用测试:

 
 1 t=Teacher('bob',18)    #实例化一个Teacher对象,而非People对象,Student子类同理
 2 print(type(t)) 
 3 print(t.name,t.age)
 4 print(t.__dict__)
 5 t.walk()    #Teacher子类继承了People的属性,使得Teacher子类的对象能够调用到父类的属性
 6 
 7 输出结果:
 8 <class '__main__.Teacher'>
 9 bob 18
10 {'name': 'bob', 'age': 18}
11 bob is walking
 

派生

派生是在子类继承父类的基础上, 定义子类独有的属性,例如Teacher可以有教师等级的划分、有教学课程的划分,但是继承父类People类是没有等级和课程的划分的。

示例:

 
 1 #定义父类People
 2 class People:
 3     def __init__(self, name, age,sex):
 4         self.name = name
 5         self.age = age
 6         self.sex=sex
 7     def walk(self):
 8         print('%s is walking' % self.name)
 9     def test(self):
10         print('test class from father class %s' %self.name)
11 #定义Teacher子类
12 class Teacher(People):
13     school = 'jialidun'
14     def __init__(self, name, age,sex,level,salary):
15         People.__init__(self,name,age,sex)    #继承父类的初始化内容,实例化时候接收的参数name、age、sex会传给People.__init__
16         self.level=level    #派生的独有属性
17         self.salary=salary    #派生的独有属性
18     def teach(self):    #派生的独有属性
19         print('%s is teaching' %self.name)
20     def test(self):    #派生父类的已有属性,对象在进行属性引用的时候会优先引用实例化过程中用到的类
21         People.test(self)
22         print('from teacher')
23 #定义Student子类
24 class Student(People):
25     def __init__(self, name, age,sex,group):
26         People.__init__(self, name, age, sex)
27         self.group=group
28     def study(self):
29             print('%s is studying' %self.name)
 

测试验证:

1 t=Teacher('natasha',18,'male',10,3000) #__init__(t,'natasha',18,'male',10,3000)
2 print(Teacher.__bases__)
3 print(Teacher.__dict__)
4 t.test()

组合

不同于继承,组合是包含的意思,表示一种什么有什么的关系,也是为了减少重复代码的

示例:还是People、Teacher和Student的例子,只是加上了一个Birthday生日类

 
 1 #Birthday类,需要传入年月日
 2 class Birthday:
 3     def __init__(self,year,mon,day):
 4         self.year=year
 5         self.mon=mon
 6         self.day=day
 7     def tell_birth(self):
 8         print('出生于<%s>年 <%s>月 <%s>日' % (self.year,self.mon,self.day))
 9 #People类,需要接受名字年龄年月日,年月日传给Birthday类
10 class People:
11     def __init__(self, name, age, year, mon, day):
12         self.name = name
13         self.age = age
14         #__init__接收的year, mon, day传给Birthday类
15         self.birth = Birthday(year, mon, day)   #包含Birthday类,生日不只是人类才有,其他动物也可以有生日,不同于继承
16     def walk(self):
17         print('%s is walking' % self.name)
18 #Teacher类
19 class Teacher(People):
20     def __init__(self, name, age, year, mon, day,level,salary):
21         #__init__接收的name, age, year, mon, day传给People类
22         People.__init__(self,name,age,year,mon,day)
23         self.level=level
24         self.salary=salary
25     def teach(self):
26         print('%s is teaching' %self.name)
27 #Student类
28 class Student(People):
29     def __init__(self, name, age, year, mon, day,group):
30         People.__init__(self,name,age,year,mon,day)
31         self.group=group
32     def study(self):
33         print('%s is studying' %self.name)
 

测试验证:

 
1 t=Teacher('hurry',18,1990,2,33,10,3000)    #传入的值为Teacher类接收的值
2 print(t.name,t.age)    #对象t的名字和年龄
3 print(t.birth)    #输出的是一个类对象,因为父类People定义的birth属性就是一个类Birthday
4 t.birth.tell_birth()    #查看对象t所继承的People类的birth属性(Birthday类)的tell_birth()属性
5 print(t.birth.year)
6 print(t.birth.mon)
7 print(t.birth.day)
 

接口和抽象类

接口

接口是一组功能的入口,要调用某一组功能,需要通过接口来进行调用,而不需要关注这组功能是如何实现的,要的只是结果。

在类里,接口是提取了一群类共同的函数,可以把接口当做一个函数的集合。

python模仿接口示例:

 
 1 #模仿Linux内文件读写的接口,Linux不管是文本,还是磁盘还是进程都是通过文件去实现的,只不过方法不同,但是没关系
 2 class File:    #定义一个接口类,提供read和write方法,但是一定是pass没有处理过程的,因为功能的实现具体靠的是子类
 3     def read(self): #定接口函数read
 4         pass
 5     def write(self): #定义接口函数write
 6         pass
 7 #定义子类实现读写功能
 8 #文本文件的读写
 9 class Txt(File): #文本,具体实现read和write
10     def du(self):     #注意并不是read
11         print('文本数据的读取方法')
12     def xie(self):    #注意并不是write
13         print('文本数据的写入方法')
14 #硬盘数据的读写
15 class Sata(File): #磁盘,具体实现read和write
16     def read(self):
17         print('硬盘数据的读取方法')
18     def write(self):
19         print('硬盘数据的写入方法')
20 #进程数据的读写
21 class Process(File):
22     def read(self):
23         print('进程数据的读取方法')
24     def write(self):
25         print('进程数据的写入方法')
 

测试验证:硬盘和进程一样,所以制作文本和硬盘的测试即可

硬盘读写测试:

 
1 disk=Sata()    #实例化一个硬盘读写对象
2 disk.read()    #硬盘读
3 disk.write()    #硬盘写
4 
5 输出结果:
6 硬盘数据的读取方法
7 硬盘数据的写入方法
 

文本读写测试:执行后会发现没有任何输出,那是因为txt对象实际上访问的read和write属性并非子类Txt所提供的属性,Txt所提供的属性只是du和xie,但是txt对象有read和write属性,别忘了Txt类是继承了父类File的属性,所以实际上txt对象的read和write属性是父类File提供的

1 txt=Txt()
2 txt.read()
3 txt.write()

正确的做法是将Txt类的du和xie方法改成read和write方法,这么做的意义为归一化

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

抽象类

抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。

抽象类介于接口和归一化中间,用于实现接口的归一化

当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法。即父类限制:

  1、子类必须要有父类的方法

  2、子类实现的方法必须跟父类的方法的名字一样

python的抽象类通过abc模块实现。

接口归一化示例:

 
 1 import abc
 2 class File(metaclass=abc.ABCMeta):  #metaclass指的是元类,边会讲,现在只需记住这个词
 3     @abc.abstractmethod     #抽象方法,即一个装饰器装饰read属性
 4     def read(self):
 5         pass
 6     @abc.abstractmethod      #抽象方法,即一个装饰器装饰write属性
 7     def write(self):
 8         pass
 9 # # 当继承File类时候,如果没有read和write方法,会提示出错TypeError: Can't instantiate abstract class Txt with abstract methods read, write
10 # class Txt(File):
11 #     def du(self):
12 #         print('文本数据的读取方法')
13 #     def xie(self):
14 #         print('文本数据的写入方法')
15 #定义子类具体实现文本的读写操作
16 class Txt(File):
17     def read(self):
18         print('文本数据的读取方法')
19     def write(self):
20         print('文本数据的写入方法')
21 #定义子类具体实现硬盘的读写操作
22 class Sata(File):
23     def read(self):
24         print('硬盘数据的读取方法')
25     def write(self):
26         print('硬盘数据的写入方法')
27 #定义子类具体实现进程的读写操作
28 class Process(File):
29     def read(self):
30         print('进程数据的读取方法')
31     def write(self):
32         print('进程数据的写入方法')
 

测试验证:

 
 1 t=Txt()
 2 t.read()
 3 t.write()
 4 s=Sata()
 5 s.read()
 6 s.write()
 7 输出结果:
 8 文本数据的读取方法
 9 文本数据的写入方法
10 硬盘数据的读取方法
11 硬盘数据的写入方法
 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!