前言 学一门语言贵在坚持用它,不用就淡忘了,而记录下一篇文章也有助于日后快速回忆。全文分为两大部分,分别是Python基础语法和面向对象。 第一部分 Python基础语法4 E C; a! `0 i
第一部分 Python基础语法 1. 认识Python1.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.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)—— 集成了开发软件需要的所有工具,一般包括以下工具: 图形用户界面 代码编辑器(支持 代码补全/自动缩进) 编译器/解释器 调试器(断点/单步执行) ……
; W" I4 c+ a$ K3 }+ U8 _- P) T
8 l) B% U7 g0 u$ l
PyCharm 是 Python 的一款非常优秀的集成开发环境
; v( t; x ]: v+ m- y+ H3 i. Y. m
PyCharm运行工具栏 1.3. PyCharm 的设置PyCharm 的 配置信息 是保存在 用户家目录下 的 .PyCharmxxxx.x 目录下的,xxxx.x 表示当前使用的 PyCharm 的版本号 1.3.1 恢复 PyCharm 的初始设置:$ rm -r ~/.PyCharm2016.3重新启动 PyCharm
& Z7 I9 z' X+ c w8 v 1.3.2 PyCharm 安装和启动步骤:执行以下终端命令,解压缩下载后的安装包
4 X. |. f ?$ u0 d8 j4 V% V, c# W $ tar -zxvf pycharm-professional-2017.1.3.tar.gz/opt 目录用户存放给主机额外安装的软件 $ sudo mv pycharm-2017.1.3/ /opt/切换工作目录 * x$ N0 M0 n6 \! d/ {0 U* g
$ cd /opt/pycharm-2017.1.3/bin启动 PyCharm
( y B, S# [% t: z" I $ ./pycharm.sh1.3.3 设置启动图标在专业版中,选择菜单 Tools / Create Desktop Entry... 可以设置任务栏启动图标 注意:设置图标时,需要勾选 Create the entry for all users
0 S6 w& B0 c2 j快捷方式文件# n G! k, `7 |0 ^# f
/usr/share/applications/jetbrains-pycharm.desktop 5 S7 @' a) t6 J0 D
在 ubuntu 中,应用程序启动的快捷方式通常都保存在 /usr/share/applications 目录下 1.3.4 卸载之前版本的 PyCharm要卸载 PyCharm 只需要做以下两步工作: 删除解压缩目录 6 B9 Q4 \2 w8 U/ C2 }) k
$ sudo rm -r /opt/pycharm-2016.3.1/删除家目录下用于保存配置信息的隐藏目录
- T' j4 M3 I8 z9 i $ rm -r ~/.PyCharm2016.3/如果不再使用 PyCharm 还需要将 /usr/share/applications/ 下的 jetbrains-pycharm.desktop 删掉 1.4. 多文件项目的演练开发 项目 就是开发一个 专门解决一个复杂业务功能的软件 通常每 一个项目 就具有一个 独立专属的目录,用于保存 所有和项目相关的文件 在 PyCharm 中,要想让哪一个 Python 程序能够执行,必须首先通过 鼠标右键的方式执行 一下 对于初学者而言,在一个项目中设置多个程序可以执行,是非常方便的,可以方便对不同知识点的练习和测试 对于商业项目而言,通常在一个项目中,只有一个 可以直接执行的 Python 源程序 9 t. ?9 q& S/ }) E3 H) y2 O9 B
. m5 c! }2 D9 [. l; k/ |
让选中的程序可以执行 2. 注释2.1 单行注释(行注释)print("hello python") # 输出 `hello python`为了保证代码的可读性,# 后面建议先添加一个空格,然后再编写相应的说明文字;为了保证代码的可读性,注释和代码之间 至少要有 两个空格。 2.2 多行注释(块注释)"""
! W' j) @1 `0 f( W4 S这是一个多行注释
% L( u6 L$ Z" H4 L ]
" M& A) `3 D. z; a9 t在多行注释之间,可以写很多很多的内容……
" [, D. u3 b- v""") `6 E* S0 Z. i# V, W- D7 q
print("hello python")提示: 注释不是越多越好,对于一目了然的代码,不需要添加注释 对于 复杂的操作,应该在操作开始前写上若干行注释 对于 不是一目了然的代码,应在其行尾添加注释(为了提高可读性,注释应该至少离开代码 2 个空格) 绝不要描述代码,假设阅读代码的人比你更懂 Python,他只是不知道你的代码要做什么
' v+ [2 N1 n3 D 2.3 代码规范: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 赋值运算符运算符 描述 实例 = 简单的赋值运算符 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 用于判断 两个变量引用的对象是否为同一个 == 用于判断 引用变量的值 是否相等
# F8 ?( G3 v( Y4 A1 ] 3.5 成员运算符Python成员运算符测试给定值是否为序列中的成员。有两个成员运算符,如下所述: 运算符 描述 in 如果在指定的序列中找到一个变量的值,则返回true,否则返回false。not in 如果在指定序列中找不到变量的值,则返回true,否则返回false。 3.6 逻辑运算符运算符 逻辑表达式 描述 and x and y 只有 x 和 y 的值都为 True,才会返回 True<br />否则只要 x 或者 y 有一个值为 False,就返回 False or x or y 只要 x 或者 y 有一个值为 True,就返回 True<br />只有 x 和 y 的值都为 False,才会返回 False not not x 如果 x 为 True,返回 False<br />如果 x 为 False,返回 True 3.7 运算符优先级以下表格的算数优先级由高到最低顺序排列:
0 N1 O( Q3 m" ]% Y' A% s% d& n
运算符 描述 ** 幂 (最高优先级) * / % // 乘、除、取余数、取整除 + - 加法、减法 <= < > >= 比较运算符 == != 等于运算符 = %= /= //= -= += *= **= 赋值运算符 is is not 身份运算符 in not in 成员运算符 not or and 逻辑运算符 <补>程序执行原理
. I* t7 @" d1 t' `
Python程序执行示意图 Python 的解释器有多大?# 1. 确认解释器所在位置
, k$ R) t4 c. z: D$ which python
* {, G* J# h4 l6 \, b, N9 H2 e" F) E$ ]0 |3 Y% H. Y2 E
# 2. 查看 python 文件大小(只是一个软链接)& ~ k0 X$ S ~2 g! W
$ ls -lh /usr/bin/python. @ c0 K Y& F5 l% M
6 W3 P1 K# ]% A! ]: B
# 3. 查看具体文件大小+ O3 ^7 Q; {# y$ G: f \
$ ls -lh /usr/bin/python2.74. 变量4.1 变量定义变量名 = 值使用交互式方式,如果要查看变量内容,直接输入变量名即可,不需要使用 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) 2 O, Q; ^: }: M3 s8 _9 b H
提示:在 Python 2.x 中,整数 根据保存数值的长度还分为: int(整数) long(长整数)
使用 type 函数可以查看一个变量的类型
4 ^+ x% D" n, X; M9 J# p3 ?; z; o: G In [1]: type(name)<补>不同类型变量之间的计算数字型变量 之间可以直接计算 " Y# f, _; g7 w; u
<补>从键盘获取输入信息:input字符串变量 = 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 表示小数点后只显示两位 %% 输出 % 语法格式如下:
6 k8 }0 r/ Z$ X, G3 q" a5 k print("格式化字符串" % 变量1)" s5 C5 `+ d! J
7 A- d% [% b$ i, z8 r7 H; q% K. h
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" 字符串、列表、元组 切片 使用 索引值 来限定范围,从一个大的 字符串 中 切出 小的 字符串 列表 和 元组 都是 有序 的集合,都能够 通过索引值 获取到对应的数据 字典 是一个 无序 的集合,是使用 键值对 保存数据
( O# U. f" n! a8 i. M, V% i
0 b: u; M4 C* h面向对象编程 —— Object Oriented Programming 简写 OOP 面向过程 —— 怎么做?
6 }( K4 \! B u# z8 K% f把完成某一个需求的 所有步骤 从头到尾 逐步实现. K1 u- J: S4 E8 R! q
根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数' _7 B j1 I- `1 X1 X
最后完成的代码,就是顺序地调用 不同的函数
% A! _' k$ s* ]2 @特点: C4 @/ m% S: Q! ~; O5 q
注重 步骤与过程,不注重职责分工
0 X# |; h0 I1 f( ~) k5 Y9 l5 X如果需求复杂,代码会变得很复杂
/ o0 T( w; r ]- u6 h7 a开发复杂项目,没有固定的套路,开发难度很大! 面向对象 —— 谁来做?相比较函数,面向对象 是更大的封装,根据职责在 一个对象中封装多个方法 在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)3 n+ X: @* j; y; U2 c H2 a5 E
根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)
. y) W) L0 r. n- H! } G最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法1 b3 J+ [4 q" O; @" ^/ G& n
特点:5 M2 [, S0 F6 S# |
注重 对象和职责,不同的对象承担不同的职责, Y0 _% o% `8 o- [
更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
: ~9 V/ Y. ^* N- }需要在面向过程基础上,再学习一些面向对象的语法 类和对象$ n6 b4 E' q0 B& [: ~9 {
类 是对一群具有 相同 特征 或者 行为 的事物的一个统称,是抽象的,特征 被称为 属性,行为 被称为 方法。5 W! V7 _% E+ f* @( i" r; V
对象 是 由类创建出来的一个具体存在,是类的实例化。
u4 R& H$ S! s0 B( r在程序开发中,要设计一个类,通常需要满足一下三个要素: 类名 这类事物的名字,满足大驼峰命名法 属性 这类事物具有什么样的特征 方法 这类事物具有什么样的行为
- _1 o" {% z v2 s6 M 2. 面向对象基础语法2.1 dir 内置函数和内置方法在 Python 中 对象几乎是无所不在的,我们之前学习的 变量、数据、函数 都是对象。在 Python 中可以使用以下两个方法验证: 序号 方法名 类型 作用 01 __new__ 方法 创建对象时,会被 自动 调用 02 __init__ 方法 对象被初始化时,会被 自动 调用 03 __del__ 方法 对象被从内存中销毁前,会被 自动 调用 04 __str__ 方法 返回对象的描述信息,print 函数输出使用 提示 利用好 dir() 函数,在学习时很多内容就不需要死记硬背了。 2.2 定义简单的类(只包含方法)面向对象是更大的封装,在 一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!
定义一个只包含方法的类: class 类名:4 o4 \1 h/ L9 [3 R, z0 P1 L( `
: s6 p1 j6 D# x2 H8 S) i- _ def 方法1(self, 参数列表):
0 w' O( L. i# w" {5 V/ c* F pass
, C8 E5 A/ T3 ]' }& Q N1 B! d
' f8 q1 p' p5 ^ def 方法2(self, 参数列表):
) _" a1 r' H. n7 g% V pass方法 的定义格式和之前学习过的函数几乎一样,区别在于第一个参数必须是 self。注意:类名的 命名规则 要符合 大驼峰命名法。当一个类定义完成之后,要使用这个类来创建对象,语法格式如下: 对象变量 = 类名()在面向对象开发中,引用的概念是同样适用的!
使用 print输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)。 提示:在计算机中,通常使用 十六进制 表示 内存地址。
如果在开发中,希望使用 print输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__这个内置方法了: class Cat:
+ I( C- _" @4 n2 k) y1 @
) d" F I, u9 r; o2 g" q/ z4 n def __init__(self, new_name):) b% a. g4 i& O- Z* I
9 h. l: A ^8 Z* ~! [: m* V* V self.name = new_name
" O6 I# }7 g' _0 _3 V" h
# A" e' B" D2 ?! ` print("%s 来了" % self.name)
# v _0 t! H- Z3 P5 z; @9 V* ~, S: o7 B% M u- {
def __del__(self):9 M0 Z* z( o( C4 x1 R
" }0 |( e! `, R) m7 |& c- t. G print("%s 去了" % self.name)6 g5 Y& Z4 G/ s" ?4 U
1 F6 s2 \' y5 F u0 C def __str__(self):( w; r9 `0 j j) V
return "我是小猫:%s" % self.name4 {1 {& Z9 I% U2 h. r$ q
7 }7 F& o9 y$ q* h* ^1 b
tom = Cat("Tom")% s5 w! Y5 I) ]" J
print(tom)注意:__str__方法必须返回一个字符串。 2.3 方法中的 self 参数在 Python 中,要 给对象设置属性,非常的容易,只需要在 类的外部的代码 中直接通过 对象.设置一个属性即可,但是不推荐使用: class Cat:
7 h, ?: n: b# p9 a """这是一个猫类"""
$ R0 A e' r P( C: q0 K6 n0 D
+ e* j! x# o0 _7 D# Z7 d def eat(self):, Z5 N7 O% M: t3 {4 C
print("小猫爱吃鱼")
! z% @/ Y8 {: V
6 D1 {, B9 p6 H" _0 s* ?8 {: ~; h9 a def drink(self):
2 q5 I P i6 Q- ? print("小猫在喝水")
' @& D. Y/ O: m& @, s) ^
4 t8 [) A1 A, {" I* B, i. r: R( }tom = Cat()
0 {0 W, N. J( H0 [+ T1 O# 给对象设置属性; k3 B* g8 w* j5 X/ Z9 ]
tom.name = "Tom"因为:对象属性的封装应该封装在类的内部 由哪一个对象调用的方法,方法内的 self就是哪一个对象的引用
在类封装的方法内部,self 就表示当前调用方法的对象自己,在方法内部:
, `% |1 T+ ]* e# g& a可以通过 self.访问对象的属性,也可以通过 self.调用对象的其他方法。 调用方法时,程序员不需要传递 self 参数。 在 类的外部,通过变量名.访问对象的 属性和方法
, A0 _0 ]" Z& f- h+ s: j# j# |" g在 类封装的方法中,通过 self.访问对象的 属性和方法 + P% c- g+ T7 t( g# s
2.4 初始化方法:__init____init__ 方法是 专门 用来定义一个类具有哪些属性的方法!
在 __init__ 方法内部使用 self.属性名 = 属性的初始值 就可以定义属性,定义属性之后,再使用 类创建的对象,都会拥有该属性。 在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行 改造:把希望设置的属性值,定义成 __init__方法的参数 在方法内部使用 self.属性 = 形参 接收外部传递的参数 在创建对象时,使用 类名(属性1, 属性2...) 调用
8 T0 t) i0 I! t; B5 K4 S% A class Cat:
- C6 \7 z3 T- z( g D& e* H
+ Q# E& `% J1 g def __init__(self, name):) J9 T6 n! j4 d3 k# w: [
print("初始化方法 %s" % name)! E! a: p7 t0 q
self.name = name
1 I+ o$ B8 G% b+ j2 x6 A. C6 B ...
/ O& g G2 e! ?' y
; s; X3 u w8 }* L# ]tom = Cat("Tom")
: O; s q8 Q2 [$ h...
4 j3 O v& v, @" h2 n$ Y
& G* N8 l0 o+ V( t. m. Zlazy_cat = Cat("大懒猫")
~7 T& o: b: @9 v$ A' i( n...2.5 私有属性和私有方法应用场景 定义方式 8 a) \ t+ ^/ E. K
私有属性和私有方法 伪私有属性和私有方法Python 中,并没有 真正意义 的 私有在给 属性、方法 命名时,实际是对名称做了一些特殊处理,使得外界无法访问到处理方式:在 名称 前面加上_类名 => _类名__名称 # 私有属性,外部不能直接访问到
) b" E8 C3 v( w' Qprint(xiaofang._Women__age)
. G& p( e5 _2 n; l& G. C$ M D* Y5 H% s' P- I; I& ~* A
# 私有方法,外部不能直接调用6 c$ ]8 l' O7 z! c' H8 }
xiaofang._Women__secret()提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法。 3. 封装、继承和多态面向对象三大特性: 3.1 继承3.1.1 单继承继承的概念:子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法。 class 类名(父类名):
# P# c8 a2 t! {) H0 U! M2 `2 _6 x ~! I9 t' S$ c; q- p. Q) }
pass当 父类 的方法实现不能满足子类需求时,可以对方法进行重写(override)重写 父类方法有两种情况: 覆盖 父类的方法:父类的方法实现 和 子类的方法实现完全不同
& V$ ]7 Z! ]+ q- ]/ C I. ?具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现。 对父类方法进行 扩展:子类的方法实现 中 包含 父类的方法实现
. e# k* _$ i; X2 K$ d" u在子类中 重写 父类的方法;在需要的位置使用 super().父类方法 来调用父类方法的执行代码;其他的位置针对子类的需求,编写 子类特有的代码实现。 , ?/ T, i3 w3 |% _& {& N, f
关于 super在 Python 中 super 是一个 特殊的类 super()就是使用 super 类创建出来的对象 最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现 5 C: U% A# o" I: h3 p: s0 Y( q0 [
调用父类方法的另外一种方式:在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:父类名.方法(self)。目前在 Python 3.x 还支持这种方式,但不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改。 父类的 私有属性 和 私有方法子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问 私有属性、方法 通常用于做一些内部的事情 3.1.2 多继承子类 可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法,例如:孩子 会继承自己 父亲 和 母亲 的 特性。 class 子类名(父类名1, 父类名2...):
$ T# s7 z" I0 {& L passPython 中的 MRO算法(Method Resolution Order)如果 不同的父类 中存在 同名的方法,子类对象 在调用方法时,会调用 哪一个父类中的方法呢?
/ @. B7 c2 c- ^4 p' |* U提示:开发时,应该尽量避免这种容易产生混淆的情况!—— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免使用多继承。 Python 中针对 类 提供了一个 内置属性__mro__ 可以查看 方法 搜索顺序 在搜索方法时,是按照 mro 的输出结果 从左至右 的顺序查找的 如果在当前类中 找到方法,就直接执行,不再搜索 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索 如果找到最后一个类,还没有找到方法,程序报错
, S1 D% R+ y* M9 \, G
MRO 是 method resolution order —— 方法搜索顺序,主要用于 在多继承时判断 方法、属性 的调用 路径 新式类与旧式(经典)类新式类:以 object 为基类的类,推荐使用 经典类:不以 object为基类的类,不推荐使用 % X# w* @6 ~. K' D, s/ K
在 Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object作为该类的 基类 —— Python 3.x 中定义的类都是 新式类,在 Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为 基类。 class 类名(object):1 p0 H. t/ S4 W0 y: f, Y
passobject 是 Python 为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用 dir(object) 函数查看。 3.2 多态面向对象三大特性: 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 定义类的准则 继承 实现代码的重用,相同的代码不需要重复的编写 设计类的技巧 子类针对自己特有的需求,编写特定的代码 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果 增加代码的灵活度 以 继承 和 重写父类方法 为前提 调用方法的技巧,不会影响到类的内部设计
* w+ z8 t. W. {9 \! n. p# p
多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
案例:在 Dog 类中封装方法 game:普通狗只是简单的玩耍定义 XiaoTianDog 继承自 Dog,并且重写 game 方法:哮天犬需要在天上玩耍定义 Person 类,并且封装一个 和狗玩 的方法:在方法内部,直接让 狗对象 调用 game 方法
2 O, ?$ u' e1 ?) N1 j4 E
多态示例 Person 类中只需要让 狗对象 调用 game 方法,而不关心具体是 什么狗。
/ [: ^; E! k) j& y 4. 类属性和类方法4.1 类的结构通常会把:创建出来的 对象 叫做 类的实例创建对象的 动作 叫做 实例化对象的属性 叫做 实例属性对象调用的方法 叫做 实例方法每一个对象 都有自己独立的内存空间,保存各自不同的属性多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部 0 A$ v% n3 v4 Y9 y" X8 [+ ^
各个不同的属性,独一份的方法 在 Python 中,类是一个特殊的对象。 Python 中 一切皆对象: class AAA: 定义的类属于 类对象 obj1 = AAA() 属于 实例对象
在程序运行时,类同样会被加载到内存在程序运行时,类对象在内存中只有一份,使用 一个类可以创建出很多个对象实例除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法——类属性、类方法,通过 类名. 的方式可以 访问类的属性 或者 调用类的方法
6 v" r6 g2 H" r" m4 V8 G' t) ~
类的结构 4.2 类属性和实例属性类属性 就是 类对象中定义的属性通常用来记录与这个类相关的特征类属性不会用于记录具体对象的特征示例:定义一个 工具类,每件工具都有自己的 name:需求 —— 知道使用这个类,创建了多少个工具对象? # Y3 T6 z+ B% Y
: S/ @, w; S3 f4 K
$ w! C9 f* x, m$ @( ^, Y* h
属性的获取机制在 Python 中 属性的获取 存在一个 向上查找机制
+ K3 o- @* ~; O9 ^+ P4 C K
6 R- b7 x2 U' ^, b) E& z
因此,要访问类属性有两种方式: 4.3 类方法和静态方法4.3.1 类方法语法如下 @classmethod0 Q4 D. f6 }7 e. W8 H4 Z* l
def 类方法名(cls):+ J: G% w, E& K) k
pass类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法 类方法的 第一个参数 应该是 cls 由 哪一个类 调用的方法,方法内的 cls 就是 哪一个类的引用 这个参数和 实例方法 的第一个参数是 self 类似 提示 使用其他名称也可以,不过习惯使用 cls 通过 类名. 调用 类方法,调用方法时,不需要传递 cls 参数 在方法内部 可以通过 cls. 访问类的属性 也可以通过 cls. 调用其他的类方法 0 k' _9 N- T& |
示例 @classmethod
/ A1 z6 l1 F2 Ldef show_tool_count(cls):1 i" o8 E; H1 ?% L# N
"""显示工具对象的总数"""
+ T/ s+ G8 X2 b print("工具对象的总数 %d" % cls.count)4.3.2 静态方法语法如下 @staticmethod
0 ^. P+ s$ o3 `3 Idef 静态方法名():
4 b. y3 k9 l& [+ R9 L! s- k3 v pass示例: 7 G- Q1 U+ b) D x& A
R) R b8 h+ t0 ~9 e
) V& F& `2 x% B2 i4 s
探索: 5. 单例5.1 单例设计模式5.2 静态方法:__new__使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间 __new__ 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个:在内存中为对象 分配空间 返回 对象的引用 Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__ 方法
1 g1 |9 p. L8 T( s
重写 __new__ 方法 的代码非常固定!
& [9 }( c1 r* P$ f, S$ q" l
/ v/ p) U |8 E; Y' o
) a& r! P9 n" b" k3 J s0 T( i
: I9 j# C8 \6 B5 m) f K6 w6 \% l
5.3 Python 中的单例
6 q# n, S+ \: i. O4 ]: u* y6 M
7 k$ _0 o( ]3 S& A @
{% [+ y1 }" ~+ T3 F; L) A
. _% L! n3 J& _- p
只执行一次初始化工作需求 让 初始化动作 只被 执行一次 & D5 e' ]( b! V( D2 { T! T8 a# g7 {
解决办法 定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False 在 __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作 然后将 init_flag 设置为 True 这样,再次 自动 调用 __init__ 方法时,初始化动作就不会被再次执行 了
/ m9 E% z |5 p" Q1 `: H
6 \, _2 c/ n1 ]) N
5 C3 Z$ @* J( J' A% n% C1 L
" |! y1 |5 t# @" R' j9 Z Tips1、Python 能够自动的将一对括号内部的代码连接在一起: '''& P6 `0 B2 P L& n( [' Q
**需求**
0 `( D! F2 N8 w- `- I: G; Z q% m" a# ` r
* 定义 `input_password` 函数,提示用户输入密码% _! T; O; r0 P& b; p
* 如果用户输入长度 < 8,抛出异常: Q, N+ u# r! g7 [8 A
* 如果用户输入长度 >=8,返回输入的密码* P; M Y0 d/ p; m8 g p
'''
& e8 j1 G' J1 s% Y! sdef input_password():
" ]- W. g3 l7 }$ F5 w( g! e' L; c- f
# 1\. 提示用户输入密码
) W* T6 c8 J; X8 g" S( W pwd = input("请输入密码:")" l, y1 |. N& U5 `& a
5 P$ E# c! e: Q
# 2\. 判断密码长度,如果长度 >= 8,返回用户输入的密码
. r7 {3 t% J2 C6 i# C if len(pwd) >= 8:
+ C; S% v7 n# }, E- G return pwd9 l# y( c9 k& }' m" d7 L- K" P8 G+ ^
8 @4 t `9 C$ I, _( x # 3\. 密码长度不够,需要抛出异常
) X- j" J; H0 m4 k/ @% e # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数$ P" S- f. @# c7 k
ex = Exception("密码长度不够")1 L% a- n! @# w/ |/ N
" C! Y6 t. l, q* X # 2> 抛出异常对象' m' {( C& t+ ], Z
raise ex
: d8 v6 e8 Y5 L
6 Z: c$ M! \! _6 y! C* s+ otry:
7 s. L2 L9 ?) V. L# G! H7 b user_pwd = input_password()' y" p* U& _, M, L; }* k$ G
print(user_pwd)+ {% i8 @; o) X* Y' p1 [- c! h
except Exception as result:
+ Y6 l* i5 ?! Z% c* g7 O print("发现错误:%s" % result)2、一个对象的 属性 可以是 另外一个类创建的对象。3、在__init__方法中定义类的属性时,如果 不知道设置什么初始值,可以设置为 None):None 关键字 表示 什么都没有,表示一个 空对象,没有方法和属性,是一个特殊的常量。可以将 None 赋值给任何一个变量。 在 Python 中针对 None 比较时,建议使用is 判断
4、eval() 函数十分强大 —— 将字符串 当成 有效的表达式 来求值 并 返回计算结果 ; ^8 X- V9 ?5 D8 U$ `, g; y* h
, f. L/ [1 F9 O ]+ j0 [4 P" w7 L" z0 ^
. O0 J* N% q7 G( a. r2 z# s
在开发时千万不要使用 eval 直接转换 input 的结果,举个例子: __import__('os').system('ls')# 等价代码import osos.system("终端命令")
- E1 t$ L( N; `1 c! ~3 d. F% e |