Python进阶笔记(一)

一.面向对象概述

1.面向过程与面向对象

  1. 面向过程 —— 怎么做

  2. 把完成某一个需求的 所有步骤 从头到尾 逐步实现

  3. 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数

  4. 最后完成的代码,就是顺序地调用 不同的函数

特点

  1. 注重 步骤与过程,不注重职责分工
  2. 如果需求复杂,代码会变得很复杂
  3. 开发复杂项目,没有固定的套路,开发难度很大!

  4. 面向对象 —— 谁来做

相比较函数,面向对象更大封装,根据 职责一个对象中 封装 多个方法

  1. 在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)
  2. 根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)
  3. 最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法

特点

  1. 注重 对象和职责,不同的对象承担不同的职责
  2. 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
  3. 需要在面向过程基础上,再学习一些面向对象的语法

2.重要概念:类和对象

2.1 概念引入

在洗衣机洗衣服的过程中,洗衣机就是我们所说的对象,整个过程描述如下:图纸—>洗衣机—>洗衣服。在这个过程中,图纸就是类,而洗衣机就是对象。有了类,我们就能根据它来创建对象。

简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。

类和对象的关系

  • 类是模板对象 是根据 这个模板创建出来的,应该 先有类,再有对象
  • 类只有一个,而对象可以有很多个
    • 不同的对象 之间 属性 可能会各不相同
  • 中定义了什么 属性和方法对象 中就有什么属性和方法,不可能多,也不可能少

总结:类和对象的关系,就是用类去创建一个对象,或者用类去实例化一个对象。

2.2 类

类是对一系列列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物

  • 特征即是属性
  • 行为即是方法

在程序开发中,要设计一个类,通常需要满足一下三个要素:

  1. 类名 这类事物的名字,满足大驼峰命名法
  2. 属性 这类事物具有什么样的特征
  3. 方法 这类事物具有什么样的行为

大驼峰命名法

CapWords

  1. 每一个单词的首字母大写
  2. 单词与单词之间没有下划线

类名的确定

名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类

属性和方法的确定

  • 对象的特征描述,通常可以定义成 属性
  • 对象具有的行为(动词),通常可以定义成 方法

提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑

2.3 对象

对象是类创建出来的真实存在的事物,例如:洗衣机。

注意:开发中,先有类,再有对象。

2.4 定义类

在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 基本语法
class 类名():
代码
......

class Washer():
def wash(self):
print("我会洗衣服")

class Student(object):

# __init__是一个特殊方法用于在创建对象时进行初始化操作
# 通过这个方法我们可以为学生对象绑定name和age两个属性
def __init__(self, name, age):
self.name = name
self.age = age

def study(self, course_name):
print('%s正在学习%s.' % (self.name, course_name))

# PEP 8要求标识符的名字用全小写多个单词用下划线连接
# 但是部分程序员和公司更倾向于使用驼峰命名法(驼峰标识)
def watch_movie(self):
if self.age < 18:
print('%s只能观看《熊出没》.' % self.name)
else:
print('%s正在观看爱情大电影.' % self.name)

说明: 写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。

2.5 创建和使用对象

对象又名实例。创建对象也叫做实例化对象。

语法:对象名 = 类名()

当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def main():
# 创建学生对象并指定姓名和年龄
stu1 = Student('骆昊', 38)
# 给对象发study消息
stu1.study('Python程序设计')
# 给对象发watch_av消息
stu1.watch_movie()
stu2 = Student('王大锤', 15)
stu2.study('思想品德')
stu2.watch_movie()


if __name__ == '__main__':
main()

3.面向对象基础语法

3.1 dir内置函数(知道)

  • Python对象几乎是无所不在的,我们之前学习的 变量数据函数 都是对象

Python 中可以使用以下两个方法验证:

  1. 标识符 / 数据 后输入一个 .,然后按下 TAB 键,iPython 会提示该对象能够调用的 方法列表
  2. 使用内置函数 dir 传入 标识符 / 数据,可以查看对象内的所有属性及方法

提示 __方法名__ 格式的方法是 Python 提供的 内置方法 / 属性,稍后会给大家介绍一些常用的内置方法 / 属性

序号 方法名 类型 作用
01 __new__ 方法 创建对象时,会被 自动 调用
02 __init__ 方法 对象被初始化时,会被 自动 调用
03 __del__ 方法 对象被从内存中销毁前,会被 自动 调用
04 __str__ 方法 返回对象的描述信息print 函数输出使用

提示 利用好 dir() 函数,在学习时很多内容就不需要死记硬背了

3.2 定义简单的类(仅包含方法)

3.2.1 定义只包含方法的类

  • Python 中要定义一个只包含方法的类,语法格式如下:
1
2
3
4
5
6
7
class 类名:

def 方法1(self):
pass

def 方法2(self, 参数列表):
pass
  • 方法 的定义格式和之前学习过的函数 几乎一样
  • 区别在于第一个参数必须是 self,大家暂时先记住,稍后介绍 self

注意:类名 的 命名规则 要符合 大驼峰命名法

3.2.2 创建对象

对象又名实例。创建对象也叫做实例化对象。

语法:对象名 = 类名()

3.2.3 第一个面向对象程序

需求

  • 小猫 鱼,小猫

分析

  1. 定义一个猫类 Cat
  2. 定义两个方法 eatdrink
  3. 按照需求 —— 不需要定义属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Cat: # 对类进行封装
"""这是一个猫类"""

def eat(self):
print("小猫爱吃鱼")

def drink(self):
print("小猫在喝水")

tom = Cat() # 创建一个叫tom的对象
tom.drink() # 调用tom中的方法
tom.eat()
print(tom) # 打印tom对象在内存中的地址
addr = id(tom)
print("%x" % addr)
# output:
# 小猫在喝水
# 小猫爱吃鱼
# <__main__.Cat object at 0x7f76373fa670>
# 7f780b7c1670

3.2.4 引用概念的强调

在面向对象开发中,引用的概念是同样适用的!

  • Python 中使用类 创建对象之后tom 变量中 仍然记录的是 对象在内存中的地址
  • 也就是 tom 变量 引用新建的猫对象
  • 使用 print 输出 对象变量,默认情况下,是能够输出这个变量 引用的对象由哪一个类创建的对象,以及 在内存中的地址十六进制表示

提示:在计算机中,通常使用 十六进制 表示 内存地址

  • 十进制十六进制 都是用来表达数字的,只是表示的方式不一样
  • 十进制十六进制 的数字之间可以来回转换
  • %d 可以以 10 进制 输出数字
  • %x 可以以 16 进制 输出数字

补充知识:引用与赋值

在Python中,令values=[0,1,2];values[1]=values,为何结果是[0,[…],2]?]

1
2
3
>>> values = [0, 1, 2]
>>> values[1] = values
>>> values[0, [...], 2]

我预想应当是

1
[0, [0, 1, 2], 2]

但结果却为何要赋值无限次?

可以说 Python 没有赋值,只有引用。你这样相当于创建了一个引用自身的结构,所以导致了无限循环。为了理解这个问题,有个基本概念需要搞清楚。

Python 没有「变量」,我们平时所说的变量其实只是「标签」,是引用。这里的引用类似C语言中的指针。

3.2.5 使用Cat类再创建一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Cat:
"""这是一个猫类"""

def eat(self):
print("小猫爱吃鱼")

def drink(self):
print("小猫在喝水")

tom = Cat()
tom.drink()
tom.eat()
print(tom)

lazy_cat = Cat()
lazy_cat.eat()
lazy_cat.drink()
print(lazy_cat) # lazy_cat 和 tom 是两个不同的对象
lazy_cat2 = lazy_cat # 这里涉及到引用
print(lazy_cat2) # lazy_cat2和lazy_cat2表示同一个对象
# 小猫在喝水
# 小猫爱吃鱼
# <__main__.Cat object at 0x7fbcf2de2670>
# 小猫爱吃鱼
# 小猫在喝水
# <__main__.Cat object at 0x7fbcf2de7fd0>
# <__main__.Cat object at 0x7f0869e2afd0>

3.3 案例改造 —— 给对象增加属性

  • 在Python中,要给对象设置属性,非常的容易,但是不推荐使用

    因为:对象属性的封装应该封装在类的内部

  • 只需要在 类的外部的代码 中直接通过 . 设置一个属性即可

注意:这种方式虽然简单,但是不推荐使用!

1
2
3
4
tom.name = "Tom" # 这种属性只针对tom
...

lazy_cat.name = "大懒猫" # 这种属性只针对lazy_cat

3.4 方法中的self参数

哪一个对象 调用的方法,方法内的 self 就是 哪一个对象的引用

  • 在类封装的方法内部,self 就表示 当前调用方法的对象自己
  • 调用方法时,程序员不需要传递 self 参数
  • 在方法内部
    • 可以通过 self. 访问对象的属性
    • 也可以通过 self. 调用其他的对象方法
  • 改造代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cat:

def eat(self):
print("%s 爱吃鱼" % self.name)

tom = Cat()
tom.name = "Tom" # 访问对象的属性
tom.eat() # 访问对象的方法

lazy_cat = Cat()
lazy_cat.name = "大懒猫"
lazy_cat.eat()
'''
output:
Tom 爱吃鱼
大懒猫 爱吃鱼
'''

005_方法中的self

  • 类的外部,通过 变量名. 访问对象的 属性和方法
  • 类封装的方法中,通过 self. 访问对象的 属性和方法

3.5 注意避免在类的外部给对象增加属性

  • 将案例代码进行调整,先调用方法 再设置属性,观察一下执行效果
1
2
3
4
5
tom = Cat()
tom.drink()
tom.eat()
tom.name = "Tom"
print(tom)
  • 程序执行报错如下:
  • 原因:python自顶向下执行,执行tom.drink()时不能通过self.name访问name,因为name还未定义。
1
2
AttributeError: 'Cat' object has no attribute 'name'
属性错误:'Cat' 对象没有 'name' 属性

提示

  • 在日常开发中,不推荐在类的外部给对象增加属性
    • 如果在运行时,没有找到属性,程序会报错
  • 对象应该包含有哪些属性,应该 封装在类的内部
坚持原创技术分享,您的支持将鼓励我继续创作!