(转)Python成长之路【第九篇】:Python基础之面向对象

一、三大编程范式

正本清源一:有人说,函数式编程就是用函数编程-->错误1

编程范式即编程的方法论,标识一种编程风格

大家学习了基本的Python语法后,大家就可以写Python代码了,然后每个人写代码的风格不同,这些不同的风格就代表了不同的流派

如果把Python的基本语法比作武术的基本功那么不同的编程风格就好比不同的武林门派

虽然大家风格不同,但是都可以完成你的编程需求,Python是一门面向对象的编程语言,但是到目前为止,你从未接触面向对象编程,然而你已经可以解决很多问题了,在Python中并没有人强制你使用哪一种固定的风格

根本就没有什么门派是天下无敌的,不同的风格在不同的场景下都有各自的牛逼之处

三大编程范式:

1、面向过程编程:捂裆派

2、函数式编程:峨眉派

3、面向对象编程:少林的蛋黄派

二、编程进化论

1、编程最开始就是无组织无结构,从简单控制流中按步写指令

2、从上述的指令中提取重复的代码块逻辑,组织到一起,(比方说,你定义了一个函数),便实现了代码重用,且代码由无结构走向了结构化,创建程序的过程变得更具逻辑性

3、我们定义函数都是独立于函数定义变量,然后作为参数传递给函数,这意味着:数据与动作是分离的

扫描二维码关注公众号,回复: 2703510 查看本文章

4、如果我们把数据和动作内嵌到一个结构(函数或类)里面,那么我们就有了一个“对象系统”(对象就是数据与函数整合到一起的产物)

 1 # 数据
 2 ShuJu = {
 3     "name":"Dou",
 4     "age":2,
 5     "gender":"male"
 6 }
 7 
 8 # 动作
 9 def jiao(name):
10     print("%s在叫"%name["name"])
11 def chi(name):
12     print("%s在吃"%name["name"])
13 
14 jiao(ShuJu)  # 执行动作
15 chi(ShuJu)  # 执行动作
举例

三、面向对象设计与面向对象编程

正本清源二:有人说,只有class定义类这才是面向对象,def定义函数就是函数相关的,跟面向对象没关系-->错误2

面向对象设计(Object oriented design):将一类具体事物的数据和动作整合到一起,即面向对象设计

面向对象设计(OOD)不会特别要求面向对象编程语言,事实上,OOD可以由纯结构化语言来实现(比如C),但如果想要构造具备对象性质和特点的数据类型,就需要在程序上做更多的努力

 四、小结

一门面向对象的语言不一定会强制你写OO方面的程序,例如C++可以被认为“更好的C”,而Java,则要求万物皆类,此外还规定,一个源文件对应一个类定义

然而,在Python中,类和OOD都不是日常编程所需要的,尽管它从一开始设计就是面向对象,并且结构上支持OOD,但Python没有限定或要求你在你的应用中写OO代码

用面向对象语言写程序,和一个程序的设计是面向对象的,两者是八竿子打不着的两码事

用C写的linux kernel事实上比C++/Java之类语言搞出来的大多数项目更加面向对象,只是绝大部分人都自以为自己到处写class的代码才是面向对象的正统、而死脑筋的linux搞得泛文件抽象不过是过程式思维搞出来的老古董

 五、类和对象

1、什么叫类:

类是一种数据结构,就好比一个模型,该模型用来表述一类事物(事物即数据和动作的结合体),用它来生产真实的物体(实例)

2、什么叫对象:

睁开眼,你看到的一切事物都是一个个对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)

(铅笔是对象,人是对象,房子是对象,狗是对象,计算机是对象,水杯是对象)

3、类与对象的关系:

对象是由类产生的,上帝造人,上帝首先有一个造人的模板,这个模板即人的类,然后上帝根据类的定义来生产一个个的人

4、什么叫实例化:

由生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例 = 对象)

 六、类的相关知识

1、初识类

在Python中声明函数与声明类相似

声明函数

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

声明类

?
1
2
3
4
5
6
7
8
9
10
class 类名:
     "类的文档字符串"
     类体
     
# 我们创建一个类
class Data:
     pass
 
# 用类Data实例化出一个对象d1
d1 = Data()

经典类与新式类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
大前提:
1 、只有在Python2中才分经典类和新式类,Python3中统一都是新式类
2 、新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类
3 、所有类不管是否显示声明父类,都有一个默认继承 object 父类
在Python2中的区分
经典类:
class 类名:
     pass
 
新式类:
class 类名(父类):
     pass
 
在Python3中,上述两种定义方式全都是新式类

2、属性

类是用来描述一类事物,类的对象指的是这一类事物中的一个个体

是事物就要有属性,属性分为

1:数据属性:就是变量

2:函数属性:就是函数,在面向对象里通常称为方法

注意:类和对象均用点来访问自己的属性

3、类的属性

风湿理论:数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用

类的数据属性

?
1
2
3
4
5
6
7
8
# 定义一个狗的类,然后在类中定义一个类的属性,声音是汪汪汪,这样,只要是狗,它们共有的属性就是汪汪汪
# 类属性又称为静态变量,或者是静态数据,这些数据是与它们所属的类对象绑定的,不依赖于任何类实例
# 如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字
 
class dog:
     language = "汪汪汪"
 
print (dog.language)

 类的函数属性(又称为方法)

?
1
2
3
4
5
6
7
8
9
10
11
class Chinese:
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
Chinese. help ()
 
name = "大山"
Chinese.win(name)  # 带参函数,所以调用时需要传入参数,将"大山"传给了self

查看类属性

我们定义的类的属性到底存在哪里了?有两种方式查看

dir(类名):查出的是一个名字的列表

类名.__dict__:查出的是一个字典,key为属性名,value为属性值

?
1
2
3
4
5
6
7
8
9
class Chinese:
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
print ( dir (Chinese))  # 显示结果是一个列表,包含类(包含内建属性在内的)所有属性名
print (Chinese.__dict__)  # 显示结果是一个字典,包含类的所有属性:属性值

 特殊的类属性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Chinese:
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
 
print (Chinese.__name__)  # 类的名字(字符串)
print (Chinese.__doc__)  # 类的文档字符串
print (Chinese.__base__)  # 类的第一个父类
print (Chinese.__bases__)  # 类的所有父类构成的元组
print (Chinese.__dict__)  # 类的属性和属性值
print (Chinese.__module__)  # 类定义所在的模块
print (Chinese.__class__)  # 实例Chinses对应的类(仅新式类中)

 七、对象相关知识

 对象是由类实例化而来的,类实例化的结果称为一个实例或者称作一个对象

1、实例化

?
1
2
3
4
5
6
7
8
9
10
11
class Chinese:
     "这是一个类"
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
p1 = Chinese() # 类名加上括号就是实例化(可以理解为函数的运行,返回值就是一个实例)
 
# 这就是实例化,只不过你得到p1实例,没有做任何事情

2、构造函数

上述的实例化过程,没有任何事情,啥意思?

类是数据属性和函数属性的结合,描述的是一类事物

这类事物的一个具体表现就是一个实例对象,比方说中国人是一个类,而你就是这个类的一个实例

你除了有中国人这个数据属性外,还应该有名字,年龄,性别等数据属性

如何为实例定制数据属性,可以使用类的一个内置方法__init__()该方法,在类()实例化是会自动执行

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Chinese:
     "这是一个类"
     character = "善良"
     def __init__( self ,name,age,gender):  # 实例化过程可以简单理解为执行该函数的过程,实例本身会当做参数传递给self(这是默认的步骤)
         self .name = name
         self .age = age
         self .gender = gender
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
# p1 = Chinese()  # 会报错
                   # 自动执行__init__方法,而这个方法需要参数
                   # 这些参数应该写在类名后边的括号里,然后由类传进去
                   # 给__init__函数,也就是说,传给类的参数就是传给__init__的参数
 
p1 = Chinese( "小明" , "8" , "男" )
p2 = Chinese( "小强" , "9" , "男" )
p3 = Chinese( "小红" , "6" , "女" )
 
print (p1.__dict__)

注意:在说实例化的时候说过,执行类()会自动返回一值,这个值就是实例,而类()会自动执行__init__,所以一定不要在该函数内定义返回值,会冲突(不怕死可以试试)

 3、实例属性

风湿理论:

1、实例只有数据属性(实例的函数属性严格来说是类的函数属性)

2、del 实例/对象,只是回收了实例的数据属性,函数属性是属于类的,是不会回收的

正本清源三:有人说,类有数据属性和函数属性,实例对象是由类产生的,所以实例也有数据属性和函数属性-->错误3

 实例化的过程实际就是执行__init_的过程,这个函数内部只是为了实例本身即self设定了一堆数据(变量),所以实例只有数据属性

八、类属性与对象(实例)属性

正本清源四:有人说实例是类产生的,所以实例肯定能访问到类属性,然后就没有说为什么-->错误4

1 class Chinese:  # class 为关键字,函数名后边不用加()
2     "这是一个类"  # 文档字符串
3     country = "China"  # 类数据属性
4     def __init__(self,name):   # 实例化自动默认执行此函数
5         self.name = name       # 实例(对象)属性
6     def play_ball(self,ball):  # 类函数
7         print("%s正在打%s" %(self.name,ball))
8 
9 p1 = Chinese("Albert")  # 实例化对象
类示范
 1 # 类属性又称为静态变量,或者是静态数据,这些数据是与它们所属的类对象绑定,不依赖任何实例
 2 class Chinese:
 3     country = "China"
 4     def __init__(self,name):
 5         self.name = name
 6     def play_ball(self,ball):
 7         print("%s正在打%s" %(self.name,ball))
 8 def say_word(self,word):
 9     print("%s say:%s"%(self.name, word))
10 
11 # 查看类属性
12 print(Chinese.country)
13 # 修改类属性
14 Chinese.country = "CHINA"
15 print(Chinese.country)
16 # 删除类属性
17 del Chinese.country
18 print(Chinese.__dict__)
19 # 增加类属性
20 Chinese.country = "China"
21 Chinese.location = "Asia"
22 print(Chinese.__dict__)
23 
24 # 类的函数属性增加
25 Chinese.say_word = say_word
26 print(Chinese.__dict__)
27 
28 p1 = Chinese("Albert")
29 p1.say_word("here")
类属性使用
 1 class Chinese:
 2     country = "China"
 3     def __init__(self,name):
 4         self.name = name
 5     def play_ball(self,ball):
 6         print("%s正在打%s" %(self.name,ball))
 7 
 8 p1 = Chinese("Albert")
 9 # 查看实例属性
10 print(p1.name)
11 # print(p1.age)  # 不存在于p1的属性字典中,会报错
12 
13 print(p1.country)  # country 也不存在于p1的属性字典中,为何可以取到呢?
14 
15 # 删除
16 # del p1.country  # 报错,因为p1的属性字典里根本就没有country属性
17 del p1.name
18 print(p1.__dict__)
19 
20 # 增加
21 p1.gender = "male"
22 print(p1.__dict__)
23 
24 # 修改
25 p1.gender = "female"
26 print(p1.__dict__)
实例属性使用

 九、静态属性、类方法、静态方法

 1 # 静态属性
 2 class room:
 3     "这是一个定义住所的类"
 4     def __init__(self,name,owner,length,wide,high):
 5         self.name = name
 6         self.owner = owner
 7         self.length = length
 8         self.wide = wide
 9         self.high = high
10     @property   # 静态属性装饰器
11     def cal_area(self):
12         s = "%s的住所是%s,面积是%s" % (self.owner, self.name, self.length * self.wide)
13         return s
14 
15 r1 = room("toilet","alex",100,100,10000)
16 
17 print(r1.cal_area)  # 调用方法在外表看起来,感觉像是在调用普通的数据属性,感受不到内部的逻辑
18 # ri.cal_area = 10  # 无法设置 报错
静态属性
 1 # 类方法
 2 
 3 class room:
 4     tag = 1
 5     def __init__(self,name):
 6         self.name = name
 7     @classmethod  # 类方法装饰器
 8     def lei(cls):
 9         print(cls.tag)
10 room.lei()
类方法
 1 # 静态方法
 2 # staticmothod静态方法只是名义上的归属类管理,不能使用类变量和实例变量,是类的工具包
 3 
 4 class room:
 5     def __init__(self,name):
 6         self.name = name
 7     @staticmethod   # 相当于类的工具包
 8     def foo(a,b):
 9         print(a,b)
10     def test():
11         print("这可不是静态方法,用类调用没问题,你用实例调用看看")
12 
13 p1 = room("albert")
14 room.foo(1,2)
15 p1.foo(3,4)
16 # p1.test()  # 报错
静态方法

十、组合

定义一个人的类,人有头,手,脚等数据属性,这几个属性又可以是通过一个类实例化的对象,这就是组合

用途:

1:做关联

2:小的组成大的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Hand:
     pass
class Foot:
     pass
class Trunk:
     pass
class Head:
     pass
 
class Person:
     def __init__( self , id ,_num,hand,foot,trunk,head):
         self .id_num = id_num
         self .name = name
         self .hand = Hand()
         self .foot = Foot()
         self .trunk = Trunk()
         self .head = Head()
 1 class Teacher:
 2     def __init__(self,name,age,gender):
 3         self.name = name
 4         self.age = age
 5         self.gender = gender
 6 class School:
 7     def __init__(self,name,addr):
 8         self.name = name
 9         self.addr = addr
10 
11 t1 = Teacher("老师1","30","male")
12 t2 = Teacher("老师2","31","male")
13 t3 = Teacher("老师3","32","male")
14 t4 = Teacher("老师4","33","male")
15 
16 s1 = School("北京","校区1")
17 s2 = School("北京","校区2")
18 s3 = School("北京","校区3")
19 
20 
21 class Course:
22     def __init__(self,name,price,period,school,teacher):
23         self.name = name
24         self.price = price
25         self.period = period
26         self.school = school
27         self.techer = teacher
28 teacher_msg = {
29     "1":t1,
30     "2":t2,
31     "3":t3,
32     "4":t4
33 }
34 school_msg = {
35     "1":s1,
36     "2":s2,
37     "3":s3
38 }
39 teacher_menu = """
40     "1":"老师1",
41     "2":"老师2",
42     "3":"老师3",
43     "4":"老师4"
44 """
45 school_menu = """
46     "1":"北京校区1",
47     "2":"北京校区2",
48     "3":"北京校区3"
49 """
50 while True:
51     print(teacher_menu,school_menu)
52 
53     school_choice = input("请选择校区:")
54     teacher_choice = input("请选择老师:")
55     print("接下来是创建课程!")
56     name = input("请输入课程名:")
57     price = input("请输入价格:")
58     period = input("请输入周期:")
59     C1 = Course(name,price,period,school_msg[school_choice],teacher_msg[teacher_choice])
60     print("您选择的课程是【%s】\n"
61           "周期是【%s】\n"
62           "价格是【%s】\n"
63           "教学老师是【%s】\n"
64           "学校是【%s】" %(C1.name,C1.period,C1.price,C1.techer.name,C1.school.addr))
65     break
选课系统

 十一、面向对象编程三大特性

1、继承

一、什么是类的继承?

类的继承跟现实生活中的父、子、孙子、重孙子、继承关系一样,父类又称为基类

Python中类额继承分为:单继承和多继承

?
1
2
3
4
5
6
7
8
9
class ParentClass1:  # 父类(基类)1
     pass
class ParentClass2:  # 父类(基类)2
     pass
 
class SubClass1(ParentClass1):  # 单继承
     pass
class SubClass2(ParentClass1,ParentClass2):  # 多继承
     pass

二、子继承到底继承了父类的什么属性?

正本清源五:有人说,子类继承了父类的所有属性,子类自定义的属性如果和父类重名了,那就覆盖了父类的-->错误5

三、什么时候用继承?

1、当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如机械胳膊类,脚类,身体类,电池类

2、当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好

例如:

  猫可以:喵喵叫、吃、喝、拉、撒

  狗可以:汪汪叫、吃、喝、拉、撒

 如果我们要分别为猫和狗创建一个类,那么就需要为猫 和 狗 实现他们所有的功能,如下所示:

 1 class 猫:
 2 
 3     def 喵喵叫(self):
 4         print("喵喵叫")
 5 
 6     def 吃(self):
 7         # do something
 8 
 9     def 喝(self):
10         # do something
11 
12     def 拉(self):
13         # do something
14 
15     def 撒(self):
16         # do something
17 
18 class 狗:
19 
20     def 汪汪叫(self):
21         print("汪汪叫")
22 
23     def 吃(self):
24         # do something
25 
26     def 喝(self):
27         # do something
28     
29     def 拉(self):
30         # do something
31 
32     def 撒(self):
33         # do something
伪代码

上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却把相同的功能分别在猫和狗的类中都写了一遍,共写了两遍

如果用继承的思想,如下实现:

  动物:吃、喝、拉、撒

  猫:喵喵叫(猫继承动物的功能)

  狗:汪汪叫(狗继承动物的功能)

 1 class 动物:
 2     def 吃(self):
 3         # do something
 4 
 5     def 喝(self):
 6         # do something
 7 
 8     def 拉(self):
 9         # do something
10 
11     def 撒(self):
12         # do something
13 
14 # 在类后面中写入另外一个类名,表示当前类继承另外一个类
15 class 猫(动物):
16 
17     def 喵喵叫(self):
18         print("喵喵叫")
19 
20 # 在类后面中写入另外一个类名,表示当前类继承另外一个类
21 class 狗(动物):
22 
23     def 汪汪叫(self):
24         print("汪汪叫")
伪代码
 1 class Animal:
 2     def eat(self):
 3         print("%s 吃" %(self.name))
 4 
 5     def drink(self):
 6         print("%s 喝" % (self.name))
 7 
 8     def shit(self):
 9         print("%s 拉" %(self.name))
10 
11     def pee(self):
12         print("%s 撒" %(self.name))
13 
14 class cat(Animal):
15     def __init__(self,name):
16         self.name = name
17         self.breed = ""
18 
19     def cry(self):
20         pritn("喵喵叫")
21 
22 class dog(Animal):
23     def __init__(self,name):
24         self.name = name
25         self.breed = ""
26 
27     def cry(self):
28         pritn("汪汪叫")
29 
30 c1 = cat("小白")
31 c1.shit()
32 print(c1.breed)
33 
34 c2 = dog("小黑")
35 c2.pee()
36 print(c2.breed)
继承实现

四、继承同时具有两种含义

含义一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)

含义二:声明某个子类兼容某基类,定义一个接口类,并且实现接口中定义的方法

 1 import abc
 2 class All_file(metaclass=abc.ABCMeta):
 3     @abc.abstractmethod
 4     def read(self):
 5         "子类必须实现读功能"
 6         pass
 7     @abc.abstractmethod
 8     def write(self):
 9         "子类必须实现写功能"
10         pass
11 
12 
13 class Cdrom(All_file):
14     def read(self):
15         print("cdrom read")
16     def write(self):
17         print("cdrom write")
18 class Mem(All_file):
19     def read(self):
20         print("mem read")
21     def write(self):
22         print("mem write")
23 m1 = Mem()
24 
25 m1.write()
26 
27 # class Test(All_file):
28 #     def read(self):
29 #         print("test read")
30 # t1 = Test()  # 报错,子类必须实现父类的所有方法
归一式继承 又称 接口继承

实践中,继承的第一种含义意义并不是很大,甚至常常是有害的,因为它使得子类与基类出现强耦合

继承的第二种含义非常重要,它又叫“接口继承”

接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化

归一化使得高层的外部使用者可以不加区分的处理所欲接口兼容的对象集合——就好像linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)

 

 五、继承顺序

1、Python的类可以继承多个类,Java和C#中则只能继承一个类

2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

当类是经典类时,多继承情况下,会按照深度优先方式查找

当类是新式类时,多继承情况下,会按照广度优先方式查找

         

新式类查找顺序:

class A:
    def foo(self):
        print("a")
class B(A):
    def foo(self):
        print("b")
class C(A):
    def foo(self):
        print("c")
class D(B):
    def foo(self):
        print("c")
class E(C):
    def foo(self):
        print("c")
class F(D,E):
    def foo(self):
        print(f)
print(F.__mro__)

# 查找顺序:F --> D --> B --> E --> C --> A --> object
继承查找顺序

 终极解密:Python到底是如何实现继承的,对于你定义的每一个类,Python会计算出一个方法解析顺序(MRO)元组,这个MRO元组就是一个简单的所有基类的线性顺序列表

为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止

而这个MRO元组的构造是通过一个C3线性化算法来实现的,我们不去深究这个算法数学原理,它实际上就是合并所有的父类的MRO元组,并遵循如下三条准则:

1、子类会优先于父类被检查

2、多个父类会根据他们在元组的顺序被检查

3、如果对下一个类存在两个合法的选择,选择第一个父类

 六、子类中调用父类方法

子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础修改,那么就需要在子类中调用父类的方法

方法一:父类名.父类方法()

 1 class Vehicle:  # 定义父类
 2     def __init__(self,name,speed,load,power):
 3         self.name = name
 4         self.speed = speed
 5         self.load = load
 6         self.power = power
 7     def run(self):
 8         print("开动了")
 9 class Subway(Vehicle):  # 继承父类
10     def __init__(self,name,speed,load,power,line):
11         Vehicle.__init__(self,name,speed,load,power)  # 执行父类init
12         self.line = line
13     def run(self):
14         Vehicle.run(self)  # 执行父类方法
15         print("%s %s 开动啦" %(self.name,self.line))
16 
17 r1 = Subway("北京地铁","300km/h",5000,"","13号线")
18 r1.run()
19 # 缺点:父类改名字,子类继承父类的名字需要全部更改,可扩展性差
生日礼物是想要一辆地铁

方法二:super()

 1 class Vehicle:  # 定义父类
 2     def __init__(self,name,speed,load,power):
 3         self.name = name
 4         self.speed = speed
 5         self.load = load
 6         self.power = power
 7     def run(self):
 8         print("开动了")
 9 class Subway(Vehicle):  # 继承父类
10     def __init__(self,name,speed,load,power,line):
11         super().__init__(name,speed,load,power)  # 执行父类init
12         self.line = line
13     def run(self):
14         super().run()  # 执行父类方法
15         print("%s %s 开动啦" %(self.name,self.line))
16 
17 r1 = Subway("北京地铁","300km/h",5000,"","13号线")
18 r1.run()
super版

2、多态

什么是多态:由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同

 1 class H2o:
 2     def __init__(self,name,temperature):
 3         self.name = name
 4         self.temperature = temperature
 5     def turn_ice(self):
 6         if self.temperature < 0:
 7             print("[%s]温度太低结冰了" %self.name)
 8         elif self.temperature > 0 and self.temperature < 100:
 9             print("[%s]液化成水" %self.name)
10         elif self.temperature > 100:
11             print("[%s]温度太高变成了水蒸气" %self.name)
12 
13 class Water(H2o):
14     pass
15 class Ice(H2o):
16     pass
17 class Steam(H2o):
18     pass
19 
20 w1 = Water("",25)
21 i1 = Ice("",-20)
22 s1 = Steam("蒸汽",3000)
23 
24 def func(obj):
25     obj.turn_ice()
26 
27 func(w1)
28 func(i1)
29 func(s1)
多态

多态:

多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需要考虑他们具体的类

多态表明了动态(又名,运行时)绑定的存在

类的继承有两层意义:1、改变 2、扩展

多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样

Python中的标准类型就是多态概念的一个很好示范

 3、封装

封装是啥,抛开面向对象,你单去想什么是装,装就是哪一个袋子,把苹果,香蕉,草莓,荔枝一起装进袋子里。什么是封,封就是把袋子的口子封上

在面向对象中这个袋子就是你的类或者对象,类或者对象这俩袋子内部装了数据属性和函数属性,那么对于类和对象来说“封”的概念从何而来呢,其实封的概念就是代表隐藏

封装的目的就是为了明确区分内部和外部,

那么如何实现封的效果呢?

第一个层面的封装:类就是袋子,这本身就是一种封装

例:略

第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问

例:

1:变量名字前边加一个下划线“_”就代表了只在内部使用,外部不可调用

 1 class People:
 2     _star = "哈哈哈"
 3     def __init__(self, name, age, gender):
 4         self.name = name
 5         self.age = age
 6         self.gender = gender
 7 
 8 
 9 p1 = People("albert",19,"male")
10 print(p1._star)
一个下划线实现隐藏

将代码粘贴到你的解释器之后你就蒙了,属性在外部还是可以调用到啊,那你就错了,这其实只是Python的一种约定

2:接下来测试一下加两个下划线的方法“__”

 1 class People:
 2     __star = "哈哈哈"
 3     def __init__(self, name, age, gender):
 4         self.name = name
 5         self.age = age
 6         self.gender = gender
 7 
 8 
 9 p1 = People("albert",19,"male")
10 # print(p1.__star)  # 报错
11 # 这时候你会发现,外部访问不到了,是不是真正的限制住外部访问呢,我们来查看他的字典
12 print(People.__dict__)
13 # 这时候我们会发现,字典中并没有__star这个数据
14 # 反而多了一个_People__star
15 # 这就是因为我们创建一个__开头的属性,Python会将它重命名
16 # 换成_类名__属性名
17 print(p1._People__star)
18 # 这时候你就搞明白了他的原理,所以说下划线开头的并不是真正的做了限制
两个下划线实现隐藏

 第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)

 4、面向对象的优点

从编程进化论我们得知,面向对象是一种更高级的结构化编程方式,它的好处就两点

1:通过封装明确了内外,你作为类的缔造者,你就是上帝,上帝造物的逻辑你无需知道,(你知道了你特么也成为上帝了),上帝想让你知道的你才能知道,这样就明确的划分了等级,物就是调用者,上帝就是物的创造者

2:通过集成+多态在语言层面支持了归一化设计

注意:不用面向对象语言(即不用class),一样可以封装(通过定义模块和接口),而你写了满屏的class也不代表就有了归一化设计等,面向对象是一种风格,千万不要被花哨的东西所迷惑

-end-

转自:
Python成长之路【第九篇】:Python基础之面向对象 - Mr_Albert - 博客园
https://www.cnblogs.com/albert0924/p/8921709.html

一、三大编程范式

正本清源一:有人说,函数式编程就是用函数编程-->错误1

编程范式即编程的方法论,标识一种编程风格

大家学习了基本的Python语法后,大家就可以写Python代码了,然后每个人写代码的风格不同,这些不同的风格就代表了不同的流派

如果把Python的基本语法比作武术的基本功那么不同的编程风格就好比不同的武林门派

虽然大家风格不同,但是都可以完成你的编程需求,Python是一门面向对象的编程语言,但是到目前为止,你从未接触面向对象编程,然而你已经可以解决很多问题了,在Python中并没有人强制你使用哪一种固定的风格

根本就没有什么门派是天下无敌的,不同的风格在不同的场景下都有各自的牛逼之处

三大编程范式:

1、面向过程编程:捂裆派

2、函数式编程:峨眉派

3、面向对象编程:少林的蛋黄派

二、编程进化论

1、编程最开始就是无组织无结构,从简单控制流中按步写指令

2、从上述的指令中提取重复的代码块逻辑,组织到一起,(比方说,你定义了一个函数),便实现了代码重用,且代码由无结构走向了结构化,创建程序的过程变得更具逻辑性

3、我们定义函数都是独立于函数定义变量,然后作为参数传递给函数,这意味着:数据与动作是分离的

4、如果我们把数据和动作内嵌到一个结构(函数或类)里面,那么我们就有了一个“对象系统”(对象就是数据与函数整合到一起的产物)

 1 # 数据
 2 ShuJu = {
 3     "name":"Dou",
 4     "age":2,
 5     "gender":"male"
 6 }
 7 
 8 # 动作
 9 def jiao(name):
10     print("%s在叫"%name["name"])
11 def chi(name):
12     print("%s在吃"%name["name"])
13 
14 jiao(ShuJu)  # 执行动作
15 chi(ShuJu)  # 执行动作
举例

三、面向对象设计与面向对象编程

正本清源二:有人说,只有class定义类这才是面向对象,def定义函数就是函数相关的,跟面向对象没关系-->错误2

面向对象设计(Object oriented design):将一类具体事物的数据和动作整合到一起,即面向对象设计

面向对象设计(OOD)不会特别要求面向对象编程语言,事实上,OOD可以由纯结构化语言来实现(比如C),但如果想要构造具备对象性质和特点的数据类型,就需要在程序上做更多的努力

 四、小结

一门面向对象的语言不一定会强制你写OO方面的程序,例如C++可以被认为“更好的C”,而Java,则要求万物皆类,此外还规定,一个源文件对应一个类定义

然而,在Python中,类和OOD都不是日常编程所需要的,尽管它从一开始设计就是面向对象,并且结构上支持OOD,但Python没有限定或要求你在你的应用中写OO代码

用面向对象语言写程序,和一个程序的设计是面向对象的,两者是八竿子打不着的两码事

用C写的linux kernel事实上比C++/Java之类语言搞出来的大多数项目更加面向对象,只是绝大部分人都自以为自己到处写class的代码才是面向对象的正统、而死脑筋的linux搞得泛文件抽象不过是过程式思维搞出来的老古董

 五、类和对象

1、什么叫类:

类是一种数据结构,就好比一个模型,该模型用来表述一类事物(事物即数据和动作的结合体),用它来生产真实的物体(实例)

2、什么叫对象:

睁开眼,你看到的一切事物都是一个个对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)

(铅笔是对象,人是对象,房子是对象,狗是对象,计算机是对象,水杯是对象)

3、类与对象的关系:

对象是由类产生的,上帝造人,上帝首先有一个造人的模板,这个模板即人的类,然后上帝根据类的定义来生产一个个的人

4、什么叫实例化:

由生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例 = 对象)

 六、类的相关知识

1、初识类

在Python中声明函数与声明类相似

声明函数

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

声明类

?
1
2
3
4
5
6
7
8
9
10
class 类名:
     "类的文档字符串"
     类体
     
# 我们创建一个类
class Data:
     pass
 
# 用类Data实例化出一个对象d1
d1 = Data()

经典类与新式类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
大前提:
1 、只有在Python2中才分经典类和新式类,Python3中统一都是新式类
2 、新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类
3 、所有类不管是否显示声明父类,都有一个默认继承 object 父类
在Python2中的区分
经典类:
class 类名:
     pass
 
新式类:
class 类名(父类):
     pass
 
在Python3中,上述两种定义方式全都是新式类

2、属性

类是用来描述一类事物,类的对象指的是这一类事物中的一个个体

是事物就要有属性,属性分为

1:数据属性:就是变量

2:函数属性:就是函数,在面向对象里通常称为方法

注意:类和对象均用点来访问自己的属性

3、类的属性

风湿理论:数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用

类的数据属性

?
1
2
3
4
5
6
7
8
# 定义一个狗的类,然后在类中定义一个类的属性,声音是汪汪汪,这样,只要是狗,它们共有的属性就是汪汪汪
# 类属性又称为静态变量,或者是静态数据,这些数据是与它们所属的类对象绑定的,不依赖于任何类实例
# 如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字
 
class dog:
     language = "汪汪汪"
 
print (dog.language)

 类的函数属性(又称为方法)

?
1
2
3
4
5
6
7
8
9
10
11
class Chinese:
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
Chinese. help ()
 
name = "大山"
Chinese.win(name)  # 带参函数,所以调用时需要传入参数,将"大山"传给了self

查看类属性

我们定义的类的属性到底存在哪里了?有两种方式查看

dir(类名):查出的是一个名字的列表

类名.__dict__:查出的是一个字典,key为属性名,value为属性值

?
1
2
3
4
5
6
7
8
9
class Chinese:
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
print ( dir (Chinese))  # 显示结果是一个列表,包含类(包含内建属性在内的)所有属性名
print (Chinese.__dict__)  # 显示结果是一个字典,包含类的所有属性:属性值

 特殊的类属性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Chinese:
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
 
print (Chinese.__name__)  # 类的名字(字符串)
print (Chinese.__doc__)  # 类的文档字符串
print (Chinese.__base__)  # 类的第一个父类
print (Chinese.__bases__)  # 类的所有父类构成的元组
print (Chinese.__dict__)  # 类的属性和属性值
print (Chinese.__module__)  # 类定义所在的模块
print (Chinese.__class__)  # 实例Chinses对应的类(仅新式类中)

 七、对象相关知识

 对象是由类实例化而来的,类实例化的结果称为一个实例或者称作一个对象

1、实例化

?
1
2
3
4
5
6
7
8
9
10
11
class Chinese:
     "这是一个类"
     character = "善良"
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
p1 = Chinese() # 类名加上括号就是实例化(可以理解为函数的运行,返回值就是一个实例)
 
# 这就是实例化,只不过你得到p1实例,没有做任何事情

2、构造函数

上述的实例化过程,没有任何事情,啥意思?

类是数据属性和函数属性的结合,描述的是一类事物

这类事物的一个具体表现就是一个实例对象,比方说中国人是一个类,而你就是这个类的一个实例

你除了有中国人这个数据属性外,还应该有名字,年龄,性别等数据属性

如何为实例定制数据属性,可以使用类的一个内置方法__init__()该方法,在类()实例化是会自动执行

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Chinese:
     "这是一个类"
     character = "善良"
     def __init__( self ,name,age,gender):  # 实例化过程可以简单理解为执行该函数的过程,实例本身会当做参数传递给self(这是默认的步骤)
         self .name = name
         self .age = age
         self .gender = gender
     def help ():
         print ( "帮助了一位小朋友" )
     def win( self ):
         print ( "%s获得了第一!" % self )
 
# p1 = Chinese()  # 会报错
                   # 自动执行__init__方法,而这个方法需要参数
                   # 这些参数应该写在类名后边的括号里,然后由类传进去
                   # 给__init__函数,也就是说,传给类的参数就是传给__init__的参数
 
p1 = Chinese( "小明" , "8" , "男" )
p2 = Chinese( "小强" , "9" , "男" )
p3 = Chinese( "小红" , "6" , "女" )
 
print (p1.__dict__)

注意:在说实例化的时候说过,执行类()会自动返回一值,这个值就是实例,而类()会自动执行__init__,所以一定不要在该函数内定义返回值,会冲突(不怕死可以试试)

 3、实例属性

风湿理论:

1、实例只有数据属性(实例的函数属性严格来说是类的函数属性)

2、del 实例/对象,只是回收了实例的数据属性,函数属性是属于类的,是不会回收的

正本清源三:有人说,类有数据属性和函数属性,实例对象是由类产生的,所以实例也有数据属性和函数属性-->错误3

 实例化的过程实际就是执行__init_的过程,这个函数内部只是为了实例本身即self设定了一堆数据(变量),所以实例只有数据属性

八、类属性与对象(实例)属性

正本清源四:有人说实例是类产生的,所以实例肯定能访问到类属性,然后就没有说为什么-->错误4

1 class Chinese:  # class 为关键字,函数名后边不用加()
2     "这是一个类"  # 文档字符串
3     country = "China"  # 类数据属性
4     def __init__(self,name):   # 实例化自动默认执行此函数
5         self.name = name       # 实例(对象)属性
6     def play_ball(self,ball):  # 类函数
7         print("%s正在打%s" %(self.name,ball))
8 
9 p1 = Chinese("Albert")  # 实例化对象
类示范
 1 # 类属性又称为静态变量,或者是静态数据,这些数据是与它们所属的类对象绑定,不依赖任何实例
 2 class Chinese:
 3     country = "China"
 4     def __init__(self,name):
 5         self.name = name
 6     def play_ball(self,ball):
 7         print("%s正在打%s" %(self.name,ball))
 8 def say_word(self,word):
 9     print("%s say:%s"%(self.name, word))
10 
11 # 查看类属性
12 print(Chinese.country)
13 # 修改类属性
14 Chinese.country = "CHINA"
15 print(Chinese.country)
16 # 删除类属性
17 del Chinese.country
18 print(Chinese.__dict__)
19 # 增加类属性
20 Chinese.country = "China"
21 Chinese.location = "Asia"
22 print(Chinese.__dict__)
23 
24 # 类的函数属性增加
25 Chinese.say_word = say_word
26 print(Chinese.__dict__)
27 
28 p1 = Chinese("Albert")
29 p1.say_word("here")
类属性使用
 1 class Chinese:
 2     country = "China"
 3     def __init__(self,name):
 4         self.name = name
 5     def play_ball(self,ball):
 6         print("%s正在打%s" %(self.name,ball))
 7 
 8 p1 = Chinese("Albert")
 9 # 查看实例属性
10 print(p1.name)
11 # print(p1.age)  # 不存在于p1的属性字典中,会报错
12 
13 print(p1.country)  # country 也不存在于p1的属性字典中,为何可以取到呢?
14 
15 # 删除
16 # del p1.country  # 报错,因为p1的属性字典里根本就没有country属性
17 del p1.name
18 print(p1.__dict__)
19 
20 # 增加
21 p1.gender = "male"
22 print(p1.__dict__)
23 
24 # 修改
25 p1.gender = "female"
26 print(p1.__dict__)
实例属性使用

 九、静态属性、类方法、静态方法

 1 # 静态属性
 2 class room:
 3     "这是一个定义住所的类"
 4     def __init__(self,name,owner,length,wide,high):
 5         self.name = name
 6         self.owner = owner
 7         self.length = length
 8         self.wide = wide
 9         self.high = high
10     @property   # 静态属性装饰器
11     def cal_area(self):
12         s = "%s的住所是%s,面积是%s" % (self.owner, self.name, self.length * self.wide)
13         return s
14 
15 r1 = room("toilet","alex",100,100,10000)
16 
17 print(r1.cal_area)  # 调用方法在外表看起来,感觉像是在调用普通的数据属性,感受不到内部的逻辑
18 # ri.cal_area = 10  # 无法设置 报错
静态属性
 1 # 类方法
 2 
 3 class room:
 4     tag = 1
 5     def __init__(self,name):
 6         self.name = name
 7     @classmethod  # 类方法装饰器
 8     def lei(cls):
 9         print(cls.tag)
10 room.lei()
类方法
 1 # 静态方法
 2 # staticmothod静态方法只是名义上的归属类管理,不能使用类变量和实例变量,是类的工具包
 3 
 4 class room:
 5     def __init__(self,name):
 6         self.name = name
 7     @staticmethod   # 相当于类的工具包
 8     def foo(a,b):
 9         print(a,b)
10     def test():
11         print("这可不是静态方法,用类调用没问题,你用实例调用看看")
12 
13 p1 = room("albert")
14 room.foo(1,2)
15 p1.foo(3,4)
16 # p1.test()  # 报错
静态方法

十、组合

定义一个人的类,人有头,手,脚等数据属性,这几个属性又可以是通过一个类实例化的对象,这就是组合

用途:

1:做关联

2:小的组成大的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Hand:
     pass
class Foot:
     pass
class Trunk:
     pass
class Head:
     pass
 
class Person:
     def __init__( self , id ,_num,hand,foot,trunk,head):
         self .id_num = id_num
         self .name = name
         self .hand = Hand()
         self .foot = Foot()
         self .trunk = Trunk()
         self .head = Head()
 1 class Teacher:
 2     def __init__(self,name,age,gender):
 3         self.name = name
 4         self.age = age
 5         self.gender = gender
 6 class School:
 7     def __init__(self,name,addr):
 8         self.name = name
 9         self.addr = addr
10 
11 t1 = Teacher("老师1","30","male")
12 t2 = Teacher("老师2","31","male")
13 t3 = Teacher("老师3","32","male")
14 t4 = Teacher("老师4","33","male")
15 
16 s1 = School("北京","校区1")
17 s2 = School("北京","校区2")
18 s3 = School("北京","校区3")
19 
20 
21 class Course:
22     def __init__(self,name,price,period,school,teacher):
23         self.name = name
24         self.price = price
25         self.period = period
26         self.school = school
27         self.techer = teacher
28 teacher_msg = {
29     "1":t1,
30     "2":t2,
31     "3":t3,
32     "4":t4
33 }
34 school_msg = {
35     "1":s1,
36     "2":s2,
37     "3":s3
38 }
39 teacher_menu = """
40     "1":"老师1",
41     "2":"老师2",
42     "3":"老师3",
43     "4":"老师4"
44 """
45 school_menu = """
46     "1":"北京校区1",
47     "2":"北京校区2",
48     "3":"北京校区3"
49 """
50 while True:
51     print(teacher_menu,school_menu)
52 
53     school_choice = input("请选择校区:")
54     teacher_choice = input("请选择老师:")
55     print("接下来是创建课程!")
56     name = input("请输入课程名:")
57     price = input("请输入价格:")
58     period = input("请输入周期:")
59     C1 = Course(name,price,period,school_msg[school_choice],teacher_msg[teacher_choice])
60     print("您选择的课程是【%s】\n"
61           "周期是【%s】\n"
62           "价格是【%s】\n"
63           "教学老师是【%s】\n"
64           "学校是【%s】" %(C1.name,C1.period,C1.price,C1.techer.name,C1.school.addr))
65     break
选课系统

 十一、面向对象编程三大特性

1、继承

一、什么是类的继承?

类的继承跟现实生活中的父、子、孙子、重孙子、继承关系一样,父类又称为基类

Python中类额继承分为:单继承和多继承

?
1
2
3
4
5
6
7
8
9
class ParentClass1:  # 父类(基类)1
     pass
class ParentClass2:  # 父类(基类)2
     pass
 
class SubClass1(ParentClass1):  # 单继承
     pass
class SubClass2(ParentClass1,ParentClass2):  # 多继承
     pass

二、子继承到底继承了父类的什么属性?

正本清源五:有人说,子类继承了父类的所有属性,子类自定义的属性如果和父类重名了,那就覆盖了父类的-->错误5

三、什么时候用继承?

1、当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如机械胳膊类,脚类,身体类,电池类

2、当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好

例如:

  猫可以:喵喵叫、吃、喝、拉、撒

  狗可以:汪汪叫、吃、喝、拉、撒

 如果我们要分别为猫和狗创建一个类,那么就需要为猫 和 狗 实现他们所有的功能,如下所示:

 1 class 猫:
 2 
 3     def 喵喵叫(self):
 4         print("喵喵叫")
 5 
 6     def 吃(self):
 7         # do something
 8 
 9     def 喝(self):
10         # do something
11 
12     def 拉(self):
13         # do something
14 
15     def 撒(self):
16         # do something
17 
18 class 狗:
19 
20     def 汪汪叫(self):
21         print("汪汪叫")
22 
23     def 吃(self):
24         # do something
25 
26     def 喝(self):
27         # do something
28     
29     def 拉(self):
30         # do something
31 
32     def 撒(self):
33         # do something
伪代码

上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却把相同的功能分别在猫和狗的类中都写了一遍,共写了两遍

如果用继承的思想,如下实现:

  动物:吃、喝、拉、撒

  猫:喵喵叫(猫继承动物的功能)

  狗:汪汪叫(狗继承动物的功能)

 1 class 动物:
 2     def 吃(self):
 3         # do something
 4 
 5     def 喝(self):
 6         # do something
 7 
 8     def 拉(self):
 9         # do something
10 
11     def 撒(self):
12         # do something
13 
14 # 在类后面中写入另外一个类名,表示当前类继承另外一个类
15 class 猫(动物):
16 
17     def 喵喵叫(self):
18         print("喵喵叫")
19 
20 # 在类后面中写入另外一个类名,表示当前类继承另外一个类
21 class 狗(动物):
22 
23     def 汪汪叫(self):
24         print("汪汪叫")
伪代码
 1 class Animal:
 2     def eat(self):
 3         print("%s 吃" %(self.name))
 4 
 5     def drink(self):
 6         print("%s 喝" % (self.name))
 7 
 8     def shit(self):
 9         print("%s 拉" %(self.name))
10 
11     def pee(self):
12         print("%s 撒" %(self.name))
13 
14 class cat(Animal):
15     def __init__(self,name):
16         self.name = name
17         self.breed = ""
18 
19     def cry(self):
20         pritn("喵喵叫")
21 
22 class dog(Animal):
23     def __init__(self,name):
24         self.name = name
25         self.breed = ""
26 
27     def cry(self):
28         pritn("汪汪叫")
29 
30 c1 = cat("小白")
31 c1.shit()
32 print(c1.breed)
33 
34 c2 = dog("小黑")
35 c2.pee()
36 print(c2.breed)
继承实现

四、继承同时具有两种含义

含义一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)

含义二:声明某个子类兼容某基类,定义一个接口类,并且实现接口中定义的方法

 1 import abc
 2 class All_file(metaclass=abc.ABCMeta):
 3     @abc.abstractmethod
 4     def read(self):
 5         "子类必须实现读功能"
 6         pass
 7     @abc.abstractmethod
 8     def write(self):
 9         "子类必须实现写功能"
10         pass
11 
12 
13 class Cdrom(All_file):
14     def read(self):
15         print("cdrom read")
16     def write(self):
17         print("cdrom write")
18 class Mem(All_file):
19     def read(self):
20         print("mem read")
21     def write(self):
22         print("mem write")
23 m1 = Mem()
24 
25 m1.write()
26 
27 # class Test(All_file):
28 #     def read(self):
29 #         print("test read")
30 # t1 = Test()  # 报错,子类必须实现父类的所有方法
归一式继承 又称 接口继承

实践中,继承的第一种含义意义并不是很大,甚至常常是有害的,因为它使得子类与基类出现强耦合

继承的第二种含义非常重要,它又叫“接口继承”

接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化

归一化使得高层的外部使用者可以不加区分的处理所欲接口兼容的对象集合——就好像linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)

 

 五、继承顺序

1、Python的类可以继承多个类,Java和C#中则只能继承一个类

2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

当类是经典类时,多继承情况下,会按照深度优先方式查找

当类是新式类时,多继承情况下,会按照广度优先方式查找

         

新式类查找顺序:

class A:
    def foo(self):
        print("a")
class B(A):
    def foo(self):
        print("b")
class C(A):
    def foo(self):
        print("c")
class D(B):
    def foo(self):
        print("c")
class E(C):
    def foo(self):
        print("c")
class F(D,E):
    def foo(self):
        print(f)
print(F.__mro__)

# 查找顺序:F --> D --> B --> E --> C --> A --> object
继承查找顺序

 终极解密:Python到底是如何实现继承的,对于你定义的每一个类,Python会计算出一个方法解析顺序(MRO)元组,这个MRO元组就是一个简单的所有基类的线性顺序列表

为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止

而这个MRO元组的构造是通过一个C3线性化算法来实现的,我们不去深究这个算法数学原理,它实际上就是合并所有的父类的MRO元组,并遵循如下三条准则:

1、子类会优先于父类被检查

2、多个父类会根据他们在元组的顺序被检查

3、如果对下一个类存在两个合法的选择,选择第一个父类

 六、子类中调用父类方法

子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础修改,那么就需要在子类中调用父类的方法

方法一:父类名.父类方法()

 1 class Vehicle:  # 定义父类
 2     def __init__(self,name,speed,load,power):
 3         self.name = name
 4         self.speed = speed
 5         self.load = load
 6         self.power = power
 7     def run(self):
 8         print("开动了")
 9 class Subway(Vehicle):  # 继承父类
10     def __init__(self,name,speed,load,power,line):
11         Vehicle.__init__(self,name,speed,load,power)  # 执行父类init
12         self.line = line
13     def run(self):
14         Vehicle.run(self)  # 执行父类方法
15         print("%s %s 开动啦" %(self.name,self.line))
16 
17 r1 = Subway("北京地铁","300km/h",5000,"","13号线")
18 r1.run()
19 # 缺点:父类改名字,子类继承父类的名字需要全部更改,可扩展性差
生日礼物是想要一辆地铁

方法二:super()

 1 class Vehicle:  # 定义父类
 2     def __init__(self,name,speed,load,power):
 3         self.name = name
 4         self.speed = speed
 5         self.load = load
 6         self.power = power
 7     def run(self):
 8         print("开动了")
 9 class Subway(Vehicle):  # 继承父类
10     def __init__(self,name,speed,load,power,line):
11         super().__init__(name,speed,load,power)  # 执行父类init
12         self.line = line
13     def run(self):
14         super().run()  # 执行父类方法
15         print("%s %s 开动啦" %(self.name,self.line))
16 
17 r1 = Subway("北京地铁","300km/h",5000,"","13号线")
18 r1.run()
super版

2、多态

什么是多态:由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同

 1 class H2o:
 2     def __init__(self,name,temperature):
 3         self.name = name
 4         self.temperature = temperature
 5     def turn_ice(self):
 6         if self.temperature < 0:
 7             print("[%s]温度太低结冰了" %self.name)
 8         elif self.temperature > 0 and self.temperature < 100:
 9             print("[%s]液化成水" %self.name)
10         elif self.temperature > 100:
11             print("[%s]温度太高变成了水蒸气" %self.name)
12 
13 class Water(H2o):
14     pass
15 class Ice(H2o):
16     pass
17 class Steam(H2o):
18     pass
19 
20 w1 = Water("",25)
21 i1 = Ice("",-20)
22 s1 = Steam("蒸汽",3000)
23 
24 def func(obj):
25     obj.turn_ice()
26 
27 func(w1)
28 func(i1)
29 func(s1)
多态

多态:

多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需要考虑他们具体的类

多态表明了动态(又名,运行时)绑定的存在

类的继承有两层意义:1、改变 2、扩展

多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样

Python中的标准类型就是多态概念的一个很好示范

 3、封装

封装是啥,抛开面向对象,你单去想什么是装,装就是哪一个袋子,把苹果,香蕉,草莓,荔枝一起装进袋子里。什么是封,封就是把袋子的口子封上

在面向对象中这个袋子就是你的类或者对象,类或者对象这俩袋子内部装了数据属性和函数属性,那么对于类和对象来说“封”的概念从何而来呢,其实封的概念就是代表隐藏

封装的目的就是为了明确区分内部和外部,

那么如何实现封的效果呢?

第一个层面的封装:类就是袋子,这本身就是一种封装

例:略

第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问

例:

1:变量名字前边加一个下划线“_”就代表了只在内部使用,外部不可调用

 1 class People:
 2     _star = "哈哈哈"
 3     def __init__(self, name, age, gender):
 4         self.name = name
 5         self.age = age
 6         self.gender = gender
 7 
 8 
 9 p1 = People("albert",19,"male")
10 print(p1._star)
一个下划线实现隐藏

将代码粘贴到你的解释器之后你就蒙了,属性在外部还是可以调用到啊,那你就错了,这其实只是Python的一种约定

2:接下来测试一下加两个下划线的方法“__”

 1 class People:
 2     __star = "哈哈哈"
 3     def __init__(self, name, age, gender):
 4         self.name = name
 5         self.age = age
 6         self.gender = gender
 7 
 8 
 9 p1 = People("albert",19,"male")
10 # print(p1.__star)  # 报错
11 # 这时候你会发现,外部访问不到了,是不是真正的限制住外部访问呢,我们来查看他的字典
12 print(People.__dict__)
13 # 这时候我们会发现,字典中并没有__star这个数据
14 # 反而多了一个_People__star
15 # 这就是因为我们创建一个__开头的属性,Python会将它重命名
16 # 换成_类名__属性名
17 print(p1._People__star)
18 # 这时候你就搞明白了他的原理,所以说下划线开头的并不是真正的做了限制
两个下划线实现隐藏

 第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)

 4、面向对象的优点

从编程进化论我们得知,面向对象是一种更高级的结构化编程方式,它的好处就两点

1:通过封装明确了内外,你作为类的缔造者,你就是上帝,上帝造物的逻辑你无需知道,(你知道了你特么也成为上帝了),上帝想让你知道的你才能知道,这样就明确的划分了等级,物就是调用者,上帝就是物的创造者

2:通过集成+多态在语言层面支持了归一化设计

注意:不用面向对象语言(即不用class),一样可以封装(通过定义模块和接口),而你写了满屏的class也不代表就有了归一化设计等,面向对象是一种风格,千万不要被花哨的东西所迷惑

-end-

猜你喜欢

转载自www.cnblogs.com/jyfootprint/p/9461282.html