一、三大编程范式
编程范式即编程的方法论,标识一种编程风格。
我们学习完Python语法后,就可以写python代码了,然后每个人写代码的风格不同,这些不同的风格就代表了不同的流派。
如果把python的基本语法比作无数的基本功,那么不同的编程风格就好比不同的武林门派。
虽然大家风格不同,但是都可以完成你的编程需求,Python是一门面向对象编程语言,但是到目前为止,你从未接触面向对象编程,然而你已经可以解决很多问题了,在Python中并没有人强制你使用哪一种固定的风格。
根本就没有什么门派是天下无敌的,不同的风格在不同的场景下都有各自的牛逼之处。
编程范式:
a.面向过程编程
b.函数式编程:函数式 = 编程语言定义的函数+数学意义的函数;通俗来讲,函数式就是用编程语言去实现数学函数。这种函数内对象是永恒不变的,要么参数是函数,要么返回值是函数,没有for和while循环,所有的循环都由递归去实现,无变量的赋值(即不用变量去保存状态),无赋值即不改变。
c.面向对象编程:定义类+实例对象的方式去实现面向对象的设计
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 class Dog:
4 def __init__(self, name, gender, type):
5 self.name = name
6 self.gender = gender
7 self.type = type
8 def bark(self):
9 print('一条名字为[%s]的[%s],狂吠不止' %(self.name, self.type))
10 def eat(self):
11 print('[%s]正在啃骨头' % self.name)
12
13
14 dog1 = Dog('alex', 'female', '京巴')
15 dog1.bark() # 一条名字为[alex]的[京巴],狂吠不止
面向对象编程
二、编程进化论
1.编程最开始就是无组织无结构,从简单控制流按步写指令。
2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比方说,定义了一个函数),便实现了代码重用,且代码由无结构走向了结构化,创建程序的过程变得更具逻辑性。
3.我们定义函数都是独立于函数外定义变量,然后作为参数传递给函数,这意味着:数据与动作是分离的。
4.如果我们把数据和动作内嵌到一个结构(函数或类)里面,那么我们就有了一个‘对象系统’(对象就是数据与函数整合到一起的产物)。
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 def dog(name, gender, type):
6 def call(dog):
7 print('一条狗[%s],汪汪汪' % dog['name'])
8 def eat(dog):
9 print('一条[%s]正在啃骨头' % dog['type'])
10 def init(name, gender, type):
11 dog_type = {
12 'name': name,
13 'gender':gender,
14 'type': type,
15 'call': call,
16 'eat': eat
17 }
18 return dog_type
19 res = init(name, gender, type)
20 return res
21
22
23 d1 = dog('旺财', '公', '藏獒')
24 d1['call'](d1) # 一条狗[旺财],汪汪汪
25 d1['eat'](d1) # 一条[藏獒]正在啃骨头
举例
三、类与对象概括
类:把一类事物的相同的特征和动作整合到一起就是类,类是一个抽象的概念。
对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一块。
类与对象的关系:对象都是由类产生的。
实例化:由类生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例=对象)
四、类的相关知识
1 初始类
在Python中声明函数与声明类很相似
声明函数
1 def functionName(args):
2 '函数文档字符串'
3 函数体
声明类
1 class 类名:
2 '类的文档字符串'
3 类体
4
5 # 创建类
6 class Data:
7 pass
8
9 # 用类Data实例化出一个对象d1
10 d1 = Data()
经典类与新式类
1 大前提:
2 1.只有在Python2中才分新式类和经典类,Python3中统一都是新式类
3 2.新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类
4 3.所有类不管是否显式声明父类,都有一个默认继承object父类
5 在Python中的区分
6 经典类:
7 class 类名:
8 pass
9 新式类:
10 class 类名(父类):
11 pass
12 在Python3中,上述两种定义方式全都是新式类
2 属性
类是用来描述一类事物,类的对象指的是这一类事物中的一个个体。
事物就要有属性,属性分:
a.数据属性:就是变量
b.函数属性:就是函数,在面向对象里通常称为方法
注:类和对象均用“.”来访问自己的属性
3 类的属性
概括:数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用。
4 查看类属性
定义的类属性到底存放到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性石
五、类属性与对象(实例)属性
•类属性:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 # 类属性又称为静态变量,或者是静态数据,这些数据是它们的类对象绑定的,不依赖于任何类实例。
4 # 如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字。
5
6
7 class ChinesePeople:
8 country = 'China'
9
10 def __init__(self, name):
11 self.name = name
12
13 def play_ball(self, ball):
14 print('[%s]正在打 %s' % (self.name, ball))
15
16 def say_word(self, word):
17 print('[%s] 说 %s' % (self.name, word))
18
19
20 # 查看类属性
21 print(ChinesePeople.country) # China
22
23 # 修改类属性
24 ChinesePeople.country = 'CHINA'
25 print(ChinesePeople.country) # CHINA
26
27 # 删除类属性
28 del ChinesePeople.country
29
30 # 增加类属性
31 ChinesePeople.country = 'China'
32 ChinesePeople.location = 'Asia'
33 print(ChinesePeople.__dict__) # {'__module__': '__main__', '__init__': <function ChinesePeople.__init__ at 0x0000003E9C6F8950>, 'play_ball': <function ChinesePeople.play_ball at 0x0000003E9C6F88C8>, 'say_word': <function ChinesePeople.say_word at 0x0000003E9C6F89D8>, '__dict__': <attribute '__dict__' of 'ChinesePeople' objects>, '__weakref__': <attribute '__weakref__' of 'ChinesePeople' objects>, '__doc__': None, 'country': 'China', 'location': 'Asia'}
34 print(ChinesePeople.location) # Asia
•实例属性
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class ChinesePeople:
6 country = 'China'
7
8 def __init__(self, name):
9 self.name = name
10
11 def play_ball(self, ball):
12 print('[%s]正在打 %s' % (self.name, ball))
13
14 def say_word(self, word):
15 print('[%s] 说 %s' % (self.name, word))
16
17
18 C1 = ChinesePeople('alex')
19 print(C1.__dict__) # {'name': 'alex'}
20 print(dir(C1)) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'country', 'name', 'play_ball', 'say_word']
21
22 # 查看
23 print(C1.name) # alex
24 C1.play_ball('篮球') # [alex]正在打 篮球
25
26 # 增加
27 C1.age = 18
28 print(C1.__dict__) # {'name': 'alex', 'age': 18}
29 print(C1.age) # 18
30
31 # 不要修改底层的字典结构
32 # C1.__dict__['sex'] = 'male'
33 # print(C1.__dict__) # {'name': 'alex', 'age': 18, 'sex': 'male'}
34 # print(C1.sex) # male
35
36 # 修改
37 C1.age = 28
38 print(C1.__dict__) # {'name': 'alex', 'age': 28}
39
40 # 删除
41 del C1.age
42 print(C1.__dict__) # {'name': 'alex'}
六、静态属性、静态方法和类方法
静态方法: @staticmethod 静态方法只是名义上的归属类管理,不能使用类变量和实例变量,是类的工具包
静态属性: @property # 将函数属性变为数据属性
类方法: @classmethod
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Room:
6 def __init__(self, name, owner, width, length, height):
7 self.name = name
8 self.owner = owner
9 self.width = width
10 self.length = length
11 self.height = height
12
13 # 第一种方法
14 @property # 将函数属性变为数据属性
15 def cal_area(self):
16 print('[%s]住的%s总面积是:%s' % (self.owner, self.name, self.width * self.height))
17
18 # 第二种方法
19 # def cal_area(self):
20 # print('[%s]住的%s总面积是:%s' % (self.owner, self.name, self.width * self.height))
21
22
23 r1 = Room('卧室', 'alex', 20, 10, 100)
24 # 第一种方法调用
25 r1.cal_area # [alex]住的卧室总面积是:2000
26
27 # 第二种方法调用
28 # r1.cal_area() # [alex]住的卧室总面积是:2000
静态属性
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Room:
6 tag = 100
7
8 def __init__(self, name, owner, width, length, height):
9 self.name = name
10 self.owner = owner
11 self.width = width
12 self.length = length
13 self.height = height
14
15 @classmethod
16 def tell_info(cls):
17 print(cls)
18 print('from tell_info', cls.tag) # ---> cls.tag == Room.tag
19
20
21 Room.tell_info() # <class '__main__.Room'> ; from tell_info 100
类属性
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Room:
6 def __init__(self, name, owner, width, length, height):
7 self.name = name
8 self.owner = owner
9 self.width = width
10 self.length = length
11 self.height = height
12
13 @staticmethod
14 def wash_body(name):
15 print('[%s]正在洗澡'% name)
16
17
18 Room.wash_body('alex') # [alex]正在洗澡
类方法
七、组合
定义一个人的类,人有头,身体,手,脚等,这几个属性又可以是通过一个类实例化的对象,这就是组合。
用途:
a.关联
b.小的组合成大的
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Hand:
6 pass
7
8
9 class Foot:
10 pass
11
12
13 class Trunk:
14 pass
15
16
17 class Head:
18 pass
19
20
21 class Person:
22 def __init__(self, id_num, name, hand, foot, trunk, head):
23 self.id_num = id_num
24 self.name = name
25 self.hand = hand
26 self.foot = foot
27 self.trunk = trunk
28 self.head = head
八、面向对象编程三大特性
8.1、继承
一 什么是类的继承?
类的继承跟现实生活中的父、子、孙子、重孙子,继承关系一样,父亲又称为基类。
Python中类的继承分为:单继承和多继承
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 class ParentClass1:
4 pass
5
6
7 class ParentClass2:
8 pass
9
10
11 class SubClass(ParentClass1): # 单继承
12 pass
13
14
15 class SubClass(ParentClass1, ParentClass2): # 多继承
16 pass
二 什么时候用继承?
1.当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如胳膊类、腿类、身体类等。
2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
例如:
猫:喵喵喵、吃、喝、拉、撒
狗:汪汪汪、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为猫和狗实现他们所有的功能,如下所示:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Animal:
6 def eat(self):
7 pass # do something
8
9 def drink(self):
10 pass # do something
11
12 def pull(self):
13 pass # do something
14
15
16 class Cat(Animal):
17 def call(self):
18 print('喵喵喵')
19
20
21 class Dog(Animal):
22 def call(self):
23 print('汪汪汪')
伪代码
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Animal:
6 def eat(self):
7 print('[%s] 吃' % self.name)
8
9 def drink(self):
10 print('[%s] 喝' % self.name)
11
12 def pull(self):
13 print('[%s] 拉' % self.name)
14
15
16 class Cat(Animal):
17 def __init__(self, name):
18 self.name = name
19
20 def cry(self):
21 print('喵喵喵')
22
23
24 class Dog(Animal):
25 def __init__(self, name):
26 self.name = name
27
28 def cry(self):
29 print('汪汪汪')
30 # ----调用----
31
32
33 c1 = Cat('小黑猫')
34 c1.cry()
35 c1.drink()
继承
三 继承同时具有两种含义
1.继承基类的方法,并且做出自己的改变或者扩展(代码重用)
注:实践中含义意义并不是很大,甚至常常是有害的,因为它使得子类与基类出现强耦合。
2.声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法
注:它又叫'接口继承',接口继承实质上是要求'作出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象'---->这在程序设计上,叫做归一化。
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 import abc # 添加接口继承模块
4
5
6 class All_file(metaclass=abc.ABCMeta): # 接口
7 @abc.abstractmethod
8 def read(self):
9 pass
10
11 @abc.abstractmethod
12 def write(self):
13 pass
14
15
16 class Disk(All_file):
17 def read(self):
18 print('Disk read')
19
20 def write(self):
21 print('Disk write')
22
23
24 class Cdrom(All_file):
25 def read(self):
26 print('Cdrom read')
27
28 def write(self):
29 print('Cdrom write')
30
31
32 class Memory(All_file):
33 def read(self):
34 print('Memory read')
35
36 def write(self):
37 print('Memory write')
38
39
40 m1 = Memory()
41 m1.write() # Memory write
42 m1.read() # Memory read
归一化
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合,就好像Linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、硬盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出'字符设备'和'块设备',然后做出针对性的设计,细致到什么程度,视需求而定)。
四 继承顺序
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 class A:
4 def test(self):
5 print('A')
6
7
8 class B(A):
9 def test(self):
10 print('B')
11
12
13 class C(B):
14 def test(self):
15 print('C')
16
17
18 class D(A):
19 def test(self):
20 print('D')
21
22
23 class E(D):
24 def test(self):
25 print('E')
26
27
28 class F(C, E):
29 def test(self):
30 print('F')
31
32
33 f1 = F()
34 f1.test() # F
35 print(F.__mro__) # 继承顺序;(<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
继承顺序
Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先。
• 当类是经典类时,多继承情况下,会按照深度优先方式查找
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 # A、B都是经典类
5 class A:
6 pass
7
8
9 class B(A):
10 pass
经典类
• 当类是新式类时(Python3),多继承情况下,会按照广度优先方式查找
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 # A1、B1都是新式类
5 class A1(object):
6 pass
7
8
9 class B1(A1):
10 pass
新式类
注:经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了更多的功能,从写法上区别的是父类继承了object类,那么该类便是新式类,否则便是经典类。
解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止,而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
五 子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Vehicle: # 定义交通工具类
6 Country = 'China'
7
8 def __init__(self, name, speed, load, power):
9 self.name = name
10 self.speed = speed
11 self.load = load
12 self.power = power
13
14 def run(self):
15 print('开车啦')
16
17
18 class Subway(Vehicle):
19
20 def __init__(self, name, speed, load, power, line):
21 # 方法一
22 Vehicle.__init__(self, name, speed, load, power)
23 self.line = line
24 # 方法 二
25 # self.name = name
26 # self.speed = speed
27 # self.load = load
28 # self.power = power
29 # self.line = line
30
31 def show_info(self): # 派生类
32 print('地铁名称:%s,线路:%s' % (self.name, self.line))
33
34 def run(self):
35 Vehicle.run(self) # 在子类中调用父类函数
36 print('%s %s号线 开车啦' % (self.name, self.line))
37
38
39 line_num = Subway('杭州', '500m/s', 2000, '电', 1)
40 line_num.show_info() # 地铁名称:杭州,线路:1
41 line_num.run() # 开车啦;杭州 1号线 开车啦
方法二:super()
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class Vehicle: # 定义交通工具类
6 Country = 'China'
7
8 def __init__(self, name, speed, load, power):
9 self.name = name
10 self.speed = speed
11 self.load = load
12 self.power = power
13
14 def run(self):
15 print('开车啦')
16
17
18 class Subway(Vehicle):
19
20 def __init__(self, name, speed, load, power, line):
21 # 方法一
22 # Vehicle.__init__(self, name, speed, load, power)
23 super().__init__(name, speed, load, power) # <==>super(Subway, self).__init__()
24 self.line = line
25 # 方法 二
26 # self.name = name
27 # self.speed = speed
28 # self.load = load
29 # self.power = power
30 # self.line = line
31
32 def show_info(self): # 派生类
33 print('地铁名称:%s,线路:%s' % (self.name, self.line))
34
35 def run(self):
36 # Vehicle.run(self) # 在子类中调用父类函数
37 super().run()
38 print('%s %s号线 开车啦' % (self.name, self.line))
39
40
41 line_num = Subway('杭州', '500m/s', 2000, '电', 1)
42 line_num.show_info() # 地铁名称:杭州,线路:1
43 line_num.run() # 开车啦;杭州 1号线 开车啦
super()
8.2、多态
多态:由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class H2o:
6 def __init__(self, name, temperature):
7 self.name = name
8 self.temperature = temperature
9
10 def turn_ice(self):
11 if self.temperature < 0:
12 print('[%s]温度太低,结冰了' % self.name)
13 elif self.temperature > 0 and self.temperature < 100:
14 print('[%s]液化成水' % self.name)
15 elif self.temperature > 100:
16 print('[%s]温度太高变成水蒸气' % self.name)
17
18
19 class Water(H2o):
20 pass
21
22
23 class Ice(H2o):
24 pass
25
26
27 class Steam(H2o):
28 pass
29
30
31 w1 = Water('水', 25)
32 w1.turn_ice() # [水]液化成水
33
34 i1 = Ice('冰', -10)
35 i1.turn_ice() # [冰]温度太低,结冰了
36
37 s1 = Steam('水蒸气', 10)
38 s1.turn_ice() # [水蒸气]液化成水
多态
多态的概念指出了对象如何通过它们共同的属性和动作来操作及访问,而不需考虑它们具体的类。
多态表明了动态(又名,运行时)绑定的存在,允许重载及运行时类型确定和验证。
例子:
水是一个类
不同温度,水被实例化成了不同的状态:冰,水蒸气,雾(多态是运行时绑定的存在,多态体现由同一个类实例化出的多个对象,这些对象执行相同的方法时,执行的过程和结果是不一样的),冰、水蒸气、雾,有一个共同的方法就是变成云,但是冰.变云()与水蒸气.变云()是截然不同的两个过程,虽然调用的方法都一样。
8.3、封装
第一层面的封装:类就是一个麻袋,这本身就是一种封装
第二层面的封装:类中定义私有的,只有类的内部使用,外部无法访问
第三层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)。
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 class Room:
5 def __init__(self, name, owner, width, length, height):
6 self.name = name
7 self.owner = owner
8 self.__width = width # 定义私有变量
9 self.__length = length # 定义私有变量
10 self.__height = height # 定义私有变量
11
12 def tell_area(self): # 对外提供一个接口,求面积
13 return self.__width * self.__length
14
15
16 r1 = Room('卧室', 'alex', 3, 10, 3)
17 area = r1.tell_area()
18 print(area) # 30
封装示例一
Python不依赖语言特性去封装数据,而是通过遵循一定的数据属性和函数属性的命名约定来达到封装的效果。
约定一:任何以单下划线开头的名字都应该是内部的,私有的。
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class People:
6 _Country = 'China'
7
8 def __init__(self, name, age):
9 self.name = name
10 self.age =age
11
12
13 p1 = People('alex', 19)
14 print(p1._Country) # China
约定一;一个“_”
约定二:双下划线开头的名字。
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 class People:
6 __Country = 'China'
7
8 def __init__(self, name, age):
9 self.name = name
10 self.age =age
11
12
13 p1 = People('alex', 19)
14 print(People.__dict__) # {'__module__': '__main__', '_People__Country': 'China', '__init__': <function People.__init__ at 0x00000031768D87B8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
15 # print(p1.__Country) # 报错
16 print(p1._People__Country) # China
约定二;二个“_”
总结:
上面两种不同的编码约定(“_”,“__”)来命名私有属性,到底哪种方式好呢?大多数而言,你应该让你的非公共名称以“_”开头,但是如果你清楚你的代码会涉及到子类,并且有些内部属性应该在子类中隐藏起来,那么才考虑使用“__”方案,但是无论哪种方案,其实Python都没有从根本上限制你的访问。
在其他语言中私有的属性在外部就是不能被访问的,在Python中则恰恰相反。
九 面向对象的优点
面向对象是一种更高等级的结构化编程方式,它的好处有两点:
1. 通过封装明确了内外,你作为类的缔造者,你是上帝,上帝造物的逻辑你无需知道,上帝让你知道的你才知道,这样就明确了划分了等级,物就是调用者,上帝就是物的创造者。
2. 通过继承+多态在语言层面支持了归一化设计