Python 基础语法与面向对象编程入门指南
前言
学习一门编程语言贵在坚持,记录笔记有助于日后快速回忆。本文分为两大部分:Python 基础语法和面向对象编程。
入门 Python 其实很容易,但需要持续练习。如果没有好的学习资料或指导,很难长期坚持。希望本文能帮助你建立扎实的 Python 基础。

第一部分 Python 基础语法
1. 认识 Python
1.1 Python 简介
Python 的创始人为吉多·范罗苏姆(Guido van Rossum)。
Python 的设计目标:
- 一门简单直观的语言并与主要竞争者一样强大
- 开源,以便任何人都可以为它做贡献
- 代码像纯英语那样容易理解
- 适用于短期开发的日常任务
Python 的设计哲学:
- 优雅、明确、简单
- 用一种方法,最好是只有一种方法来做一件事
Python 是完全面向对象的语言,在 Python 中一切皆对象。
**可扩展性:**如果需要一段关键代码运行得更快或者希望某些算法不公开,可以把这部分程序用 C 或 C++ 编写,然后在 Python 程序中使用它们。
1.2 第一个 Python 程序
执行 Python 程序的三种方式:解释器、交互式运行、IDE 运行。
Python 是一个格式非常严格的程序设计语言。Python 2.x 默认不支持中文。
- ASCII 字符只包含 256 个字符,不支持中文。
- Python 2.x 的解释器名称是
python。 - Python 3.x 的解释器名称是
python3。
为了照顾现有的程序,官方提供了一个过渡版本 —— Python 2.6。
**提示:**如果开发时,无法立即使用 Python 3.0(还有极少的第三方库不支持 3.0 的语法),建议先使用 Python 3.0 版本进行开发,然后使用 Python 2.6、Python 2.7 来执行,并且做一些兼容性的处理。
IPython 是一个 Python 的交互式 shell,比默认的 python shell 好用得多,它支持 bash shell 命令,适合于学习/验证 Python 语法或者局部代码。
集成开发环境(IDE) 集成了开发软件需要的所有工具,一般包括以下工具:
- 图形用户界面
- 代码编辑器(支持代码补全/自动缩进)
- 编译器/解释器
- 调试器(断点/单步执行)

PyCharm 是 Python 的一款非常优秀的集成开发环境。

1.3 PyCharm 的设置
PyCharm 的配置信息是保存在用户家目录下的 .PyCharmxxxx.x 目录下的,xxxx.x 表示当前使用的 PyCharm 的版本号。
1.3.1 恢复 PyCharm 的初始设置
- 关闭正在运行的 PyCharm。
- 在终端中执行以下终端命令,删除 PyCharm 的配置信息目录:
$ rm -r ~/.PyCharm2016.3
- 重新启动 PyCharm。
1.3.2 PyCharm 安装和启动步骤
- 执行以下终端命令,解压缩下载后的安装包:
$ tar -zxvf pycharm-professional-2017.1.3.tar.gz
- 将解压缩后的目录移动到
/opt目录下,可以方便其他用户使用。
/opt目录用于存放给主机额外安装的软件。
$ sudo mv pycharm-2017.1.3/ /opt/
- 切换工作目录:
$ cd /opt/pycharm-2017.1.3/bin
- 启动 PyCharm:
$ ./pycharm.sh
1.3.3 设置启动图标
在专业版中,选择菜单 Tools / Create Desktop Entry… 可以设置任务栏启动图标。
注意:设置图标时,需要勾选 Create the entry for all users。
快捷方式文件路径:/usr/share/applications/jetbrains-pycharm.desktop。
在 Ubuntu 中,应用程序启动的快捷方式通常都保存在
/usr/share/applications目录下。
1.3.4 卸载之前版本的 PyCharm
要卸载 PyCharm 只需要做以下两步工作:
- 删除解压缩目录:
$ sudo rm -r /opt/pycharm-2016.3.1/
- 删除家目录下用于保存配置信息的隐藏目录:
$ rm -r ~/.PyCharm2016.3/
如果不再使用 PyCharm 还需要将 /usr/share/applications/ 下的 jetbrains-pycharm.desktop 删掉。
1.4 多文件项目的演练
开发项目就是开发一个专门解决一个复杂业务功能的软件。 通常每一个项目就具有一个独立专属的目录,用于保存所有和项目相关的文件。 在 PyCharm 中,要想让哪一个 Python 程序能够执行,必须首先通过鼠标右键的方式执行一下。 对于初学者而言,在一个项目中设置多个程序可以执行,是非常方便的,可以方便对不同知识点的练习和测试。 对于商业项目而言,通常在一个项目中,只有一个可以直接执行的 Python 源程序。

2. 注释
注释的作用是使用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性。
2.1 单行注释(行注释)
以 # 开头,# 右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用。
print("hello python") # 输出 `hello python`
为了保证代码的可读性,# 后面建议先添加一个空格,然后再编写相应的说明文字;为了保证代码的可读性,注释和代码之间至少要有两个空格。
2.2 多行注释(块注释)
要在 Python 程序中使用多行注释,可以用一对连续的三个引号 (单引号和双引号都可以)。
"""
这是一个多行注释
在多行注释之间,可以写很多很多的内容……
"""
print("hello python")
提示:
- 注释不是越多越好,对于一目了然的代码,不需要添加注释。
- 对于复杂的操作,应该在操作开始前写上若干行注释。
- 对于不是一目了然的代码,应在其行尾添加注释(为了提高可读性,注释应该至少离开代码 2 个空格)。
- 绝不要描述代码,假设阅读代码的人比你更懂 Python,他只是不知道你的代码要做什么。
2.3 代码规范
Python 官方提供有一系列 PEP(Python Enhancement Proposals)文档,其中第 8 篇文档专门针对 Python 的代码格式给出了建议,也就是俗称的 PEP 8。
- 文档地址:https://www.python.org/dev/peps/pep-0008/
- 谷歌有对应的中文文档:http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/
3. 运算符
3.1 算术运算符
是完成基本的算术运算使用的符号,用来处理四则运算,而'+'和'*'还可以用来处理字符串。
| 运算符 | 描述 | 实例 |
|---|---|---|
| + | 加 | 10 + 20 = 30 |
| - | 减 | 10 - 20 = -10 |
| * | 乘 | 10 * 20 = 200 |
| / | 除 | 10 / 20 = 0.5 |
| // | 取整除 | 返回除法的整数部分(商),9 // 2 输出结果 4 |
| % | 取余数 | 返回除法的余数,9 % 2 = 1 |
| ** | 幂 | 又称次方、乘方,2 ** 3 = 8 |
3.2 比较(关系)运算符
| 运算符 | 描述 |
|---|---|
| == | 检查两个操作数的值是否相等,如果是,则条件成立,返回 True |
| != | 检查两个操作数的值是否不相等,如果是,则条件成立,返回 True |
| > | 检查左操作数的值是否大于右操作数的值,如果是,则条件成立,返回 True |
| < | 检查左操作数的值是否小于右操作数的值,如果是,则条件成立,返回 True |
| >= | 检查左操作数的值是否大于或等于右操作数的值,如果是,则条件成立,返回 True |
| <= | 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件成立,返回 True |
Python 2.x 中判断不等于还可以使用
<>运算符,!=在 Python 2.x 中同样可以用来判断不等于。
3.3 赋值运算符
在 Python 中,使用 = 可以给变量赋值。在算术运算时,为了简化代码的编写,Python 还提供了一系列的与算术运算符对应的赋值运算符,注意:赋值运算符中间不能使用空格。
| 运算符 | 描述 | 实例 |
|---|---|---|
| = | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c |
| += | 加法赋值运算符 | c += a 等效于 c = c + a |
| -= | 减法赋值运算符 | c -= a 等效于 c = c - a |
| *= | 乘法赋值运算符 | c *= a 等效于 c = c * a |
| /= | 除法赋值运算符 | c /= a 等效于 c = c / a |
| //= | 取整除赋值运算符 | c //= a 等效于 c = c // a |
| %= | 取模 (余数) 赋值运算符 | c %= a 等效于 c = c % a |
| **= | 幂赋值运算符 | c **= a 等效于 c = c ** a |
3.4 身份运算符
身份运算符比较两个对象的内存位置。常用的有两个身份运算符,如下所述:
| 运算符 | 描述 | 示例 |
|---|---|---|
| is | 判断两个标识符是不是引用同一个对象 | x is y,类似 id(x) == id(y) |
| is not | 判断两个标识符是不是引用不同对象 | x is not y,类似 id(a) != id(b) |
辨析:
is用于判断两个变量引用的对象是否为同一个。==用于判断引用变量的值是否相等。
3.5 成员运算符
Python 成员运算符测试给定值是否为序列中的成员。有两个成员运算符,如下所述:
| 运算符 | 描述 |
|---|---|
| in | 如果在指定的序列中找到一个变量的值,则返回 true,否则返回 false。 |
| not in | 如果在指定序列中找不到变量的值,则返回 true,否则返回 false。 |
3.6 逻辑运算符
| 运算符 | 逻辑表达式 | 描述 |
|---|---|---|
| and | x and y | 只有 x 和 y 的值都为 True,才会返回 True;否则只要 x 或者 y 有一个值为 False,就返回 False |
| or | x or y | 只要 x 或者 y 有一个值为 True,就返回 True;只有 x 和 y 的值都为 False,才会返回 False |
| not | not x | 如果 x 为 True,返回 False;如果 x 为 False,返回 True |
3.7 运算符优先级
以下表格的算数优先级由高到最低顺序排列:
| 运算符 | 描述 |
|---|---|
| ** | 幂 (最高优先级) |
| * / % // | 乘、除、取余数、取整除 |
| + - | 加法、减法 |
| <= < > >= | 比较运算符 |
| == != | 等于运算符 |
| = %= /= //= -= += *= **= | 赋值运算符 |
| is is not | 身份运算符 |
| in not in | 成员运算符 |
| not or and | 逻辑运算符 |
<补>程序执行原理 操作系统会首先让 CPU 把 Python 解释器的程序复制到内存中。 Python 解释器根据语法规则,从上向下让 CPU 翻译 Python 程序中的代码。 CPU 负责执行翻译完成的代码。
Python 的解释器有多大? 执行以下终端命令可以查看 Python 解释器的大小:
# 1. 确认解释器所在位置
$ which python
# 2. 查看 python 文件大小 (只是一个软链接)
$ ls -lh /usr/bin/python
# 3. 查看具体文件大小
$ ls -lh /usr/bin/python2.7
4. 变量
4.1 变量定义
在 Python 中,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 可以用其他变量的计算结果来定义变量。 变量名只有在第一次出现才是定义变量。
变量名 = 值
使用交互式方式,如果要查看变量内容,直接输入变量名即可,不需要使用 print 函数。 使用解释器执行,如果要输出变量的内容,必须要使用 print 函数。
4.2 变量的类型
在 Python 中定义变量是不需要指定类型(在其他很多高级语言中都需要),Python 可以根据 = 等号右侧的值,自动推导出变量中存储数据的类型。
数据类型可以分为数字型和非数字型。
数字型:
- 整型 (int):Python3 中的所有整数都表示为长整数。因此,长整数没有单独的数字类型。
- 浮点型(float)
- 布尔型(bool):真 True,非 0 数 —— 非零即真,假 False,0。
- 复数型 (complex):复数是由 x + yj 表示的有序对的实数浮点数组成,其中 x 和 y 是实数,j 是虚数单位。
非数字型:
- 有些运算符还支持这些数据类型,详见 4.4.5.3 运算符。
- 字符串(str):加号 (+) 是字符串连接运算符,星号 (*) 是重复运算符。
- 列表(list)
- 元组(tuple)
- 字典(dict)
**提示:**在 Python 2.x 中,整数根据保存数值的长度还分为:int(整数)、long(长整数)。
使用 type 函数可以查看一个变量的类型:
In [1]: type(name)
<补>不同类型变量之间的计算
- 数字型变量之间可以直接计算。
- 在 Python 中,两个数字型变量是可以直接进行算术运算的。
- 如果变量是 bool 型,在计算时 True 对应的数字是 1,False 对应的数字是 0。
- 字符串变量之间使用 + 拼接字符串。
- 字符串变量可以和整数使用 * 重复拼接相同的字符串。
- 数字型变量和字符串之间不能进行其他计算。
<补>从键盘获取输入信息:input 在 Python 中可以使用 input 函数从键盘等待用户的输入。 用户输入的任何内容 Python 都认为是一个字符串。
字符串变量 = input("提示信息:")
<补>类型转换函数
| 函数 | 说明 |
|---|---|
| int(x) | 将 x 转换为一个整数 |
| float(x) | 将 x 转换到一个浮点数 |
| str(x) | 将对象 x 转换为字符串表示形式 |
| tuple(s) | 将 s 转换为元组 |
| list(s) | 将 s 转换为列表 |
price = float(input("请输入价格:"))
<补>格式化输出:print
如果希望输出文字信息的同时,一起输出数据,就需要使用到格式化操作符。
% 被称为格式化操作符,专门用于处理字符串中的格式。
包含 % 的字符串,被称为格式化字符串。
% 和不同的字符连用,不同类型的数据需要使用不同的格式化字符。
| 格式化字符 | 含义 |
|---|---|
| %s | 字符串 |
| %d | 有符号十进制整数,%06d 表示输出的整数显示位数,不足的地方使用 0 补全 |
| %f | 浮点数,%.2f 表示小数点后只显示两位 |
| %% | 输出 % |
语法格式如下:
print("格式化字符串" % 变量 1)
print("格式化字符串" % (变量 1, 变量 2...))
4.4.5 公共方法和变量的高级应用
4.4.5.1 内置函数 Python 包含了以下内置函数:
| 函数 | 描述 | 备注 |
|---|---|---|
| len(item) | 计算容器中元素个数 | |
| del(item) | 删除变量 | del 有两种方式 |
| max(item) | 返回容器中元素最大值 | 如果是字典,只针对 key 比较 |
| min(item) | 返回容器中元素最小值 | 如果是字典,只针对 key 比较 |
| cmp(item1, item2) | 比较两个值,-1 小于 / 0 相等 / 1 大于 | Python 3.x 取消了 cmp 函数 |
注意:字符串比较符合以下规则:"0" < "A" < "a"。
4.4.5.2 切片
| 描述 | Python 表达式 | 结果 | 支持的数据类型 |
|---|---|---|---|
| 切片 | "0123456789"[::-2] | "97531" | 字符串、列表、元组 |
切片使用索引值来限定范围,从一个大的字符串中切出小的字符串。 列表和元组都是有序的集合,都能够通过索引值获取到对应的数据。 字典是一个无序的集合,是使用键值对保存数据。
面向对象编程 —— Object Oriented Programming 简写 OOP。
面向过程 —— 怎么做?
- 把完成某一个需求的所有步骤从头到尾逐步实现。
- 根据开发需求,将某些功能独立的代码封装成一个又一个函数。
- 最后完成的代码,就是顺序地调用不同的函数。
- **特点:**注重步骤与过程,不注重职责分工。如果需求复杂,代码会变得很复杂。开发复杂项目,没有固定的套路,开发难度很大!
面向对象 —— 谁来做?
- 相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法。
- 在完成某一个需求前,首先确定职责 —— 要做的事情(方法)。
- 根据职责确定不同的对象,在对象内部封装不同的方法(多个)。
- 最后完成的代码,就是顺序地让不同的对象调用不同的方法。
- **特点:**注重对象和职责,不同的对象承担不同的职责。更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。需要在面向过程基础上,再学习一些面向对象的语法。
类和对象
- 类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,特征被称为属性,行为被称为方法。
- 对象是由类创建出来的一个具体存在,是类的实例化。
- 在程序开发中,要设计一个类,通常需要满足一下三个要素:
- 类名:这类事物的名字,满足大驼峰命名法。
- 属性:这类事物具有什么样的特征。
- 方法:这类事物具有什么样的行为。
第二部分 面向对象基础语法
2.1 dir 内置函数和内置方法
在 Python 中对象几乎是无所不在的,我们之前学习的变量、数据、函数都是对象。在 Python 中可以使用以下两个方法验证:
- 在标识符 / 数据后输入一个点
.,然后按下 TAB 键,iPython 会提示该对象能够调用的方法列表。 - 使用内置函数
dir传入标识符 / 数据,可以查看对象内的所有属性及方法。
提示:
__方法名__格式的方法是 Python 提供的内置方法 / 属性。
| 序号 | 方法名 | 类型 | 作用 |
|---|---|---|---|
| 01 | __new__ | 方法 | 创建对象时,会被自动调用 |
| 02 | __init__ | 方法 | 对象被初始化时,会被自动调用 |
| 03 | __del__ | 方法 | 对象被从内存中销毁前,会被自动调用 |
| 04 | __str__ | 方法 | 返回对象的描述信息,print 函数输出使用 |
**提示:**利用好
dir()函数,在学习时很多内容就不需要死记硬背了。
2.2 定义简单的类(只包含方法)
面向对象是更大的封装,在一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!
定义一个只包含方法的类:
class 类名:
def 方法 1(self, 参数列表):
pass
def 方法 2(self, 参数列表):
pass
方法的定义格式和之前学习过的函数几乎一样,区别在于第一个参数必须是 self。注意:类名的命名规则要符合大驼峰命名法。 当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()
在面向对象开发中,引用的概念是同样适用的!
使用 print 输出对象变量,默认情况下,是能够输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)。
**提示:**在计算机中,通常使用十六进制表示内存地址。
如果在开发中,希望使用 print 输出对象变量时,能够打印自定义的内容,就可以利用 __str__ 这个内置方法了:
class Cat:
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 去了" % self.name)
def __str__(self):
return "我是小猫:%s" % self.name
tom = Cat("Tom")
print(tom)
注意:
__str__方法必须返回一个字符串。
2.3 方法中的 self 参数
在 Python 中,要给对象设置属性,非常的容易,只需要在类的外部的代码中直接通过对象。设置一个属性即可,但是不推荐使用:
class Cat:
"""这是一个猫类"""
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫在喝水")
tom = Cat()
# 给对象设置属性
tom.name = "Tom"
因为:对象属性的封装应该封装在类的内部。
由哪一个对象调用的方法,方法内的 self 就是哪一个对象的引用。 在类封装的方法内部,self 就表示当前调用方法的对象自己,在方法内部:
- 可以通过
self.访问对象的属性,也可以通过self.调用对象的其他方法。 - 调用方法时,程序员不需要传递 self 参数。
- 在类的外部,通过变量名。访问对象的属性和方法。
- 在类封装的方法中,通过
self.访问对象的属性和方法。
2.4 初始化方法:__init__
当使用 类名 () 创建对象时,会自动执行以下操作:
- 为对象在内存中分配空间 —— 创建对象。
- 为对象的属性设置初始值 —— 初始化方法 (
__init__)。
__init__ 方法是专门用来定义一个类具有哪些属性的方法!
在 __init__ 方法内部使用 self. 属性名 = 属性的初始值就可以定义属性,定义属性之后,再使用类创建的对象,都会拥有该属性。
在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行改造:
- 把希望设置的属性值,定义成
__init__方法的参数。 - 在方法内部使用
self.属性 = 形参接收外部传递的参数。 - 在创建对象时,使用
类名 (属性 1, 属性 2…)调用。
class Cat:
def __init__(self, name):
print("初始化方法 %s" % name)
self.name = name
...
tom = Cat("Tom")
...
lazy_cat = Cat("大懒猫")
...
2.5 私有属性和私有方法
应用场景 在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到。
- 私有属性就是对象不希望公开的属性。
- 私有方法就是对象不希望公开的方法。
定义方式 在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法:

伪私有属性和私有方法
Python 中,并没有真正意义的私有。在给属性、方法命名时,实际是对名称做了一些特殊处理,使得外界无法访问到。
处理方式:在名称前面加上 _类名 => _类名__名称。
# 私有属性,外部不能直接访问到
print(xiaofang._Women__age)
# 私有方法,外部不能直接调用
xiaofang._Women__secret()
**提示:**在日常开发中,不要使用这种方式,访问对象的私有属性或私有方法。
3. 封装、继承和多态
面向对象三大特性:
- 封装:根据职责将属性和方法封装到一个抽象的类中。
- 继承:实现代码的重用,相同的代码不需要重复的编写。
- 多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度。
3.1 继承
3.1.1 单继承 继承的概念:子类拥有父类以及父类的父类中封装的所有属性和方法。
class 类名 (父类名):
pass
当父类的方法实现不能满足子类需求时,可以对方法进行重写 (override)。 重写父类方法有两种情况:
- 覆盖父类的方法:父类的方法实现和子类的方法实现完全不同。具体的实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现。
- 扩展父类的方法:子类的方法实现中包含父类的方法实现。在子类中重写父类的方法;在需要的位置使用
super().父类方法来调用父类方法的执行代码;其他的位置针对子类的需求,编写子类特有的代码实现。
关于 super
在 Python 中 super 是一个特殊的类。
super() 就是使用 super 类创建出来的对象。
最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现。
**调用父类方法的另外一种方式:**在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:
父类名。方法 (self)。目前在 Python 3.x 还支持这种方式,但不推荐使用,因为一旦父类发生变化,方法调用位置的类名同样需要修改。
父类的私有属性和私有方法 子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法。 子类对象可以通过父类的公有方法间接访问到私有属性或私有方法。
私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能直接访问私有属性、方法。通常用于做一些内部的事情。
3.1.2 多继承 子类可以拥有多个父类,并且具有所有父类的属性和方法,例如:孩子会继承自己父亲和母亲的特性。
class 子类名 (父类名 1, 父类名 2...):
pass
Python 中的 MRO 算法(Method Resolution Order) 如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?
**提示:**开发时,应该尽量避免这种容易产生混淆的情况!——如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承。
Python 中针对类提供了一个内置属性 __mro__ 可以查看方法搜索顺序。
在搜索方法时,是按照 mro 的输出结果从左至右的顺序查找的。
- 如果在当前类中找到方法,就直接执行,不再搜索。
- 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索。
- 如果找到最后一个类,还没有找到方法,程序报错。
MRO 是 method resolution order —— 方法搜索顺序,主要用于在多继承时判断方法、属性的调用路径。
新式类与旧式(经典)类
- 新式类:以 object 为基类的类,推荐使用。
- 经典类:不以 object 为基类的类,不推荐使用。
在 Python 3.x 中定义类时,如果没有指定父类,会默认使用 object 作为该类的基类 —— Python 3.x 中定义的类都是新式类,在 Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为基类。
为了保证编写的代码能够同时在 Python 2.x 和 Python 3.x 运行!今后在定义类时,如果没有父类,建议统一继承自 object:
class 类名 (object):
pass
object 是 Python 为所有对象提供的基类,提供有一些内置的属性和方法,可以使用
dir(object)函数查看。
3.2 多态
面向对象三大特性:
- 封装:根据职责将属性和方法封装到一个抽象的类中。定义类的准则。
- 继承:实现代码的重用,相同的代码不需要重复的编写。设计类的技巧。子类针对自己特有的需求,编写特定的代码。
- 多态:不同的子类对象调用相同的父类方法,产生不同的执行结果。增加代码的灵活度。以继承和重写父类方法为前提。调用方法的技巧,不会影响到类的内部设计。
多态更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
案例:
- 在 Dog 类中封装方法 game:普通狗只是简单的玩耍。
- 定义 XiaoTianDog 继承自 Dog,并且重写 game 方法:哮天犬需要在天上玩耍。
- 定义 Person 类,并且封装一个和狗玩的方法:在方法内部,直接让狗对象调用 game 方法。

Person 类中只需要让狗对象调用 game 方法,而不关心具体是什么狗。
4. 类属性和类方法
4.1 类的结构
通常会把:
- 创建出来的对象叫做类的实例。
- 创建对象的动作叫做实例化。
- 对象的属性叫做实例属性。
- 对象调用的方法叫做实例方法。
每一个对象都有自己独立的内存空间,保存各自不同的属性。 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部。

在 Python 中,类是一个特殊的对象。
Python 中一切皆对象:
class AAA:定义的类属于类对象obj1 = AAA()属于实例对象
在程序运行时,类同样会被加载到内存。
在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例。
除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法——类属性、类方法,通过 类名. 的方式可以访问类的属性或者调用类的方法。

4.2 类属性和实例属性
类属性就是类对象中定义的属性。 通常用来记录与这个类相关的特征。 类属性不会用于记录具体对象的特征。
**示例:**定义一个工具类,每件工具都有自己的 name。 需求 —— 知道使用这个类,创建了多少个工具对象?

属性的获取机制 在 Python 中属性的获取存在一个向上查找机制。

因此,要访问类属性有两种方式:
类名。类属性对象。类属性(不推荐,因为如果使用对象。类属性 = 值赋值语句,只会给对象添加一个属性,而不会影响到类属性的值)
4.3 类方法和静态方法
4.3.1 类方法 类属性就是针对类对象定义的属性。 使用赋值语句在 class 关键字下方可以定义类属性。 类属性用于记录与这个类相关的特征。
类方法就是针对类对象定义的方法。 在类方法内部可以直接访问类属性或者调用其他的类方法。 语法如下:
@classmethod
def 类方法名 (cls):
pass
类方法需要用修饰器 @classmethod 来标识,告诉解释器这是一个类方法。
类方法的第一个参数应该是 cls。
由哪一个类调用的方法,方法内的 cls 就是哪一个类的引用。
这个参数和实例方法的第一个参数是 self 类似。
**提示:**使用其他名称也可以,不过习惯使用 cls。
通过 类名. 调用类方法,调用方法时,不需要传递 cls 参数。
在方法内部可以通过 cls. 访问类的属性,也可以通过 cls. 调用其他的类方法。
示例
定义一个工具类,每件工具都有自己的 name。
需求 —— 在类封装一个 show_tool_count 的类方法,输出使用当前这个类,创建的对象个数。
@classmethod
def show_tool_count(cls):
"""显示工具对象的总数"""
print("工具对象的总数 %d" % cls.count)
4.3.2 静态方法 在开发时,如果需要在类中封装一个方法,这个方法:
- 既不需要访问实例属性或者调用实例方法。
- 也不需要访问类属性或者调用类方法。 这个时候,可以把这个方法封装成一个静态方法。 语法如下:
@staticmethod
def 静态方法名 ():
pass
静态方法需要用修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法。
通过 类名. 调用静态方法。
示例:
- 静态方法
show_help显示游戏帮助信息。 - 类方法
show_top_score显示历史最高分。 - 实例方法
start_game开始当前玩家的游戏。

探索:
- 实例方法 —— 方法内部需要访问实例属性。实例方法内部可以使用
类名.访问类属性。 - 类方法 —— 方法内部只需要访问类属性。
- 静态方法 —— 方法内部,不需要访问实例属性和类属性。
5. 单例
5.1 单例设计模式
设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
单例设计模式目的 —— 让类创建的对象,在系统中只有唯一的一个实例。
每一次执行 类名 () 返回的对象,内存地址是相同的。
单例设计模式的应用场景
- 音乐播放对象
- 回收站对象
- 打印机对象 ……
5.2 静态方法:__new__
使用 类名 () 创建对象时,Python 的解释器首先会调用 __new__ 方法为对象分配空间。
__new__ 是一个由 object 基类提供的内置的静态方法,主要作用有两个:
- 在内存中为对象分配空间。
- 返回对象的引用。
Python 的解释器获得对象的引用后,将引用作为第一个参数,传递给 __init__ 方法。
重写
__new__方法的代码非常固定! 重写__new__方法一定要return super().__new__(cls),否则 Python 的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法。 注意:__new__是一个静态方法,在调用时需要主动传递 cls 参数。


5.3 Python 中的单例
单例 —— 让类创建的对象,在系统中只有唯一的一个实例。
定义一个类属性,初始值是 None,用于记录单例对象的引用。
重写 __new__ 方法。
如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果。
返回类属性中记录的对象引用。


只执行一次初始化工作
在每次使用 类名 () 创建对象时,Python 的解释器都会自动调用两个方法:
__new__分配空间。__init__对象初始化。 在对__new__方法改造之后,每次都会得到第一次被创建对象的引用。 但是:初始化方法还会被再次调用。
需求 让初始化动作只被执行一次。
解决办法
定义一个类属性 init_flag 标记是否执行过初始化动作,初始值为 False。
在 __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作。
然后将 init_flag 设置为 True。
这样,再次自动调用 __init__ 方法时,初始化动作就不会被再次执行了。

Tips
- Python 能够自动的将一对括号内部的代码连接在一起:
'''
**需求**
* 定义 `input_password` 函数,提示用户输入密码
* 如果用户输入长度 < 8,抛出异常
* 如果用户输入长度 >=8,返回输入的密码
'''
def input_password():
# 1. 提示用户输入密码
pwd = input("请输入密码:")
# 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码
if len(pwd) >= 8:
return pwd
# 3. 密码长度不够,需要抛出异常
# 1> 创建异常对象 - 使用异常的错误信息字符串作为参数
ex = Exception("密码长度不够")
# 2> 抛出异常对象
raise ex
try:
user_pwd = input_password()
print(user_pwd)
except Exception as result:
print("发现错误:%s" % result)
- 一个对象的属性可以是另外一个类创建的对象。
- 在
__init__方法中定义类的属性时,如果不知道设置什么初始值,可以设置为 None):None 关键字表示什么都没有,表示一个空对象,没有方法和属性,是一个特殊的常量。可以将 None 赋值给任何一个变量。
在 Python 中针对 None 比较时,建议使用
is判断。
eval()函数十分强大 —— 将字符串当成有效的表达式来求值并返回计算结果。

在开发时千万不要使用 eval 直接转换 input 的结果,举个例子:
__import__('os').system('ls')
# 等价代码
import os
os.system("终端命令")
总结与建议
掌握 Python 基础语法和面向对象编程是成为合格开发者的第一步。建议学习者:
- 多动手实践:理论结合代码,通过编写小程序巩固知识点。
- 遵循规范:养成遵守 PEP 8 编码规范的习惯,提高代码可读性。
- 深入理解:不仅要知道怎么用,还要理解背后的原理,如内存管理、对象模型等。
- 持续学习:关注社区动态,学习新的库和框架,拓展技术边界。
通过本文的学习,希望你能建立起系统的 Python 知识体系,为后续深入学习数据分析、Web 开发或人工智能打下坚实基础。

