阅读 777

这篇文章里的都搞定,你的Python基础就差不多可以接私活了

前言

学一门语言贵在坚持用它,不用就淡忘了,而记录下一篇文章也有助于日后快速回忆。全文分为两大部分,分别是Python基础语法和面向对象。

第一部分 Python基础语法

![](https://pic3.zhimg.com/80/v2-0ffebab4402a464192d0a82bee64cf1f_720w.png)
第一部分 Python基础语法

1. 认识Python

1.1 Python 简介

Python 的创始人为吉多·范罗苏姆(Guido van Rossum)。
Python 的设计目标: 一门简单直观的语言并与主要竞争者一样强大 开源,以便任何人都可以为它做贡献 代码像纯英语那样容易理解 适用于短期开发的日常任务
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,Integrated Development Environment)—— 集成了开发软件需要的所有工具,一般包括以下工具:
  • 图形用户界面
  • 代码编辑器(支持 代码补全/自动缩进)
  • 编译器/解释器
  • 调试器(断点/单步执行)
  • ……
![](https://pic4.zhimg.com/80/v2-f41169a54691afe9d31ff76d32aa2726_720w.png)
PyCharm 是 Python 的一款非常优秀的集成开发环境
![](https://pic2.zhimg.com/80/v2-d03849d3795b227cb8e043ca3c4219c7_720w.png)
PyCharm运行工具栏

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 源程序
![](https://pic3.zhimg.com/80/v2-cf06133a55fc3ba596069d89a1030c8e_720w.png)
让选中的程序可以执行

2. 注释

  • 注释的作用 使用用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性

2.1 单行注释(行注释)

  • 以 # 开头,# 右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用

    print("hello python") # 输出 hello python

为了保证代码的可读性,# 后面建议先添加一个空格,然后再编写相应的说明文字;为了保证代码的可读性,注释和代码之间 至少要有 两个空格。

2.2 多行注释(块注释)

  • 要在 Python 程序中使用多行注释,可以用 一对 连续的 三个 引号(单引号和双引号都可以)

    """ 这是一个多行注释 在多行注释之间,可以写很多很多的内容…… """ print("hello python")

提示:
  1. 注释不是越多越好,对于一目了然的代码,不需要添加注释
  2. 对于 复杂的操作,应该在操作开始前写上若干行注释
  3. 对于 不是一目了然的代码,应在其行尾添加注释(为了提高可读性,注释应该至少离开代码 2 个空格)
  4. 绝不要描述代码,假设阅读代码的人比你更懂 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 逻辑运算符

<补>程序执行原理

![](https://pic1.zhimg.com/80/v2-93e234ae89c928653c679a9a9ae857ae_720w.png)
Python程序执行示意图
  1. 操作系统会首先让 CPU 把 Python 解释器 的程序复制到 内存 中
  2. Python 解释器 根据语法规则,从上向下 让 CPU 翻译 Python 程序中的代码
  3. 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)

<补>不同类型变量之间的计算

  1. 数字型变量 之间可以直接计算
  • 在 Python 中,两个数字型变量是可以直接进行 算数运算的
  • 如果变量是 bool 型,在计算时 True 对应的数字是 1 False 对应的数字是 0
  1. 字符串变量 之间使用 + 拼接字符串
  2. 字符串变量 可以和 整数 使用 * 重复拼接相同的字符串
  3. 数字型变量 和 字符串 之间 不能进行其他计算

<补>从键盘获取输入信息: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. 面向对象基础语法

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 私有属性和私有方法

应用场景
  • 在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到
  • 私有属性 就是 对象 不希望公开的 属性
  • 私有方法 就是 对象 不希望公开的 方法
定义方式
  • 在 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线,定义的就是 私有属性或方法:
![](https://pic2.zhimg.com/80/v2-e3ad88ac9879369a5e43d9ae0851d7d6_720w.png)
私有属性和私有方法
伪私有属性和私有方法 Python 中,并没有 真正意义 的 私有 在给 属性、方法 命名时,实际是对名称做了一些特殊处理,使得外界无法访问到 处理方式:在 名称 前面加上_类名 => _类名__名称
# 私有属性,外部不能直接访问到
print(xiaofang._Women__age)
# 私有方法,外部不能直接调用
xiaofang._Women__secret()
复制代码
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法。

3. 封装、继承和多态

面向对象三大特性:
  1. 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
  2. 继承 实现代码的重用,相同的代码不需要重复的编写
  3. 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

3.1 继承

3.1.1 单继承

继承的概念:子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法。
class 类名(父类名):
    pass
复制代码
当 父类 的方法实现不能满足子类需求时,可以对方法进行重写(override) 重写 父类方法有两种情况:
  1. 覆盖 父类的方法:父类的方法实现 和 子类的方法实现完全不同 具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现。
  2. 对父类方法进行 扩展:子类的方法实现 中 包含 父类的方法实现 在子类中 重写 父类的方法;在需要的位置使用 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 多态

面向对象三大特性:
  1. 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 定义类的准则
  2. 继承 实现代码的重用,相同的代码不需要重复的编写 设计类的技巧 子类针对自己特有的需求,编写特定的代码
  3. 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果 增加代码的灵活度 以 继承 和 重写父类方法 为前提 调用方法的技巧,不会影响到类的内部设计
多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
案例: 在 Dog 类中封装方法 game:普通狗只是简单的玩耍 定义 XiaoTianDog 继承自 Dog,并且重写 game 方法:哮天犬需要在天上玩耍 定义 Person 类,并且封装一个 和狗玩 的方法:在方法内部,直接让 狗对象 调用 game 方法
![](https://pic4.zhimg.com/80/v2-5fd4bc422fc95dd21f04fd70d08f4c01_720w.png)
多态示例
Person 类中只需要让 狗对象 调用 game 方法,而不关心具体是 什么狗。

4. 类属性和类方法

4.1 类的结构

通常会把: 创建出来的 对象 叫做 类的实例 创建对象的 动作 叫做 实例化 对象的属性 叫做 实例属性 对象调用的方法 叫做 实例方法 每一个对象 都有自己独立的内存空间,保存各自不同的属性 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
![](https://pic3.zhimg.com/80/v2-185db99889ec361fa3dbbd9b506f09bf_720w.png)
各个不同的属性,独一份的方法
在 Python 中,类是一个特殊的对象。
Python 中 一切皆对象: class AAA: 定义的类属于 类对象 obj1 = AAA() 属于 实例对象
在程序运行时,类同样会被加载到内存 在程序运行时,类对象在内存中只有一份,使用 一个类可以创建出很多个对象实例 除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法——类属性、类方法,通过 类名. 的方式可以 访问类的属性 或者 调用类的方法
![](https://pic2.zhimg.com/80/v2-b46468c7475662f93a28dec31c7305a5_720w.png)
类的结构

4.2 类属性和实例属性

类属性 就是 类对象中定义的属性 通常用来记录与这个类相关的特征 类属性不会用于记录具体对象的特征 示例: 定义一个 工具类,每件工具都有自己的 name: 需求 —— 知道使用这个类,创建了多少个工具对象?
![](https://pic1.zhimg.com/80/v2-d452ef763c2ebcd789f39dc6b046177d_720w.png)

属性的获取机制

在 Python 中 属性的获取 存在一个 向上查找机制
![](https://pic1.zhimg.com/80/v2-0146d5733379c9891601c9c1463c64fd_720w.png)
因此,要访问类属性有两种方式:
  • 类名.类属性
  • 对象.类属性 (不推荐,因为如果使用 对象.类属性 = 值 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值)

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 开始当前玩家的游戏
![](https://pic1.zhimg.com/80/v2-8d24571745a998538d36ec3865d1a622_720w.png)
探索:
  • 实例方法 —— 方法内部需要访问 实例属性 实例方法 内部可以使用 类名. 访问类属性
  • 类方法 —— 方法内部 只需要访问 类属性
  • 静态方法 —— 方法内部,不需要访问 实例属性 和 类属性

5. 单例

5.1 单例设计模式

  • 设计模式 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
  • 单例设计模式 目的 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例 每一次执行 类名() 返回的对象,内存地址是相同的
  • 单例设计模式的应用场景 音乐播放 对象 回收站 对象 打印机 对象 ……

5.2 静态方法: new

  • 使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间
  • __new__ 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个: 在内存中为对象 分配空间 返回 对象的引用
  • Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__ 方法
重写 __new__ 方法 的代码非常固定!
  • 重写 __new__ 方法 一定要 return super().__new__(cls),否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法
  • 注意:__new__ 是一个静态方法,在调用时需要 主动传递 cls 参数
![](https://pic1.zhimg.com/80/v2-d69dd97043da1602fb2e2ca62cd4691c_720w.png)
![](https://pic3.zhimg.com/80/v2-b9aeeb240dea4a0c71404f62647ab677_720w.png)

5.3 Python 中的单例

  • 单例 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例 定义一个 类属性,初始值是 None,用于记录 单例对象的引用 重写 __new__ 方法 如果 类属性 is None,调用父类方法分配空间,并在类属性中记录结果 返回 类属性 中记录的 对象引用
![](https://pic4.zhimg.com/80/v2-d629f237967444252d57293836977814_720w.png)
![](https://pic1.zhimg.com/80/v2-bf07599406031918907cab274562ee0e_720w.png)

只执行一次初始化工作

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

Tips

1、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)
复制代码
2、一个对象的 属性 可以是 另外一个类创建的对象。 3、在__init__方法中定义类的属性时,如果 不知道设置什么初始值,可以设置为 None):None 关键字 表示 什么都没有,表示一个 空对象,没有方法和属性,是一个特殊的常量。可以将 None 赋值给任何一个变量。
在 Python 中针对 None 比较时,建议使用is 判断
4、eval() 函数十分强大 —— 将字符串 当成 有效的表达式 来求值 并 返回计算结果
![](https://pic2.zhimg.com/80/v2-891384d94feec4a60b1c1ecf5301e0fc_720w.png)
在开发时千万不要使用 eval 直接转换 input 的结果,举个例子:
__import__('os').system('ls')
# 等价代码import os
os.system("终端命令")
复制代码

最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以加小编的python学习交流企鹅群:1075110200,配套资料可以找管理小姐姐免费领取。