List
(列表) 是 Python
中使用 最频繁 的数据类型,在其他语言中通常叫做 数组[]
定义,数据 之间使用 ,
分隔0
开始
注意:从列表中取值时,如果 超出索引范围,程序会报错
name_list = ["zhangsan", "lisi", "wangwu"]
列表可以使用的方法如下:
In [1]: name_list.
name_list.append name_list.count name_list.insert name_list.reverse
name_list.clear name_list.extend name_list.pop name_list.sort
name_list.copy name_list.index name_list.remove
序号 | 分类 | 关键字 / 函数 / 方法 | 说明 |
---|---|---|---|
1 | 增加 | 列表.insert(索引, 数据) | 在指定位置插入数据 |
列表.append(数据) | 在末尾追加数据 | ||
列表.extend(列表2) | 将列表2 的数据追加到列表 | ||
2 | 修改 | 列表[索引] = 数据 | 修改指定索引的数据 |
3 | 删除 | del 列表[索引] | 删除指定索引的数据 |
列表.remove(数据) | 删除第一个出现的指定数据 | ||
列表.pop() | 删除末尾数据 | ||
列表.pop(索引) | 删除指定索引数据 | ||
列表.clear | 清空列表 | ||
4 | 统计 | len(列表) | 列表长度 |
列表.count(数据) | 数据在列表中出现的次数 | ||
5 | 排序 | 列表.sort() | 升序排序 |
列表.sort(reverse=True) | 降序排序 | ||
列表.reverse() | 逆序、反转 |
del
关键字(delete
) 同样可以删除列表中元素del
关键字本质上是用来 将一个变量从内存中删除的del
关键字将变量从内存中删除,后续的代码就不能再使用这个变量了del name_list[1]
在日常开发中,要从列表删除数据,建议 使用列表提供的方法
遍历 就是 从头到尾 依次 从 列表 中获取数据
在 Python
中为了提高列表的遍历效率,专门提供的 迭代 iteration 遍历
使用 for
就能够实现迭代遍历
# for 循环内部使用的变量 in 列表
for name in name_list:
循环内部针对列表元素进行操作
print(name)
Python
的 列表 中可以 存储不同类型的数据Tuple
(元组)与列表类似,不同之处在于元组的 元素不能修改
Python
开发中,有特定的应用场景,
分隔()
定义0
开始
info_tuple = ("zhangsan", 18, 1.75)
info_tuple = ()
具体原因看另一篇博客
info.count info.index
# for 循环内部使用的变量 in 元组
for item in info:
循环内部针对元组元素进行操作
print(item)
- 在
Python
中,可以使用for
循环遍历所有非数字型类型的变量:列表、元组、字典 以及 字符串- 提示:在实际开发中,除非 能够确认元组中的数据类型,否则针对元组的循环遍历需求并不是很多
for in
遍历 元组()
本质上就是一个元组info = ("zhangsan", 18)
print("%s 的年龄是 %d" % info)
list
函数可以把元组转换成列表list(元组)
tuple
函数可以把列表转换成元组tuple(列表)
dictionary
(字典) 是 除列表以外 Python
之中 最灵活 的数据类型物体
的相关信息{}
定义,
分隔
key
是索引value
是数据:
分隔xiaoming = {"name": "小明",
"age": 18,
"gender": True,
"height": 1.75}
字典 能够使用的函数如下:
In [1]: xiaoming.
xiaoming.clear xiaoming.items xiaoming.setdefault
xiaoming.copy xiaoming.keys xiaoming.update
xiaoming.fromkeys xiaoming.pop xiaoming.values
xiaoming.get xiaoming.popitem
# for 循环内部使用的 `key 的变量` in 字典
for k in xiaoming:
print("%s: %s" % (k, xiaoming[k]))
提示:在实际开发中,由于字典中每一个键值对保存数据的类型是不同的,所以针对字典的循环遍历需求并不是很多
for in
遍历 字典物体
的相关信息 —— 描述更复杂的数据信息card_list = [{"name": "张三",
"qq": "12345",
"phone": "110"},
{"name": "李四",
"qq": "54321",
"phone": "10086"}
]
"
或者 一对单引号 '
定义一个字符串
\"
或者 \'
做字符串的转义,但是在实际开发中:
"
,可以使用 '
定义字符串'
,可以使用 "
定义字符串for
循环遍历 字符串中每一个字符大多数编程语言都是用
"
来定义字符串
string = "Hello Python"
for c in string:
print(c)
字符串 能够使用的 方法 如下:
In [1]: hello_str.
hello_str.capitalize hello_str.isidentifier hello_str.rindex
hello_str.casefold hello_str.islower hello_str.rjust
hello_str.center hello_str.isnumeric hello_str.rpartition
hello_str.count hello_str.isprintable hello_str.rsplit
hello_str.encode hello_str.isspace hello_str.rstrip
hello_str.endswith hello_str.istitle hello_str.split
hello_str.expandtabs hello_str.isupper hello_str.splitlines
hello_str.find hello_str.join hello_str.startswith
hello_str.format hello_str.ljust hello_str.strip
hello_str.format_map hello_str.lower hello_str.swapcase
hello_str.index hello_str.lstrip hello_str.title
hello_str.isalnum hello_str.maketrans hello_str.translate
hello_str.isalpha hello_str.partition hello_str.upper
hello_str.isdecimal hello_str.replace hello_str.zfill
hello_str.isdigit hello_str.rfind
提示:正是因为 python 内置提供的方法足够多,才使得在开发时,能够针对字符串进行更加灵活的操作!应对更多的开发需求!
方法 | 说明 |
---|---|
string.isspace() | 如果 string 中只包含空格,则返回 True |
string.isalnum() | 如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True |
string.isalpha() | 如果 string 至少有一个字符并且所有字符都是字母则返回 True |
string.isdecimal() | 如果 string 只包含数字则返回 True,全角数字 |
string.isdigit() | 如果 string 只包含数字则返回 True,全角数字 、⑴ 、\u00b2 |
string.isnumeric() | 如果 string 只包含数字则返回 True,全角数字 ,汉字数字 |
string.istitle() | 如果 string 是标题化的(每个单词的首字母大写)则返回 True |
string.islower() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True |
string.isupper() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True |
方法 | 说明 |
---|---|
string.startswith(str) | 检查字符串是否是以 str 开头,是则返回 True |
string.endswith(str) | 检查字符串是否是以 str 结束,是则返回 True |
string.find(str, start=0, end=len(string)) | 检测 str 是否包含在 string 中,如果 start 和 end 指定范围,则检查是否包含在指定范围内,如果是返回开始的索引值,否则返回 -1 |
string.rfind(str, start=0, end=len(string)) | 类似于 find(),不过是从右边开始查找 |
string.index(str, start=0, end=len(string)) | 跟 find() 方法类似,不过如果 str 不在 string 会报错 |
string.rindex(str, start=0, end=len(string)) | 类似于 index(),不过是从右边开始 |
string.replace(old_str, new_str, num=string.count(old)) | 把 string 中的 old_str 替换成 new_str,如果 num 指定,则替换不超过 num 次 |
方法 | 说明 |
---|---|
string.capitalize() | 把字符串的第一个字符大写 |
string.title() | 把字符串的每个单词首字母大写 |
string.lower() | 转换 string 中所有大写字符为小写 |
string.upper() | 转换 string 中的小写字母为大写 |
string.swapcase() | 翻转 string 中的大小写 |
方法 | 说明 |
---|---|
string.ljust(width) | 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串 |
string.rjust(width) | 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串 |
string.center(width) | 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串 |
方法 | 说明 |
---|---|
string.lstrip() | 截掉 string 左边(开始)的空白字符 |
string.rstrip() | 截掉 string 右边(末尾)的空白字符 |
string.strip() | 截掉 string 左右两边的空白字符 |
方法 | 说明 |
---|---|
string.partition(str) | 把字符串 string 分成一个 3 元素的元组 (str前面, str, str后面) |
string.rpartition(str) | 类似于 partition() 方法,不过是从右边开始查找 |
string.split(str="", num) | 以 str 为分隔符拆分 string,如果 num 有指定值,则仅分隔 num + 1 个子字符串,str 默认包含 ‘\r’, ‘\t’, ‘\n’ 和空格 |
string.splitlines() | 按照行(’\r’, ‘\n’, ‘\r\n’)分隔,返回一个包含各行作为元素的列表 |
string.join(seq) | 以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串 |
字符串[开始索引:结束索引:步长]
注意:
[开始索引, 结束索引)
=> 开始索引 >= 范围 < 结束索引
起始
位开始,到 结束
位的前一位 结束(不包含结束位本身)1
,如果连续切片,数字和冒号都可以省略演练需求
末尾
的字符串开始
~ 5 位置 的字符串末尾 - 1
的字符串答案
num_str = "0123456789"
# 1. 截取从 2 ~ 5 位置 的字符串
print(num_str[2:6])
# 2. 截取从 2 ~ `末尾` 的字符串
print(num_str[2:])
# 3. 截取从 `开始` ~ 5 位置 的字符串
print(num_str[:6])
# 4. 截取完整的字符串
print(num_str[:])
# 5. 从开始位置,每隔一个字符截取字符串
print(num_str[::2])
# 6. 从索引 1 开始,每隔一个取一个
print(num_str[1::2])
# 倒序切片
# -1 表示倒数第一个字符
print(num_str[-1])
# 7. 截取从 2 ~ `末尾 - 1` 的字符串
print(num_str[2:-1])
# 8. 截取字符串末尾两个字符
print(num_str[-2:])
# 9. 字符串的逆序(面试题)
print(num_str[::-1])
Python 包含了以下内置函数:
函数 | 描述 | 备注 |
---|---|---|
len(item) | 计算容器中元素个数 | |
del(item) | 删除变量 | del 有两种方式 |
max(item) | 返回容器中元素最大值 | 如果是字典,只针对 key 比较 |
min(item) | 返回容器中元素最小值 | 如果是字典,只针对 key 比较 |
cmp(item1, item2) | 比较两个值,-1 小于/0 相等/1 大于 | Python 3.x 取消了 cmp 函数 |
注意
描述 | Python 表达式 | 结果 | 支持的数据类型 |
---|---|---|---|
切片 | “0123456789”[::-2] | “97531” | 字符串、列表、元组 |
运算符 | Python 表达式 | 结果 | 描述 | 支持的数据类型 |
---|---|---|---|---|
+ | [1, 2] + [3, 4] | [1, 2, 3, 4] | 合并 | 字符串、列表、元组 |
* | [“Hi!”] * 4 | [‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’] | 重复 | 字符串、列表、元组 |
in | 3 in (1, 2, 3) | True | 元素是否存在 | 字符串、列表、元组、字典 |
not in | 4 not in (1, 2, 3) | True | 元素是否不存在 | 字符串、列表、元组、字典 |
> >= == < <= | (1, 2, 3) < (2, 2, 3) | True | 元素比较 | 字符串、列表、元组 |
注意
in
在对 字典 操作时,判断的是 字典的键in
和 not in
被称为 成员运算符成员运算符用于 测试 序列中是否包含指定的 成员
运算符 | 描述 | 实例 |
---|---|---|
in | 如果在指定的序列中找到值返回 True,否则返回 False | 3 in (1, 2, 3) 返回 True |
not in | 如果在指定的序列中没有找到值返回 True,否则返回 False | 3 not in (1, 2, 3) 返回 False |
注意:在对 字典 操作时,判断的是 字典的键
Python
中完整的 for 循环
的语法如下:for 变量 in 集合:
循环体代码
else:
没有通过 break 退出循环,**循环结束后,会执行的代码**所以加了break说明上面的没有全部便利,下面的就不会被执行
students = [
{"name": "阿土",
"age": 20,
"gender": True,
"height": 1.7,
"weight": 75.0},
{"name": "小美",
"age": 19,
"gender": False,
"height": 1.6,
"weight": 45.0},
]
find_name = "阿土"
for stu_dict in students:
print(stu_dict)
# 判断当前遍历的字典中姓名是否为find_name
if stu_dict["name"] == find_name:
print("找到了")
# 如果已经找到,直接退出循环,就不需要再对后续的数据进行比较
break
else:
print("没有找到")
print("循环结束")
which
查询 python3
解释器所在路径$ which python3
#! /usr/bin/python3
- 变量 和 数据 都是保存在 内存 中的
- 在
Python
中 函数 的 参数传递 以及 返回值 都是靠 引用 传递的
在 Python
中
id()
函数可以查看变量中保存数据所在的 内存地址注意:如果变量已经被定义,当给一个变量赋值的时候,本质上是 修改了数据的引用
- 变量 不再 对之前的数据引用
- 变量 改为 对新赋值的数据引用
变量引用
的示例在 Python
中,变量的名字类似于 便签纸 贴在 数据 上
在 Python
中,函数的 实参/返回值 都是是靠 引用 来传递来的,也就是说调用函数时,调用的参数/返回的数据都用的是数据的引用而不是数据本身
不可变类型,内存中的数据不允许被修改:
int
, bool
, float
, complex
, long(2.x)
str
tuple
可变类型,内存中的数据可以被修改:
list
dict
注意:字典的 key
只能使用不可变类型的数据
注意
(hash)
Python
中内置有一个名字叫做 hash(o)
的函数
哈希
是一种 算法,其作用就是提取数据的 特征码(指纹)
Python
中,设置字典的 键值对 时,会首先对 key
进行 hash
已决定如何在内存中保存字典的数据,以方便 后续 对字典的操作:增、删、改、查
key
必须是不可变类型数据value
可以是任意类型的数据提示:在其他的开发语言中,大多 不推荐使用全局变量 —— 可变范围太大,导致程序不好维护!
注意:函数执行时,需要处理变量时 会:
全局变量的引用
提示:在其他的开发语言中,大多 不推荐使用全局变量 —— 可变范围太大,导致程序不好维护!
注意:只是在函数内部定义了一个局部变量而已,只是变量名相同 —— 在函数内部不能直接修改全局变量的值
global
进行声明,这个就可以在函数内部修改全局变量的名字a = 10
def demo():
print("%d" % a)
print("%d" % b)
print("%d" % c)
b = 20
demo()
c = 30
注意
如果局部变量和全局变量的名字相同,在局部变量下面会有一个虚线
g_
或者 gl_
的前缀提示:具体的要求格式,各公司要求可能会有些差异
函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形式
定义函数时,是否接收参数,或者是否返回结果,是根据 实际的功能需求 来决定的!
return
关键字可以返回结果问题:一个函数执行后能否返回多个结果?
提示:如果一个函数返回的是元组,括号可以省略
技巧
Python
中,可以 将一个元组 使用 赋值语句 同时赋值给 多个变量result = temp, wetness = measure()
题目要求
a = 6
, b = 100
# 解法 1 - 使用临时变量
c = b
b = a
a = c
# 解法 2 - 不使用临时变量
a = a + b
b = a - b
a = a - b
a, b = b, a
问题 1:在函数内部,针对参数使用 赋值语句,会不会影响调用函数时传递的 实参变量? —— 不会!
问题 2:如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据
+=
python
中,列表变量调用 +=
本质上是在执行列表变量的 extend
方法,不会修改变量的引用,就会改变全局变量def print_info(name, gender=True):
gender_text = "男生"
if not gender:
gender_text = "女生"
print("%s 是 %s" % (name, gender_text))
提示
def print_info(name, gender=True, title):
def print_info(name, title="", gender=True)
有时可能需要 一个函数 能够处理的参数 个数 是不确定的,这个时候,就可以使用 多值参数
python
中有 两种 多值参数:
*
可以接收 元组*
可以接收 字典一般在给多值参数命名时,习惯使用以下两个名字
*args
—— 存放 元组 参数,前面有一个 *
**kwargs
—— 存放 字典 参数,前面有两个 *
args
是 arguments
的缩写,有变量的含义
kw
是 keyword
的缩写,kwargs
可以记忆 键值对参数
def demo(num, *args, **kwargs):
print(num)
print(args)
print(kwargs)
demo(1, 2, 3, 4, 5, name="小明", age=18, gender=True)
提示:多值参数 的应用会经常出现在网络上一些大牛开发的框架中,知道多值参数,有利于我们能够读懂大牛的代码
args
kwargs
*
*
def demo(*args, **kwargs):
print(args)
print(kwargs)
# **需要将一个元组变量/字典变量传递给函数对应的参数**
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}
# 会把 num_tuple 和 xiaoming 作为元组传递个 args
# demo(gl_nums, gl_xiaoming)
demo(*gl_nums, **gl_xiaoming)
函数调用自身的 编程技巧 称为递归
特点
代码特点
示例代码
def sum_numbers(num):
print(num)
# 递归的出口很重要,否则会出现死循环
if num == 1:
return
sum_numbers(num - 1)
sum_numbers(3)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ufqMa578-1591928644331)(media/14993074876434/002_%E9%80%92%E5%BD%92%E8%B0%83%E7%94%A8%E7%A4%BA%E6%84%8F%E5%9B%BEI.png)]
需求
sum_numbers
num
的整数参数def sum_numbers(num):
if num == 1:
return 1
# 假设 sum_numbers 能够完成 num - 1 的累加
temp = sum_numbers(num - 1)
# 函数内部的核心算法就是 两个数字的相加
return num + temp
print(sum_numbers(2))
提示:递归是一个 编程技巧,初次接触递归会感觉有些吃力!在处理 不确定的循环条件时,格外的有用,例如:遍历整个文件目录的结构
面向对象编程 —— Object Oriented Programming
简写 OOP
所有步骤
从头到尾
逐步实现特点
相比较函数,面向对象 是 更大 的 封装,根据 职责 在 一个对象中 封装 多个方法
特点
类 和 对象 是 面向对象编程的 两个 核心概念
在程序开发中,应该 先有类,再有对象
在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类!
在程序开发中,要设计一个类,通常需要满足一下三个要素:
CapWords
名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类
提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑
需求
dir
内置函数self
参数dir
内置函数(知道)Python
中 对象几乎是无所不在的,我们之前学习的 变量、数据、函数 都是对象在 Python
中可以使用以下两个方法验证:
提示 __方法名__
格式的方法是 Python
提供的 内置方法 / 属性,稍后会给大家介绍一些常用的 内置方法 / 属性
序号 | 方法名 | 类型 | 作用 |
---|---|---|---|
01 | __new__ |
方法 | 创建对象时,会被 自动 调用 |
02 | __init__ |
方法 | 对象被初始化时,会被 自动 调用 |
03 | __del__ |
方法 | 对象被从内存中销毁前,会被 自动 调用 |
04 | __str__ |
方法 | 返回对象的描述信息,print 函数输出使用 |
提示 利用好 dir()
函数,在学习时很多内容就不需要死记硬背了
面向对象 是 更大 的 封装,在 一个类中 封装 多个方法,这样 通过这个类创建出来的对象,就可以直接调用这些方法了!
Python
中要定义一个只包含方法的类,语法格式如下:class 类名:
def 方法1(self, 参数列表):
pass
def 方法2(self, 参数列表):
pass
self
,大家暂时先记住,稍后介绍 self
注意:类名 的 命名规则 要符合 大驼峰命名法
对象变量 = 类名()
需求
分析
Cat
eat
和 drink
class Cat:
"""这是一个猫类"""
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫在喝水")
tom = Cat()
tom.drink()
tom.eat()
在面向对象开发中,引用的概念是同样适用的!
Python
中使用类 创建对象之后,tom
变量中 仍然记录的是 对象在内存中的地址tom
变量 引用 了 新建的猫对象print
输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)提示:在计算机中,通常使用 十六进制 表示 内存地址
- 十进制 和 十六进制 都是用来表达数字的,只是表示的方式不一样
- 十进制 和 十六进制 的数字之间可以来回转换
%d
可以以 10 进制 输出数字%x
可以以 16 进制 输出数字lazy_cat = Cat()
lazy_cat.eat()
lazy_cat.drink()
提问:
tom
和lazy_cat
是同一个对象吗? 不是!
self
参数Python
中,要 给对象设置属性,非常的容易,但是不推荐使用
.
设置一个属性即可注意:这种方式虽然简单,但是不推荐使用!
tom.name = "Tom"
...
lazy_cat.name = "大懒猫"
self
在方法内部输出每一只猫的名字由 哪一个对象 调用的方法,方法内的
self
就是 哪一个对象的引用
在类封装的方法内部,self
就表示 当前调用方法的对象自己
调用方法时,程序员不需要传递 self
参数
在方法内部
self.
访问对象的属性self.
调用其他的对象方法在 类的外部,通过 变量名.
访问对象的 属性和方法
在 类封装的方法中,通过 self.
访问对象的 属性和方法
tom = Cat()
tom.drink()
tom.eat()
tom.name = "Tom"
print(tom)
AttributeError: 'Cat' object has no attribute 'name'
属性错误:'Cat' 对象没有 'name' 属性
提示
类名()
创建对象时,会 自动 执行以下操作:
init
)__init__
方法,__init__
是对象的内置方法
__init__
方法是 专门 用来定义一个类 具有哪些属性的方法!
在 Cat
中增加 __init__
方法,验证该方法在创建对象时会被自动调用
class Cat:
"""这是一个猫类"""
def __init__(self):
print("初始化方法")
__init__
方法内部使用 self.属性名 = 属性的初始值
就可以 定义属性Cat
类创建的对象,都会拥有该属性class Cat:
def __init__(self):
print("这是一个初始化方法")
# 定义用 Cat 类创建的猫对象都有一个 name 的属性
self.name = "Tom"
def eat(self):
print("%s 爱吃鱼" % self.name)
# 使用类名()创建对象的时候,会自动调用初始化方法 __init__
tom = Cat()
tom.eat()
__init__
方法进行 改造
__init__
方法的参数self.属性 = 形参
接收外部传递的参数类名(属性1, 属性2...)
调用class Cat:
def __init__(self, name):
print("初始化方法 %s" % name)
self.name = name
...
tom = Cat("Tom")
...
lazy_cat = Cat("大懒猫")
...
序号 | 方法名 | 类型 | 作用 |
---|---|---|---|
01 | __del__ |
方法 | 对象被从内存中销毁前,会被 自动 调用 |
02 | __str__ |
方法 | 返回对象的描述信息,print 函数输出使用 |
__del__
方法(知道)在 Python
中
类名()
创建对象时,为对象 分配完空间后,自动 调用 __init__
方法__del__
方法应用场景
__init__
改造初始化方法,可以让创建对象更加灵活__del__
如果希望在对象被销毁前,再做一些事情,可以考虑一下 __del__
方法生命周期
类名()
创建,生命周期开始__del__
方法一旦被调用,生命周期结束class Cat:
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 去了" % self.name)
# tom 是一个全局变量
tom = Cat("Tom")
print(tom.name)
# del 关键字可以删除一个对象
del tom
print("-" * 50)
__str__
方法Python
中,使用 print
输出 对象变量,默认情况下,会输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)print
输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__
这个内置方法了注意:
__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)
身份运算符用于 比较 两个对象的 内存地址 是否一致 —— 是否是对同一个对象的引用
Python
中针对 None
比较时,建议使用 is
判断运算符 | 描述 | 实例 |
---|---|---|
is | is 是判断两个标识符是不是引用同一个对象 | x is y,类似 id(x) == id(y) |
is not | is not 是判断两个标识符是不是引用不同对象 | x is not y,类似 id(a) != id(b) |
is
用于判断 两个变量 引用对象是否为同一个
==
用于判断 引用变量的值 是否相等
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> b is a
False
>>> b == a
True
应用场景
定义方式
class Women:
def __init__(self, name):
self.name = name
# 不要问女生的年龄
self.__age = 18
def __secret(self):
print("我的年龄是 %d" % self.__age)
xiaofang = Women("小芳")
# 私有属性,外部不能直接访问
# print(xiaofang.__age)
# 私有方法,外部不能直接调用
# xiaofang.__secret()
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法
Python
中,并没有 真正意义 的 私有
_类名
=> _类名__名称
# 私有属性,外部不能直接访问到
print(xiaofang._Women__age)
# 私有方法,外部不能直接调用
xiaofang._Women__secret()
__new__
方法设计模式
单例设计模式
类名()
返回的对象,内存地址是相同的__new__
方法Python
的解释器 首先 会 调用 __new__
方法为对象 分配空间__new__
是一个 由 object
基类提供的 内置的静态方法,主要作用有两个:
Python
的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__
方法重写
__new__
方法 的代码非常固定!
__new__
方法 一定要 return super().__new__(cls)
__new__
是一个静态方法,在调用时需要 主动传递 cls
参数示例代码
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
# 如果不返回任何结果,
return super().__new__(cls)
def __init__(self):
print("初始化音乐播放对象")
player = MusicPlayer()
print(player)
None
,用于记录 单例对象的引用__new__
方法is None
,调用父类方法分配空间,并在类属性中记录结果class MusicPlayer(object):
# 定义类属性记录单例对象引用
instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否已经被赋值
if cls.instance is None:
cls.instance = super().__new__(cls)
# 2. 返回类属性的单例引用
return cls.instance
类名()
创建对象时,Python
的解释器都会自动调用两个方法:
__new__
分配空间__init__
对象初始化__new__
方法改造之后,每次都会得到 第一次被创建对象的引用需求
解决办法
init_flag
标记是否 执行过初始化动作,初始值为 False
__init__
方法中,判断 init_flag
,如果为 False
就执行初始化动作init_flag
设置为 True
__init__
方法时,初始化动作就不会被再次执行 了class MusicPlayer(object):
# 记录第一个被创建对象的引用
instance = None
# 记录是否执行过初始化动作
init_flag = False
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否是空对象
if cls.instance is None:
# 2. 调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3. 返回类属性保存的对象引用
return cls.instance
def __init__(self):
if not MusicPlayer.init_flag:
print("初始化音乐播放器")
MusicPlayer.init_flag = True
# 创建多个对象
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
面向对象三大特性
需求
Dog
类中封装方法 game
XiaoTianDog
继承自 Dog
,并且重写 game
方法
Person
类,并且封装一个 和狗玩 的方法
game
方法案例小结
Person
类中只需要让 狗对象 调用 game
方法,而不关心具体是 什么狗
game
方法是在 Dog
父类中定义的多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
class Dog(object):
def __init__(self, name):
self.name = name
def game(self):
print("%s 蹦蹦跳跳的玩耍..." % self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s 飞到天上去玩耍..." % self.name)
class Person(object):
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
# 让狗玩耍
dog.game()
# 1. 创建一个狗对象
# wangcai = Dog("旺财")
wangcai = XiaoTianDog("飞天旺财")
# 2. 创建一个小明对象
xiaoming = Person("小明")
# 3. 让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai)
面向对象三大特性
继承的概念:子类 拥有 父类 的所有 方法 和 属性
class 类名(父类名):
pass
Dog
类是 Animal
类的子类,Animal
类是 Dog
类的父类,Dog
类从 Animal
类继承Dog
类是 Animal
类的派生类,Animal
类是 Dog
类的基类,Dog
类从 Animal
类派生C
类从 B
类继承,B
类又从 A
类继承C
类就具有 B
类和 A
类的所有属性和方法子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法
应用场景
重写 父类方法有两种情况:
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现
重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法
super().父类方法
来调用父类方法的执行super
Python
中 super
是一个 特殊的类super()
就是使用 super
类创建出来的对象
- 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
- 私有属性、方法 通常用于做一些内部的事情
概念
语法
class 子类名(父类名1, 父类名2...)
pass
问题的提出
提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免 使用多继承
Python
中针对 类 提供了一个 内置属性 __mro__
可以查看 方法 搜索顺序method resolution order
,主要用于 在多继承时判断 方法、属性 的调用 路径print(C.__mro__)
输出结果
(, , , )
__mro__
的输出结果 从左至右 的顺序查找的
object
是Python
为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用dir
函数查看
新式类:以 object
为基类的类,推荐使用
经典类:不以 object
为基类的类,不推荐使用
在 Python 3.x
中定义类时,如果没有指定父类,会 默认使用 object
作为该类的 基类 —— Python 3.x
中定义的类都是 新式类
在 Python 2.x
中定义类时,如果没有指定父类,则不会以 object
作为 基类
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
为了保证编写的代码能够同时在 Python 2.x
和 Python 3.x
运行!
今后在定义类时,如果没有父类,建议统一继承自 object
class 类名(object):
pass
__init__
为 对象初始化因此,通常也会把:
在程序执行时:
self.
结论
Python
中 一切皆对象:
class AAA:
定义的类属于 类对象obj1 = AAA()
属于 实例对象
Python
中,类 是一个特殊的对象 —— 类对象示例需求
name
class Tool(object):
# 使用赋值语句,定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 针对类属性做一个计数+1
Tool.count += 1
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")
# 知道使用 Tool 类到底创建了多少个对象?
print("现在创建了 %d 个工具" % Tool.count)
注意
对象.类属性 = 值
赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值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)
在类方法内部,可以直接使用
cls
访问 类属性 或者 调用类方法
在开发时,如果需要在 类 中封装一个方法,这个方法:
这个时候,可以把这个方法封装成一个 静态方法
语法如下
@staticmethod
def 静态方法名():
pass
@staticmethod
来标识,告诉解释器这是一个静态方法class Dog(object):
# 狗对象计数
dog_count = 0
@staticmethod
def run():
# 不需要访问实例属性也不需要访问类属性的方法
print("狗在跑...")
def __init__(self, name):
self.name = name
需求
Game
类top_score
记录游戏的 历史最高分player_name
记录 当前游戏的玩家姓名show_help
显示游戏帮助信息show_top_score
显示历史最高分start_game
开始当前玩家的游戏提问
如果方法内部 即需要访问 实例属性,又需要访问 类属性,应该定义成什么方法?
答案
class Game(object):
# 游戏最高分,类属性
top_score = 0
@staticmethod
def show_help():
print("帮助信息:让僵尸走进房间")
@classmethod
def show_top_score(cls):
print("游戏最高分是 %d" % cls.top_score)
def __init__(self, player_name):
self.player_name = player_name
def start_game(self):
print("[%s] 开始游戏..." % self.player_name)
# 使用类名.修改历史最高分
Game.top_score = 999
# 1. 查看游戏帮助
Game.show_help()
# 2. 查看游戏最高分
Game.show_top_score()
# 3. 创建游戏对象,开始游戏
game = Game("小明")
game.start_game()
# 4. 游戏结束,查看游戏最高分
Game.show_top_score()
Python 解释器
遇到 到一个错误,会停止程序的执行,并且提示一些错误信息,这就是 异常程序开发时,很难将 所有的特殊情况 都处理的面面俱到,通过 异常捕获 可以针对突发事件做集中的处理,从而保证程序的 稳定性和健壮性
try(尝试)
来 捕获异常try:
尝试执行的代码
except:
出现错误的处理
try
尝试,下方编写要尝试代码,不确定是否能够正常执行的代码except
如果不是,下方编写尝试失败的代码try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except:
print("请输入正确的数字")
在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
语法如下:
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except (错误类型2, 错误类型3):
# 针对错误类型2 和 3,对应的代码处理
pass
except Exception as result:
print("未知错误 %s" % result)
Python
解释器 抛出异常 时,最后一行错误信息的第一个单词,就是错误类型需求
8
除以用户输入的整数并且输出try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")
Python
解释器 抛出异常而被终止,可以再增加一个 except
语法如下:
except Exception as result:
print("未知错误 %s" % result)
提示:
- 有关完整语法的应用场景,在后续学习中,结合实际的案例会更好理解
- 现在先对这个语法结构有个印象即可
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except 错误类型2:
# 针对错误类型2,对应的代码处理
pass
except (错误类型3, 错误类型4):
# 针对错误类型3 和 4,对应的代码处理
pass
except Exception as result:
# 打印错误信息
print(result)
else:
# 没有异常才会执行的代码
pass
finally:
# 无论是否有异常,都会执行的代码
print("无论是否有异常,都会执行的代码")
else
只有在没有异常时才会执行的代码
finally
无论是否有异常,都会执行的代码
之前一个演练的 完整捕获异常 的代码如下:
try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")
except Exception as result:
print("未知错误 %s" % result)
else:
print("正常执行")
finally:
print("执行完成,但是不保证正确")
提示
需求
demo1()
提示用户输入一个整数并且返回demo2()
调用 demo1()
demo2()
def demo1():
return int(input("请输入一个整数:"))
def demo2():
return demo1()
try:
print(demo2())
except ValueError:
print("请输入正确的整数")
except Exception as result:
print("未知错误 %s" % result)
raise
异常Python
解释器会 抛出 异常之外示例
注意
Python
中提供了一个 Exception
异常类Exception
的 对象raise
关键字 抛出 异常对象需求
input_password
函数,提示用户输入密码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)
模块是 Python 程序架构的一个核心概念
py
结尾的 Python
源代码文件都是一个 模块import 模块名1, 模块名2
提示:在导入模块时,每个导入应该独占一行
import 模块名1
import 模块名2
模块名.
使用 模块提供的工具 —— 全局变量、函数、类as
指定模块的别名如果模块的名字太长,可以使用
as
指定模块的名称,以方便在代码中的使用
import 模块名1 as 模块别名
注意:模块别名 应该符合 大驼峰命名法
from ... import
的方式import 模块名
是 一次性 把模块中 所有工具全部导入,并且通过 模块名/别名 访问# 从 模块 导入 某一个工具
from 模块名1 import 工具名
模块名.
注意
如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数
import
代码应该统一写在 代码的顶部,更容易及时发现冲突as
关键字 给其中一个工具起一个别名# 从 模块 导入 所有工具
from 模块名1 import *
注意
这种方式不推荐使用,因为函数重名并没有任何的提示,出现问题不好排查
Python
的解释器在 导入模块 时,会:
在开发时,给文件起名,不要和 系统的模块文件 重名
Python
中每一个模块都有一个内置属性 __file__
可以 查看模块 的 完整路径
示例
import random
# 生成一个 0~10 的数字
rand = random.randint(0, 10)
print(rand)
注意:如果当前目录下,存在一个
random.py
的文件,程序就无法正常执行了!
Python
的解释器会 加载当前目录 下的 random.py
而不会加载 系统的 random
模块Python
文件 就是一个 模块实际开发场景
__name__
属性
__name__
属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行!
__name__
是 Python
的一个内置属性,记录着一个 字符串__name__
就是 模块名__name__
是 __main__
在很多 Python
文件中都会看到以下格式的代码:
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
# 在代码的最下方
def main():
# ...
pass
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
main()
__init__.py
_
好处
import 包名
可以一次性导入 包 中 所有的模块hm_message
的 包send_message
和 receive_message
send_message
文件中定义一个 send
函数receive_message
文件中定义一个 receive
函数hm_message
的包__init__.py
__init__.py
中指定 对外界提供的模块列表 重要!!!!# 从 当前目录 导入 模块列表
from . import send_message
from . import receive_message
setup.py
的文件from distutils.core import setup
setup(name="hm_message", # 包名
version="1.0", # 版本
description="itheima's 发送和接收消息模块", # 描述信息
long_description="完整的发送和接收消息模块", # 完整描述信息
author="itheima", # 作者
author_email="[email protected]", # 作者邮箱
url="www.itheima.com", # 主页
py_modules=["hm_message.send_message",
"hm_message.receive_message"])
有关字典参数的详细信息,可以参阅官方网站:
https://docs.python.org/2/distutils/apiref.html
$ python3 setup.py build
$ python3 setup.py sdist
注意:要制作哪个版本的模块,就使用哪个版本的解释器执行!
$ tar -zxvf hm_message-1.0.tar.gz
$ sudo python3 setup.py install
卸载模块
直接从安装目录下,把安装模块的 目录 删除就可以
$ cd /usr/local/lib/python3.5/dist-packages/
$ sudo rm -r hm_message*
pip
安装第三方模块Python
包 / 模块
pygame
就是一套非常成熟的 游戏开发模块pip
是一个现代的,通用的 Python
包管理工具Python
包的查找、下载、安装、卸载等功能安装和卸载命令如下:
# 将模块安装到 Python 2.x 环境
$ sudo pip install pygame
$ sudo pip uninstall pygame
# 将模块安装到 Python 3.x 环境
$ sudo pip3 install pygame
$ sudo pip3 uninstall pygame
Mac
下安装 iPython
$ sudo pip install ipython
Linux
下安装 iPython
$ sudo apt install ipython
$ sudo apt install ipython3
文件的作用
将数据长期保存下来,在需要的时候使用
文本文件
二进制文件
在 计算机 中要操作文件的套路非常固定,一共包含三个步骤:
Python
中要操作文件需要记住 1 个函数和 3 个方法序号 | 函数/方法 | 说明 |
---|---|---|
01 | open | 打开文件,并且返回文件操作对象 |
02 | read | 将文件内容读取到内存 |
03 | write | 将指定内容写入文件 |
04 | close | 关闭文件 |
open
函数负责打开文件,并且返回文件对象read
/write
/close
三个方法都需要通过 文件对象 来调用open
函数的第一个参数是要打开的文件名(文件名区分大小写)
read
方法可以一次性 读入 并 返回 文件的 所有内容close
方法负责 关闭文件
read
方法执行后,会把 文件指针 移动到 文件的末尾# 1. 打开 - 文件名需要注意大小写
file = open("README")
# 2. 读取
text = file.read()
print(text)
# 3. 关闭
file.close()
提示
read
方法后,文件指针 会移动到 读取内容的末尾
思考
read
方法,读取了所有内容,那么再次调用 read
方法,还能够获得到内容吗?答案
open
函数默认以 只读方式 打开文件,并且返回文件对象语法如下:
f = open("文件名", "访问方式")
访问方式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常 |
w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a | 以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
提示
写入文件示例
# 打开文件
f = open("README", "w")
f.write("hello python!\n")
f.write("今天天气真好")
# 关闭文件
f.close()
read
方法默认会把文件的 所有内容 一次性读取到内存readline
方法readline
方法可以一次读取一行内容读取大文件的正确姿势
# 打开文件
file = open("README")
while True:
# 读取一行内容
text = file.readline()
# 判断是否读到内容
if not text:
break
# 每读取一行的末尾已经有了一个 `\n`
print(text, end="")
# 关闭文件
file.close()
目标
用代码的方式,来实现文件复制过程
# 1. 打开文件
file_read = open("README")
file_write = open("README[复件]", "w")
# 2. 读取并写入文件
text = file_read.read()
file_write.write(text)
# 3. 关闭文件
file_read.close()
file_write.close()
# 1. 打开文件
file_read = open("README")
file_write = open("README[复件]", "w")
# 2. 读取并写入文件
while True:
# 每次读取一行
text = file_read.readline()
# 判断是否读取到内容
if not text:
break
file_write.write(text)
# 3. 关闭文件
file_read.close()
file_write.close()
Python
中,如果希望通过程序实现上述功能,需要导入 os
模块序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | rename | 重命名文件 | os.rename(源文件名, 目标文件名) |
02 | remove | 删除文件 | os.remove(文件名) |
序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | listdir | 目录列表 | os.listdir(目录名) |
02 | mkdir | 创建目录 | os.mkdir(目录名) |
03 | rmdir | 删除目录 | os.rmdir(目录名) |
04 | getcwd | 获取当前目录 | os.getcwd() |
05 | chdir | 修改工作目录 | os.chdir(目标目录) |
06 | path.isdir | 判断是否是文件 | os.path.isdir(文件路径) |
提示:文件或者目录操作都支持 相对路径 和 绝对路径
ASCII
编码,UNICODE
编码等Python 2.x 默认使用
ASCII
编码格式
Python 3.x 默认使用UTF-8
编码格式
ASCII
编码256
个 ASCII
字符ASCII
在内存中占用 1 个字节 的空间
8
个 0/1
的排列组合方式一共有 256
种,也就是 2 ** 8
UTF-8
编码格式UTF-8
字符,涵盖了 地球上几乎所有地区的文字UTF-8
是 UNICODE
编码的一种编码格式Python 2.x 默认使用
ASCII
编码格式
Python 3.x 默认使用UTF-8
编码格式
utf-8
编码来处理 python 文件# *-* coding:utf8 *-*
这方式是官方推荐使用的!
# coding=utf8
Python 2.x
中,即使指定了文件使用 UTF-8
的编码格式,但是在遍历字符串时,仍然会 以字节为单位遍历 字符串u
,告诉解释器这是一个 unicode
字符串(使用 UTF-8
编码格式的字符串)(UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式)# *-* coding:utf8 *-*
# 在字符串前,增加一个 `u` 表示这个字符串是一个 utf8 字符串
hello_str = u"你好世界"
print(hello_str)
for c in hello_str:
print(c)
eval
函数eval()
函数十分强大 —— 将字符串 当成 有效的表达式 来求值 并 返回计算结果
# 基本的数学计算
In [1]: eval("1 + 1")
Out[1]: 2
# 字符串重复
In [2]: eval("'*' * 10")
Out[2]: '**********'
# 将字符串转换成列表
In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list
# 将字符串转换成字典
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}"))
Out[4]: dict
需求
input_str = input("请输入一个算术题:")
print(eval(input_str))
eval
在开发时千万不要使用
eval
直接转换input
的结果!!!! 用户可以自己调用os里面的命令!!!
__import__('os').system('ls')
import os
os.system("终端命令")