Python-基础学习笔记

Python-学习笔记

文章目录

  • Python-学习笔记
    • Python代码编写
    • 1——变量
      • 1.1——变量命名
      • 1.2——变量赋值
      • 1.3——数据类型
        • 1.3.1——输入与输出
        • 1.3.2——进制转换
      • 1.4——变量与存储地址相关函数
    • 2——运算符
      • 2.1——算数运算符
      • 2.2——赋值运算符
      • 2.3——关系运算符
      • 2.4——逻辑运算符
      • 2.5——位运算符
      • 2.6——运算符的优先级
    • 3——循环语句
      • 3.1—— while循环
      • 3.2 ——for循环
      • 3.3——跳出语句与嵌套语句
        • 3.3.1——跳出语句--->break与continue
        • 3.3.2——嵌套语句
    • 4——条件语句
      • 4.1——if
    • 5——字符串
      • 5.1——字符串的存储地址原理
      • 5.2——字符串 截取
        • 5.2.1——索引概念:index
        • 5.2.2——字符串索引机制
        • 5.2.3——切片(从长字符串得到一个小字符串)
          • 格式变形:
      • 5.3——字符串的常见操作
        • 5.3.1——取长度-len
        • 5.3.2——查找内容-find,index,rfind,rindex
        • 5.3.3——判断:startswith,endswith,isalpha,isdigit,isalnum,isspace
        • 5.3.4——统计字符串中的某字符的个数-count
        • 5.3.5——替换内容-replace
        • 5.3.6——切割字符串:split,rsplit,partition,rpartition
        • 5.3.7——修改大小写:capitalize,title,upper,lower
        • 5.3.8——空格处理:添空格:ljust,rjust,center;去空格:lstrip,rstrip,strip
        • 5.3.9——字符串拼接:join(列表的拼接:['','','','','',''].join)
        • 5.3.10——字符串格式化
    • 6——列表
      • 6.1——列表的常见操作
        • 6.1.1——增
        • 6.1.2——删
        • 6.1.3——改
        • 6.1.4——查
        • 6.1.5——排序
      • 6.2——交换两个变量的值
      • 6.3——列表推导式
    • 7——元组
      • 7.1——元组的定义
      • 7.2——下标与切片
      • 7.3——可用操作
      • 7.4——**修改元组的方法**
      • 7.5——拆包与装包
    • 8——字典
      • 8.1——特点
      • 8.2——定义
        • 8.2.1——定义空字典
        • 8.2.2——定义非字典
      • 8.3——字典的常见操作
        • 8.3.1——增添元素
        • 8.3.2——查询元素
          • 8.3.2.1——遍历字典查询
      • 8.4——字典中的函数
        • 8.4.1——items
          • 8.4.1.1——函数items理解
        • 8.4.2——values
        • 8.4.3——keys
        • 8.4.4——in
        • 8.4.5——get
      • 8.5——删除元素
        • 8.5.1——del删除
        • 8.5.2——字典中的内置删除函数
          • 8.5.2.1——pop
          • 8.5.2.2——popitem
          • 8.5.2.3——clear
      • 8.6——其他内置函数
        • 8.6.1——update
        • 8.6.2——fromkeys
    • 9--集合
      • 9.1——特点
      • 9.2——定义
      • 9.3——集合的常见操作
        • 9.3.1——添加元素
          • 9.3.1.1——add:
          • 9.3.1.2——update:
        • 9.3.2——移除元素
          • 9.3.2.1——remove:
          • 9.3.2.2——discard:
          • 9.3.2.3——clear:
          • 9.3.2.4——pop:
          • 9.3.2.5——del:
        • 9.3.3——集合中的交并差
    • 小总结
      • 特点小结:
      • 类型转换:
      • 内置方法:
      • 运算符:
  • ------------------------------------------------------------------
    • 函数
      • 函数定义:
      • 函数参数:
        • 参数类型:
          • 默认参数:
            • 默认参数注意:
          • 可变参数:
            • 可变参数的特点:
            • 两个星号的可变参数的特点:
          • 返回值:
        • 全局变量和局部变量:
        • 变量的引用:
        • 嵌套:
    • 文件操作
      • 文件的基本操作
        • 文件操作步骤
          • 打开
          • 快速体验
        • 文件对象方法
          • seek()
        • 关闭
      • 文件备份
        • 步骤
        • 代码实现
        • 文件和文件夹的操作
      • 文件重命名
      • 删除文件
      • 创建文件夹
      • 删除文件夹
      • 获取当前目录
      • 改变默认目录
      • 获取目录列表
      • 总结
    • 面向对象基础
      • 理解面向对象
      • 类和对象
        • 理解类和对象
          • 对象
        • 面向对象实现方法
          • 定义类
          • 创建对象
          • self
        • 类外面添加对象属性
        • 类外面获取对象属性
        • 类里面获取对象属性
      • 魔法方法
          • 体验`__init__()`
          • 带参数的`__init__()`
          • `__str__()`
      • 总结
    • 继承的概念
      • 单继承
      • 多继承
      • 子类重写父类同名方法和属性
      • 子类调用父类的同名方法和属性
      • 多层继承
      • super()调用父类方法
      • 私有权限
        • 定义私有属性和方法
        • 获取和修改私有属性值
      • 总结
    • 面向对象三大特性
      • 多态
        • 了解多态
        • 体验多态
      • 类属性和实例属性
        • 类属性
          • 设置和访问类属性
          • 修改类属性
        • 实例属性
      • 类方法和静态方法
        • 类方法
          • 类方法特点
          • 类方法使用场景
        • 静态方法
          • 静态方法特点
          • 静态方法使用场景
      • 总结
    • 了解异常
    • 异常的写法
      • 语法
      • 快速体验
        • 捕获指定异常
          • 语法
          • 体验
          • 捕获多个指定异常
          • 捕获异常描述信息
          • 捕获所有异常
        • 异常的else
        • 异常的finally
      • 异常的传递
      • 自定义异常
      • 总结
    • 模块
      • 导入模块
        • 导入模块的方式
        • 导入方式详解
          • import
          • from..import..
          • from .. import *
          • as定义别名
      • . 制作模块
        • 定义模块
        • 测试模块
      • 调用模块
        • 注意事项
        • 模块定位顺序
      • `__all__`
      • 制作包
        • 快速体验
      • 导入包
        • 方法一
          • 体验
        • 方法二
          • 体验
      • 总结
    • 迭代器
      • 创建一个迭代器
      • StopIteration
    • 生成器
    • Python3 模块
      • import 语句
      • from … import 语句
      • from … import * 语句
      • 深入模块
      • __name__属性
      • dir() 函数
      • 标准模块
    • 从一个包中导入*
      • 标准模块
    • 从一个包中导入*

pycharm常用快捷键

  • shift+enter 添加一行
  • ctrl+/ 行注释
  • ctrl+shift+/ 块注释、多行注释
  • tab/ shift+tab 缩进、不缩进当前行
  • ctrl+D 复制选定的区域或行
  • ctrl+home 第一行,ctrl+end 最后一行
  • ctrl+g 弹出一个框,输入跳转的行数
  • ctrl+alt+L格式化

Python代码编写

1——变量

​ python是一个弱语言编码,在变量声明的时候对数据类型不是很严格。围绕变量从三个方向学习:1.变量命名;2.变量赋值;3.数据类型。 创建变量的基本格式:变量名=变量

1.1——变量命名

​ 变量名的命名规范:
​ 1.字母、数字、下划线(不能数字开头、不能使用关键字、严格区分大小写)。
​ 2.每个单词大写GetName、get_name或用下划线间隔。

1.2——变量赋值

​ 变量赋值不同于C和Java不需要声明变量类,系统会自动进行数据类型的判断。感觉很奈斯!
​ 一般情况下 整数会被认为是int型
​ 小数会被认为是float型
​ 单双三引号会被认为是字符串
​ 赋值为true和false会被认为是布尔类型
​ 然后会根据数据的大小长度再细分为是long型、double型。
无须定义,就很nice! 字符串的赋值 字符串:‘单引号’、“双引号”、‘’‘三引号’‘’
​ 单双引号用于在输出时避免编译出错:’xx说“xxxx”‘
​ 三引号用于保留格式输出:
a=‘’’
​ 123
​ 456
​ 789
‘’’
​ 则输出样式与你三引号中的样式一样。 字符串格式化 :引用%s、%d等来替换输出字符串中的变量。

name = 'Tom'
age = 18
# 输出语句
print("my name is " + name + ". I'm " + str(age) + " years odl.")
# 这样的输出格式和Java有点相像
# 输出语句格式化
print("my name is %s. I'm %d years odl" % (name, age))
# 这样的输出格式和C有点相像

Python-基础学习笔记_第1张图片

重名变量:变量可以重名,在函数使用时,会就近原则,会使用函数之前离函数最近的变量进行使用。

1.3——数据类型

基本数据类型:int、float、string、boolean。type() 用于判断数据类型。

类型转换

例:
    a=1
    type(a)  单独使用没有输出
格式:type(变量名) 判断变量类型,用print输出
	print(type(变量名))
    print(type(a))print辅助输出

1.3.1——输入与输出

​ input(builtins 内置标准库) 和 print
​ 用input输入的值在输出时都会被封装成字符串类型,!!!注意:这时候再对输入的内容进行修改就需要注意数据类型适当运用数据类型转换。
​ 字符串与字符串的相加就等于字符串的拼接。
​ 格式:变量名=input(‘输入处的指导提示信息’)

小区别:在c语言中输入的变量类型是可以控制的,而且有时候还会帮助你转换,但python中输入的就是字符串,而且它还不帮你转。

​ print使用时是默认换行的,
​ print()的end参数使用总结:
​ print(list,end=”“)
​ 1.end的默认参数是换行:end=”\n“
2.end设置为空字符可以阻断换行:end”“
3.end可以设置任意字符串:end=”++“

1.3.2——进制转换

  • 转二进制:bin(变量)
  • 转八进制:oct(变量)
  • 转十六进制:hex(变量)
  • 转十进制:int(变量)
  • 前缀: 0b 二进制 、0o 八进制 、0x 十六进制 、 默认十进制
  • n= 0x558 该变量n为十六进制数

​ 字符串转整形可以,但带小数点的字符串不能转整形;

one = input('输入第一个数:')
two = input('输入第二个数:')
print(one + two)
# 字符串的拼接
print(int(one) + int(two))
# 字符转换成整形
three = input('输入第三个数:')
four = input('输入第四个数:')
print(three + four)
# 字符串的拼接
print(int(three) + int(four))
# 字符转换成整形

​ 结果 Python-基础学习笔记_第2张图片

​ 字符串转浮点型;
​ int —> str //整形转字符串;
​ float —> str //浮点型转字符串;
​ int —> float //整形转浮点型;
​ float —> int //浮点型转整形:小数点之后的数会被去掉。
​ boolean —> int //布尔型转整形:True---->1
​ False---->0
​ boolean —> float //布尔型转浮点型:True---->1.0
​ False---->0.0
​ boolean —> str //布尔型转字符串:True---->‘True’
​ False---->’False‘
​ int —> boolean //整形转布尔类型:大于0的数----->True
​ 空字符串、 0----->False

1.4——变量与存储地址相关函数

​ id函数:用来查看变量的地址 is函数:用来比较多个变量的存储地址是否相同

#id函数的用法
a='abcd'
print(id(a))
#------------结果
12345678
#is函数的用法
b='1234'
c=b
print(b is c)
#------------结果
True

2——运算符

2.1——算数运算符

+-*/

2.2——赋值运算符

= ---> 赋值
+=-=
*= ---> 自乘
**= ---> 次方运算
/= ---> 自除
//= ---> 整除
%= ---> 模运算(求余)

2.3——关系运算符

><==!=>=<=

2.4——逻辑运算符

andornot

2.5——位运算符

& ---> 按位与运算
| ---> 按位或运算
^ ---> 按位异或运算
~ ---> 按位非运算
<< ---> 按位左移
>> ---> 按位右移

2.6——运算符的优先级

Python-基础学习笔记_第3张图片

3——循环语句

3.1—— while循环

​ 用于不固定次数的循环 格式: while 条件: #条件成立才运行(布尔值为True)
​ 循环体 #循环体要注意循环结束条件
​ else: 上面的循环体没有出现中断过(break)就会执行下面的指令 # 注:else不仅可以放在while循环中,for循环同样适用,下面for循环中用法一样。
​ 执行指令
​ break:跳出当前循环结构
​ 例:

count=0
    while Trueprint(count)
        count+=1
        if count==n
            break #跳出循环

​ 可以用if配合break来在某一种情况下跳出循环,相比起直接用while,while要明确一个主要的循环条件或循环次数,
if配合break就可以针对运算中的某一种小类情况进行跳出,或是不明确循环条件时。

3.2 ——for循环

​ 有固定次数的循环 格式:
​ for 变量名 in range(n):
​ print(变量名)
​ 打印 n-0个数,默认从0开始 循环 n-1次
​ for 变量名 in range(start,stop): #包前不包后
​ print(变量名)
​ 打印 stop-start个数,从start开始 循环 stop-start次
​ for 变量名 in range(start,stop,step):
​ print(变量名)
​ step:步数值:每step次打印(循环)出的变量的变化量由1变为step
for i in range(n):
​ 循环体
else:
​ 执行指令

for i in range(1, 11):
    print(i, end="")
print()
for i in range(0, 11):
    print(i, end="")
print()
for i in range(11):
    print(i, end="++")
for i in range(0, 11, 2):
    print(i)
s='abcd12345'
for i in s:
    print(i)	#结果:--->	a	b	c	...	1	2	....

代码执行结果: 区别for循环的几种用法和熟悉print输出的几种方式

Python-基础学习笔记_第4张图片

3.3——跳出语句与嵌套语句

3.3.1——跳出语句—>break与continue

​ continue语句:
​ 在循环中出现continue,则跳过之后的循环内容,从新循环。
​ 跳过本次循环(后面的语句不执行)继续下一次循环。
​ 区别于break:
​ 在循环中出现break,则跳出循环体执行后面的语句。

3.3.2——嵌套语句

​ 循环嵌套 —> 打印三角 例:

n = 1
while n <= 5:
    print('*' * n)  # 这里的格式是python中特有的
    n += 1
print('------------------------')
n = 1
while n <= 5:
    m = 0
    while m < n:
        print('*', end='')
        m += 1
    n += 1
    print()

4——条件语句

(运算符构成条件)

4.1——if

格式: if 条件:
条件成立执行的语句
例:

d=input(‘请输入id/n’)	#提示:这里的input输入的是字符串类型,所以在下面判断的时候要进行转换																				不然就 if id==‘1’
if id==int1):
	print(你的id1print(。。。。。)
    。。。。

python又一大特点:
python中不用{}来规定内容范围,python用缩进来表示该行代码是否与上一行是一体的。if条件语句中,if条件命令下的条件命令就是if命令行下缩进在if之后的命令语句

格式1:

if 条件1:
        条件1成立执行的语句
    else:
        条件1不成立执行的语句

格式2:

 if 条件1:
        条件1成立执行的语句
    elif 条件2:
        条件2成立执行的语句
    elif 条件3:
        条件3成立执行的语句
    .....
    else:
        以上条件都不成立执行的语句

if的简写形式:
成立的执行语句 if 条件 else 不成立的执行语句,比如:print(ok) if a>b else print(no) 例:

a = int(input('输入比大小的a值:'))
b = int(input('输入比大小的b值:'))
print('Ok') if a > b else print('No')

if会将条件结果自动转换成一个布尔值:
只有在空字符串和0、None、()、{}、[]的时候,布尔值为False,其他时候都为True

随机数:
import random #导入random这个库
ran=random.randint(范围) #random.randint(范围:1,10):生成随机数的函数

绝对值:
abs(变量) #将变量变为绝对值

5——字符串

5.1——字符串的存储地址原理

​ 在python中有一个字符串保留区,当中存放了声明的字符串,每一个字符串对应一个地址,变量名指向对应的地址,
只要字符串相同,声明内容相同的字符串变量,他都指向同一个地址。

'''
例:
s1 = 'hello'
s2 = s1
s3 = 'hello'
s4 = 'hello1'
--->    其中 s1、s2、s3 这三个变量的地址都相同
---->   具体解释: s2 = s1 的实质就是将 s1的地址赋给s2
                 s3 = 'hello' 因为在字符串保留区中已经存在了 hello 这个字符串,所以为了节省内存,就直接使用原来字符串的地址。
                 s4 = 'hello1' 这个字符串因为还未出现在字符串保留区,所以它会有一个自己的地址,而不是用’老地址‘
'''
#代码
s1 = 'hello'
s2 = s1
s3 = 'hello'
s4 = 'hello1'

print(id(s1))
print(id(s2))
print(id(s3))
print(id(s4))

5.2——字符串 截取

5.2.1——索引概念:index

​ 一个字符串,每一个字符都对应了一个编号。

例:      A   B   C   D   E   F   G   H   I
index:   0   1   2   3   4   5   6   7   8      --->这是其中一种编号方式从左自右,从0开始
index:  -9  -8  -7  -6  -5  -4  -3  -2  -1      --->这是另一种编号方式从右自左,从-1开始

5.2.2——字符串索引机制

​ 1、 0~len(s)-1
​ 2、 -len(s)~ -1
​ 虽然两种编号方式不同,但是在切片时,都是从左自右的书写,并且两种编号方式可以混合用

例:
#截取(结果为某一字符)
s1 = 'ABCDEFG'

print(s1[4])    ------> 结果:E 
print(s1[0])    ------> 结果:A  

print(s1[-1])   ------> 结果:G 

5.2.3——切片(从长字符串得到一个小字符串)

print(s[1:4])   ------> 结果:BCD      (规则:左闭右开,左取右不取)
print(s[0:5])   ------> 结果:ABCDE
print(s[:5])    ------> 结果:ABCDE   (0到index=4,包左不包右) 

print(s[-3:-1]) ------> 结果:EF   (还是先写左再写右)
print(s[-3:7]) ------> 结果:EFG   (两种编号可以混合使用)
print(s[-3:]) ------> 结果:EFG   (从-3开始到结尾)

print(s[:]------> 结果:ABCDEFG  (相当于    print(s),且两个变量的地址一样)
    x = s[:]
    print(x)
    print(s)    --->结果相同
    
    print(id(x))
    print(id(s))    --->结果相同
格式变形:

​ 字符串变量[start: end :step] 默认是一个一个取这个元素,加入步长后,每次取 当前位置+步长(step)

print(s[:-1:2]) ------> 结果:ACE
print(s[1::2])  ------> 结果: BDF
print(s[::4])   ------> 结果: AE

step:
1.步长
2.方向    step    正数  从左向右
         step   负数  从右向左

print(s[::-1])   ------> 结果: GFEDCBA    (默认从头开始,从右段开始,方向看步长的正负)
print(s[::])   ------> 结果: ABCDEFG   (默认从头开始,从左段开始)

print(s[0:6:-2])    ------> 错误示范
                    原因:它从0开始,步数为负,方向向左,则无法到index=6
print(s[6:0:-2])    ------> 结果:GECA

5.3——字符串的常见操作

5.3.1——取长度-len

path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
print(len(path))  # --->结果:71

5.3.2——查找内容-find,index,rfind,rindex

  • find:从左向右查找,只要遇到一个符合要求的,则返回位置,如果没有找到符合要求的则返回-1
  • rfind:right find。与find对应,从右向左查找,只要遇到一个符合要求的,则返回位置,如果没有找到符合要求的则返回-1
  • index与find的区别:两种在操作上没有区别,但是index如果找不到会报错。
  • rindex与index对应。
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
# 例-find
i = path.find('w')
j = path.find('m')
print(path[i:j + 1])  # --->结果:www.bilibili.com

# 除了单个字符的查找,字符串也可以
i = path.find('bilibili')
print(i)  # 多个字符时它会返回字符串的第一个字符的位置
# --->结果:14
print(path[i:])  # --->结果:bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999

# 例-rfind
a = path.rfind('i')
print(path[a:])  # --->结果:id_from = 333.999

# 例-index
# b = path.index('$')
# print(b)

# rindex不做演示

5.3.3——判断:startswith,endswith,isalpha,isdigit,isalnum,isspace

  • startswith:判断字符串开头与要求字符是否相同
  • endswith:判断字符串结尾与要求是否相同
  • is和has:两者开头的函数都是用于判断的,is表示是否相同;has表示是否存在。都返回布尔值
  • isalpha:判断字符串是否都是字母
  • isdigit:判断字符串是否都是数字
  • isalnum:判断字符串是否含有字母和数字
  • isspace:判断字符串是否只有空格
  • isupper:判断字符串是否都是大写字母
  • islower:判断字符串是否都是小写字母

5.3.4——统计字符串中的某字符的个数-count

path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
print(path.count('b'))  # --->结果:2

5.3.5——替换内容-replace

path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
print(path.replace('bilibili', 'dilidili'))  # replace('被替换内容',‘添加进去的内容’,个数),

5.3.6——切割字符串:split,rsplit,partition,rpartition

​ 切割字符串之后,切割的内容都会被放在列表中。

  • split:自左向右进行分割,遇到目标就进行分割,默认对所有目标前后进行分割。split分割会将分割符去掉,可以添加分割次数,1就分 一次。
  • rsplit:自右向左进行分割,与split对应。
  • splitlines:按行分割,也可以指定分割次数。
  • partition:与split区别,会联通分割符分成三部分:分割符前,分割符,分割符后。
  • rpartition:自右向左进行分割,与partition对应。
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
kuai = '''
aaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbb
cccccccccccccccccc
dddddddddddddddddd
'''
print(path.split('/', 1))  # --->结果:['https: ', '/ www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999']
print(path.rsplit('/', 1))  # --->结果:['https: // www.bilibili.com / video ', ' BV1R7411F7JV?spm_id_from = 333.999']
print(kuai.splitlines())  # --->结果:['', 'aaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbb', 'cccccccccccccccccc', 'dddddddddddddddddd']
print(path.partition('/'))  # --->结果:('https: ', '/', '/ www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999')
print(
    path.rpartition('/'))  # --->结果:('https: // www.bilibili.com / video ', '/', ' BV1R7411F7JV?spm_id_from = 333.999')

5.3.7——修改大小写:capitalize,title,upper,lower

  • capitalize:把字符串中的第一个首字母大写
  • title:把每个单词的首字母大写
  • upper:把每个字母都大写
  • lower:把每个单词都小写
KPL = 'hello World'
print(KPL.capitalize())  # --->结果: Hello world
print(KPL.title())  # --->结果:Hello World
print(KPL.upper())  # --->结果: HELLO WORLD
print(KPL.lower())  # --->结果:hello world

5.3.8——空格处理:添空格:ljust,rjust,center;去空格:lstrip,rstrip,strip

  • ljust:右对齐,把字符串放右边,添加的空格放左边
  • rjust:左对齐,把字符串放左边,添加的空格放右边
  • conter:剧中对齐,把字符串放自己,添加的空格分别放到两边
  • lstrip:去左空格
  • rstrip:去右空格
  • strip:首尾都去空格
KK = 'wwwbilibili com'
print(KK.ljust(30))
print(KK.rjust(30))
KK = KK.center(30)
print(KK)
print(KK.lstrip())
print(KK.rstrip())
print(KK.strip())

5.3.9——字符串拼接:join(列表的拼接:[‘’,‘’,‘’,‘’,‘’,‘’].join)

5.3.10——字符串格式化

  1. 用格式化符号进行格式化
  2. 用format格式化:分为数字格式化和关键字格式化
name = 'orange'
age = 18
# 格式化符号
print("my name is %s. I'm %d years odl" % (name, age))
#format格式化
print("my name is {}. I'm {} years odl".format(name, age))
print("my name is {0}. I'm {1} years odl.I want to eat {0}".format(name, age))
print("my name is {name}. I'm {age} years odl".format(name='orange', age=18))
#--->结果:my name is orange. I'm 18 years odl
#--->结果:my name is orange. I'm 18 years odl.I want to eat orange

6——列表

​ 用于存储多个元素 ,列表也是一种数据类型,所以列表可以套列表(可借用数据结构中广义表的概念) ,列表元素的索引方式和字符串的方式相同(下标、切片)。

list1['abc','bcd','efg']    list[1,2,3,4,5,1.5]
#列表可以套列表:
list3=[['a'],['b','c'],['d','e','f']]

​ 定义列表

  • ​ 空列表:[] -->list1=[]
  • ​ 有内容的列表: list2=[‘元素1’,‘元素2’,‘元素3’]

6.1——列表的常见操作

6.1.1——增

  • append:会按照执行顺序往列表中添加元素。追加。
  • extend:相当于加号,能把两个列表连接起来,主要看谁调用extend 。
list1 = []
list2 = ['苹果']
list3 = ['牛奶', '面包']
list1.append('酸奶')
print(list1)  # 结果:--->['酸奶']
list2.append('酸奶')
print(list2)  # 结果:--->['苹果', '酸奶']
list3.append(list2)
print(list3)  # 结果:--->['牛奶', '面包', ['苹果', '酸奶']]

list1.extend(list2)
print(list1)  # 结果:--->['酸奶', '苹果', '酸奶']
list2.extend(list3)
print(list2)  # 结果:--->['苹果', '酸奶', '牛奶', '面包', [...]]
print(list1 + list1)  # 结果:--->['酸奶', '苹果', '酸奶', '酸奶', '苹果', '酸奶']

​ 一个嵌套小练习

# 例:超时购物
container = []  # 存放商品
flag = True
while flag:
    name = input('请输入商品名:')
    price = input('请输入价格:')
    number = input('请输入个数:')
    goods = [name, price, number]
    container.append(goods)
    answer = input('是否继续购买(按q或Q退出)')

    if answer.lower() == 'q':
        flag = False

for goods in container:
    print(goods)

6.1.2——删

  • pop(index):根据下标删除列表中元素,下标在写的时候要注意不要超出范围 ,超过会报错。不填的话,会默认将从后往前依次进行删除
  • remove(element):根据元素名称删除,如果元素不存在则会报错。从左往右依次删,删第一个数。如果列表中存在多个同名的元素,则只会删除第一个。用循环删除多个。remove在删除的时候会改变列表的长度,正常的删除重复内容会有遗漏
  • clear:直接清除列表中的所有元素,变成一个空表
  • del:根据下标进行删除,也可以删除对应的列表名
# 例:pop
alist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
alist.pop(4)
print(alist)  # 结果:--->[1, 2, 3, 4, 6, 7, 8, 9]

# 例:remove
blist = [1, 0, 4, 2, 3, 5, 3, 2, 4, 4, 5, 5]
blist.remove(5)
print(blist)  # 结果:--->[1, 0, 4, 2, 3, 3, 2, 4, 4, 5, 5]

# 易错事例
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
for i in a:
    if i == '酸奶':
        a.remove(i)
print(a)  # 结果:--->['火腿肠', '辣条', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# 重新遍历的方法,可以避免这个问题
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
for i in a[::]:
    if i == '酸奶':
        a.remove(i)
print(a)  # 结果:--->['火腿肠', '辣条', '面包', '蛋糕', '可乐']
# 也可以用取元素的方法创建一个新列表
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
b = []
for i in a:
    if i != '酸奶':
        b.append(i)
print(b)
# 也可以先查看列表中待删元素的个数来决定删除的重复次数
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
b = a.count('酸奶')
for i in range(b):
    a.remove('酸奶')
print(a)

# 例:clear
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
a.clear()
print(a)

# 例:del
# del删除指定下标元素
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
del a[0]
print(a)  # 结果:--->['辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# del删除列表名
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
b = a
del a
print(b)  # 结果:--->['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# print(a)  # 结果:--->报错

6.1.3——改

  • insert:在某个位置插入一个值,其他元素往后移

    格式:a.insert(位置,新元素) 找到位置,加入值

  • append:插入一个元素放在最后,这也是与insert的区别

# 例:insert
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
a.insert(0, '奶茶')
print(a)  # 结果:--->['奶茶', '火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']

# 例:append
a.append('奶茶')
print(a)  # 结果:--->['奶茶', '火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶', '奶茶']

# 例:extend
a = [1, 1, 1, 1, 1]
b = [2, 2, 2, 2, 2]
a.extend(b)
print(a)  # 结果:--->[1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
b.extend(a)
print(b)  # 结果:--->[2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2]

6.1.4——查

  • 元素 in 列表 —> 返回布尔类型

    与之相对的是 not in

  • 列表.index(元素) —> 返回元素的下标位置,如果没有此元素则报错

  • 列表.count(元素) —> 返回整数,如果元素不存在则返回0,如果元素存在则返回元素个数

# 例:元素 in 列表
a = ['a', 'b', 'c', 'd']
if 'a' in a:
    print(True)
else:
    print(False)
# 结果:--->True
if 'h' not in a:
    print(True)
else:
    print(False)
# 结果:--->True

# 例:index
print(a.index('a'))  # 结果:--->0

# 例:count
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
print(a.count('酸奶'))  # 结果:--->4

6.1.5——排序

  • sort:默认从小到大排。
  • reverse:翻转,为True则变,为False则不变。
# 例:sort
a = [12, 43, 31, 76, 42, 98, 14, 35, 57]
a.sort()  # 默认情况下 reverse=False
print(a)  # 结果:--->[12, 14, 31, 35, 42, 43, 57, 76, 98]
a = [12, 43, 31, 76, 42, 98, 14, 35, 57]
a.sort(reverse=True)
print(a)  # 结果:--->[98, 76, 57, 43, 42, 35, 31, 14, 12]

6.2——交换两个变量的值

  • 冒泡法;–经典
  • python自己的交换方法: a,b=b,a
# 例:数值交换
a = 111
b = 222
print(id(a), id(b))  # 结果:--->2567481677616 2567481681232
a, b = b, a
print(a, b)  # 结果:--->222 111
print(id(a), id(b))  # 结果:--->2567481681232 2567481677616
# 可以理解成改变了变量名指向的地址

6.3——列表推导式

​ 一种简化表达式来得到列表。

  • ​ 格式1:[i for i in 可迭代的]:把循环结构放入到列表中。

    ​ for循环用于产生列表中的元素i,在将i放入列表中

  • ​ 格式2:[i for i in 可迭代的 if条件]:将循环和判断一同放入列表中。

    ​ 先进行循环,拿到i;再用i来判断是否要存入列表中

  • ​ 格式3:[i if 条件 else 命令 for i in 可迭代的]:如果存在有条件不成立要执行的语句,就用到if …else…

    ​ 把if…else…放在循环前面。先循环拿参数i;把i带入判断语句进行判断,如果判断为True,存入列表;如果判断为False,执行命令。

  • ​ 格式4:[(i,j)for i in 可迭代的 for j in 可迭代的]:多个for循环,靠前的for先执行

#格式一:
list1 = []
for i in range(1, 21):
    list1.append(i)
print(list1)  # 结果:--->[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
list1 = [i for i in range(1, 21)]
print(list1)  # 结果:--->[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
list1 = [i for i in range(0, 51, 2)]
print(
    list1)  # 结果:--->[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
list1 = [i * 2 for i in range(0, 26)]
print(
    list1)  # 结果:--->[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]

#格式二:
list1 = [i for i in range(0, 51, 2) if i % 2 == 0]
print(
    list1)  # 结果:--->[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]

#格式三:
list2 = ['abc', 'eFg', 'hij', 'KLM', 'NOp', 'Qr']
list1 = [i if i.isupper() else i.upper() for i in list2]
print(list1)  # 结果:--->['ABC', 'EFG', 'HIJ', 'KLM', 'NOP', 'QR']

#格式四:
list1 = [(i, j) for i in range(1, 4) for j in range(1, 4)]
print(list1)  # 结果:--->[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]
# 小练习-按要求取元素
a = ['123', '456', 'apple', 'age', 'Faker', '927', 'hello']
list1 = [i for i in a if i.isalpha()]
print(list1)  # 结果:--->['apple', 'age', 'Faker', 'hello']
list1 = [i for i in a if i.isdigit()]
print(list1)  # 结果:--->['123', '456', '927']

7——元组

​ 元组和列表相似,但是元组中的元素不能修改仅供查看。元组使用小括号:();列表使用方括号[]。tuple :表示元组; list :表示列表

7.1——元组的定义

  • ​ 元组名=() —>定义空元组
  • 元组名=(’元素‘,)—>单个元素的元组需要在元素后加个逗号
  • ​ 元组名=(‘元素’,’元素‘) —>多个元素的元组的定义

​ 注意:只有一个元素的元组的定义和多个元组定义的不同

# 例:定义元组
# 空元组
a = ()
print(type(a))  # 结果:--->
print(a)  # 结果:--->()
# 单个元素的元组
b = ('aa',)  # 结果:--->('aa',),单个元素必须在元素后面加逗号
c = ('a', 'b', '1', '2')  # 结果:--->('a', 'b', '1', '2')
print(b)
print(c)

7.2——下标与切片

​ 元组中的元素也对应了下标,可以通过下标来查找对应位置的元素,也可以通过下标切片:tuple[1:3:-1]。

# 例:下标与切片
a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
print(a[0])  # 结果:--->1
print(a[1:4])  # 结果:--->(2, 3, 4)
print(a[::-1])  # 结果:--->(0, 9, 8, 7, 6, 5, 4, 3, 2, 1)

7.3——可用操作

  • index:找到对应位置的下标

    ​ 格式:a=tuple.index(元素,起始位置,结束位置),index都可以这么用

  • count:计数,统计元组中的元素个数

  • len:求长度

  • in, not in

  • for i in tuple:

  • while

# 例:操作
# index
n = a.index(3)
print(n)  # 结果:--->2
# count
n = a.count(3)
print(n)  # 结果:--->1
# len
n = len(a)
print(n)  # 结果:--->10
# in
if 1 in a:
    print(True)  # 结果:--->True
# for
for i in a:
    print(i)  # 结果:--->1,2,3,4,5,6,7,8,9,0

7.4——修改元组的方法

​ 用类型转换,将元组强制转换成列表,通过修改列表来增删改,改完在把列表转换成元组。

  • list(tuple) —> 元组转列表
  • tuple(list) —> 列表转元组
# 例:修改元组
b = list(a)
b.append('a')
a = tuple(b)
print(a)  # 结果:--->(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 'a')

7.5——拆包与装包

​ 概念理解:将元组拆解成一个一个的元素,这些元素可以单独装包给一些变量,这是可以一个元素装一个变量,也可以多个元素装一个变量。拆包这个不仅可以在元组上使用,列表、字典都可以。

  • 拆包格式:变量a,变量b…=元组1/列表1/字典1

    ​ 拆包注意事项:变量设置的个数要与元组/列表/字典中的元素个数要对应,,过多过少都不好。

  • 数据类型的改变

    ​ 通过拆包分出来的元素,他们的数据类型都会改变。在拆包、装包时用通配符会将元素都会变成列表类型。在输出时使用通配符也会将其原有数据类型的特征去掉。

# 例:元组拆包与装包
a = ('1', '2', '3', '4', '5', '6', '7')
b, c, d, *e = a
print(b, c, d)  # 结果:--->1 2 3
print(type(b))  # 结果:--->
print(e)  # 结果:--->['4', '5', '6', '7']
print(type(e))  # 结果:--->
print(*a)  # 结果:--->1 2 3 4 5 6 7
# 例:字典拆包与装包
a = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
q, w, *e = a
print(q)  # 结果:--->a
print(type(q))  # 结果:--->
print(e)  # 结果:--->['c', 'd', 'e']
print(*e)  # 结果:--->c d e
print(type(e))  # 结果:--->
print(*a)  # 结果:--->a b c d e
# 例:列表拆包与装包
a = [1, 2, 3, 4, 5, 6, 7, 8]
g, h, j, *f = a
print(g)  # 结果:--->1
print(type(g))  # 结果:--->
print(f)  # 结果:--->[4, 5, 6, 7, 8]
print(type(f))  # 结果:--->
print(*a)  # 结果:--->1 2 3 4 5 6 7 8

​ 注意输出方式与输出结果,理解通配符的作用以及拆包与装包操作。

8——字典

8.1——特点

  • 符号:{}
  • 关键字:dict
  • 保存的元素是:key:value 一对数进行操作

8.2——定义

8.2.1——定义空字典

  • 方式一:dict1={}
  • 方式二:dict2=dict() /列表和元组都都可以用这种方式:list1=list(),tuple1=tuple()

​ 字典定义要求成对:key:value(键:值)

# 例:定义空列表
dict1 = {}
print(dict1)  # 结果:--->{}
dict2 = dict()
print(dict2)  # 结果:--->{}
# list1=list(),tuple1=tuple()是一样的

8.2.2——定义非字典

  • 方式一:字典名={key1:value1,key2:value2,key3:value3}
  • 方式二:字典名={[(key1,value1),(key2,value2),(key3,value3)]}

​ 注意:key和value要对应,这样才能定义成功。key是唯一的,value是可变的。在定义的时候key不能对应多个value。

#错误示范
classmate={'name':'Tom','name':'Tony'}
print(classmate)	# 结果:---->报错

​ 上述代码是不行的,你的一个key不能有两个value值。好比一个人的身份证号码是唯一的,可以通过身份证号找到你叫张三,当然别人也可以叫张三,甚至叫李四。字典类比身份证,身份证号码类比key值,名字类比value值。

# 例:定义非空列表
zhangsan = {'age': 18, 'sex': '男'}
print(zhangsan)  # 结果:--->{'age': 18, 'sex': '男'}

重点讲解方式二

​ 用方式二定义字典需要使用列表,元组,在这个过程中先进行一个拆包和装包,他把列表、元组拆装重组成字典,因为列表中的元组有一对元素,所以才能将其对应的放入key和value中。
​ 默认将元组中的前者放入key中,后者放入value中,单独使用列表不行,他会将列表中的元素都当作key值,单独使用元组也不行。只有列表中的元素成对存在时才能定义成功。

# 例:定义列表二
kuangtu = dict([('name', 'zhangsan'), ('age', '28'), ('sex', 'man')])
print(kuangtu)  # 结果:--->{'name': 'zhangsan', 'age': '28', 'sex': 'man'}
#特殊示范--元组套列表
A = dict((['name', 'xiaohong'], ['age', 20], ['sex', 'man']))
print(A)  # 结果:--->{'name': 'xiaohong', 'age': 20, 'sex': 'man'}
# 错误示范--只使用列表
xiaoming = dict(['age', '18', 'sex', 'man'])
print(xiaoming)  # 结果:--->报错
# 错误示范--只是要元组
daming = dict(('age', '18', 'sex', 'man'))
print(daming)  # 结果:--->报错

​ 在创建字典时,对字典内元素的添加要求是成对添加,只使用列表和只使用元组会无法确定key值和value值。第二个特殊示范用元组套列表,只要对应号key,value还是能成功的,但我在pycharm中编写的时候显示意外类型,这代表这样的操作并不常用,但是合理。

8.3——字典的常见操作

8.3.1——增添元素

  • 格式:字典名[key1]=value1

    ​ 这样就将key1和value1配对起来存入字典中了

    注意:当你在同样个字典中用同一个key存入一个新value时,会进行覆盖

# 例:字典的覆盖
dict1 = {}
dict1['name'] = 'Tom'
dict1['name'] = 'Tony'
print(dict1)  # 结果:--->{'name': 'Tony'}

​ key值是唯一的,value值可以是不唯一的。

# 小练习-利用字典存储
print('----------字典存储----------')
database = []  # 这个列表用来存储字典,把他放在循环外以免被置空
while True:
    name = input('姓名:')	#两年半练习生、时间管理带师、加拿大电音王
    age = input('年龄:') #18、38、28
    sex = input('性别:') #男、男、男
    user = {}  # 每一次都会在这儿创建一个空字典
    user['name'] = name
    user['age'] = age
    user['sex'] = sex
    database.append(user)
    Flag = input('是否继续?(w退出)')
    if Flag == 'w':
        break
print(database)
# 结果:--->[{'name': '两年半练习生', 'age': '18', 'sex': '男'}, {'name': '时间管理带师', 'age': '38', 'sex': '男'}, {'name': '加拿大电音王', 'age': '28', 'sex': '男'}]

8.3.2——查询元素

​ 字典中找元素根据key来找,取元素也是。切记如果字典中找不到输入的key值,则会报错。可以去看字典中的函数 —> get

  • 格式:dict[key] —> value
8.3.2.1——遍历字典查询

​ for循环遍历

  • ​ for i in dict: —> 把每一个key都依次赋给i

    ​ 可用 in。单独遍历字典的结果是:字典的key

# 例:单个查询
a = {'name': 'kali', 'dream': 'fly', 'talent': 'computer'}
print(a['name'])  # 结果:--->kali
# 借用上述字典循环遍历
for i in a:  # in把字典a中的key依次拿出来给到i
    print(a[i], end="/")  # 结果:--->kali/fly/computer/
    print(i, end="/")  # 结果:--->dream/computer/talent/

8.4——字典中的函数

8.4.1——items

​ 取出所有的key值和value值,将字典中key和对应的value转换成一个元组,并放入列表中

  • 格式:dict.items()
# 例:字典中的函数-items
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
b = a.items()
c = dict.items(a)
print(b)  # 结果:--->dict_items([('name', 'hacker'), ('love', 'kali'), ('talent', 'computer')])
print(c)  # 结果:--->dict_items([('name', 'hacker'), ('love', 'kali'), ('talent', 'computer')])
# 两种格式都可以。print(a.items()),print(dict.items(a))也是一样的

​ 用这个函数可对value进行查询操作

  • 格式: for key,value in dict.items:
# 例:字典中的函数-items-对value进行查询
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
for key, value in a.items():
    if value == 'kali':
        print(key)  # 结果:--->love
# 通过value值查找key值
# 遍历直接得到value值
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
for key, value in a.items():
    print(value, end='/')  # 结果:--->hacker/kali/computer/
8.4.1.1——函数items理解

​ 此时的dict.items()是一个列表–[(),(),(),()],关键字in每次都将列表中的一个元组拿出赋给 key,value 元组–(key1,value1),元组中正好也是成对的元素,这就可以分别赋给两个。

8.4.2——values

​ 只取出字典中所有value值。

  • 格式:dict.values
# 例:字典中的函数-values
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(a.values())  # 结果:--->dict_values(['hacker', 'kali', 'computer'])
b = dict.values(a)
print(b)  # 结果:--->dict_values(['hacker', 'kali', 'computer'])

8.4.3——keys

​ 只取出字典中所有key。

  • 格式:dict.keys
# 例:字典中的函数-keys
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(a.keys())  # 结果:--->dict_keys(['name', 'love', 'talent'])
b = dict.keys(a)
print(b)  # 结果:--->dict_keys(['name', 'love', 'talent'])

8.4.4——in

​ 不仅在列表,元组中可以用,字典也可以

  • 格式:key in dict

8.4.5——get

​ 用key去找value,比起直接用key去寻找,他不会报错,并且他可以设默认数据—0,‘none’,‘这里没有你要的东西’

  • 格式:dict.get(‘key’,设置默认数据)
# 例:字典中的函数-get
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(a.get('name'))  # 结果:--->hacker
# 对比get函数和通过key值直接查询
print(a.get('大帅逼', '字典中没有发现改key'))  # 结果:--->字典中没有发现改key
print(a['大帅逼'])  # 结果:--->报错

8.5——删除元素

8.5.1——del删除

  • 格式:del dict[‘key’]

    如果没有key值,则会报错

# 例:字典中的函数-get
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
del a['name']
print(a)  # 结果:--->{'love': 'kali', 'talent': 'computer'}
del a['大帅逼']
print(a)  # 结果:--->报错
# 因为没有没有key值,所以报错
del a
print(a)  # 结果:--->报错
# 因为这时候a这个字典已经被删除了,没有字典a,所以打印会报错		

8.5.2——字典中的内置删除函数

8.5.2.1——pop

​ 就删除了指定的key

  • ​ 格式:dict.pop(‘key’,默认数据)

  • ​ result=dict.pop(‘key’,默认数据)

    如果key存在则能删除成功,且result会得到被删除的key的对应value值
    如果key不存在则删除失败,result会得到这个默认数据

# 例:删除函数-pop
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
result = a.pop('大帅逼', '该字典中没有该key')
print(a)  # 结果:--->{'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(result)  # 结果:--->该字典中没有该key
result = a.pop('love', '该字典中没有该key')
print(a)  # 结果:--->{'name': 'hacker', 'talent': 'computer'}
print(result)  # 结果:--->kali
# 当使用pop函数不添加默认数据时,他也会报错
a.pop('大帅逼')
print(a)  # 结果:--->报错
8.5.2.2——popitem

​ 随机删除字典中的键值对,不用指定key值,一般是从末尾删除元素---->结合下面随机概念理解。

  • 随机概念:字典是没有下标与编号的,所有可以理解为字典中的键值对是乱序的

  • 格式:dict.popitem()

# 例:删除函数-popitem
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
a.popitem()
print(a)  # 结果:--->{'name': 'hacker', 'love': 'kali'}
dict.popitem(a)
print(a)  # 结果:--->{'name': 'hacker'}
a.popitem()
print(a)  # 结果:--->{}

​ 可以根据上述结果确定popitem函数每次都是删除最后一个元素。

8.5.2.3——clear

​ 清空字典

  • ​ 格式:dict.clear()
# 例:删除函数-clear
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
a.clear()
print(a)  # 结果:--->{}

8.6——其他内置函数

8.6.1——update

​ 字典与字典的相加

  • 格式:dict1.update(dict2)

    字典中相同key的会进行value值的覆盖,没有的key会进行追加

# 例:其他内置函数-update
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
b = {'name': 'kali', 'dream': 'fly', 'talent': 'computer'}
a.update(b)  # 这里是a调用update函数,所以是在字典a中进行覆盖与追加
print(a)  # 结果:--->{'name': 'kali', 'love': 'kali', 'talent': 'computer', 'dream': 'fly'}
print(b)  # 结果:--->{'name': 'kali', 'dream': 'fly', 'talent': 'computer'}

8.6.2——fromkeys

​ 将字符串、列表、元组转换成字典,前面这几个数据类型中的元素会被存储为key值,默认是将value值置为none,也可以自己设定默认值。

# 例:其他内置函数-fromkeys
a = [1, 2, 3, 'si', 'wu']
b = ('kali', 'hacker', 'computer', 4, 5)
c = dict.fromkeys(a)
print(c)  # 结果:--->{1: None, 2: None, 3: None, 'si': None, 'wu': None}
d = dict.fromkeys(b, 666)
print(d)  # 结果:--->{'kali': 666, 'hacker': 666, 'computer': 666, 4: 666, 5: 666}
e = dict.fromkeys(b, a)
print(e)
# 结果:--->{'kali': [1, 2, 3, 'si', 'wu'], 'hacker': [1, 2, 3, 'si', 'wu'], 'computer': [1, 2, 3, 'si', 'wu'], 4: [1, 2, 3, 'si', 'wu'], 5: [1, 2, 3, 'si', 'wu']}

9–集合

9.1——特点

​ 没有重复的元素,元素的排列是无序的(没有下标),但会自动从小到大排列数字,如果是一个字符串(字符)和数字混合的集合,输出集合时也会将数字进行排列。

9.2——定义

​ 集合也用花括号声明定义({})。

  • ​ 定义空集合:set1=set()

    ​ 空集合的定义只能这样,因为集合和字典定义用到括号一样,如果用:set1={}会被认为是定义的字典

  • ​ 定义非空集合:set2={‘xiaoxiaoguai’}

    ​ 用花括号定义,如果括号中的是单个元素,则是集合,括号中的是一对元素,则是字典。

# 例:定义集合
# 空集合
a = set()
print(type(a))  # 结果:--->
# 虽然集合也是用{}表示,但是定义空集合时不能使用
# 错误示范
b = {}
print(type(b))  # 结果:--->
# 定义非空集合
a = set('关于我学习python结果完不成任务这件事')  # 只能设置一个参数,不能用逗号多设置参数。这样会把长字符串中每一个字符都作为集合中的一个元素。
print(a)  # 结果:--->{'任', '成', '关', '完', 'o', '务', '结', '不', 't', 'n', '习', '我', '事', '件', '这', 'p', '于', 'y', '学', '果', 'h'}
b = {'大大怪', '小小怪', '猪猪侠'}  # 可设置多个参数,多个参数的设置用逗号隔开
print(b)  # 结果:--->{'大大怪', '小小怪', '猪猪侠'}
c = {0, 1, 9, 2, 8, 3, 7, 4, 6, 5, 5, 6, 6, 4, 4, 4}  # 集合特点展示
print(c)  # 结果:--->{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

9.3——集合的常见操作

9.3.1——添加元素

9.3.1.1——add:
  • 添加元素的命令,添加已有元素时(重复元素)是无法添加的
9.3.1.2——update:
  • 和字典中的update作用一样,用法也一样。相当于并集操作
# 例:添加元素
a = set()
b = {'we', 'are', 'family'}
print(a)  # 结果:--->
print(b)  # 结果:--->
a.add('boy')
a.add('girl')
a.add('father')  # add也只能带一个参数,不能同时添加多个元素
a.add('mother')
a.add('girl')  # 重复添加,但是输出结果只会有一个'girl'
a.add('sister')
print(a)  # 结果:--->{'sister', 'girl', 'boy', 'father', 'mother'}
b.add(a)  # add只能添加元素,不能添加其他类型的数据(列表、元组、字典)
print(b)  # 结果:--->报错
b.update(a)#注意不要把追加对象弄反了
print(a)  # 结果:--->{'sister', 'girl', 'boy', 'father', 'mother'}
print(b)  # 结果:--->{'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}

9.3.2——移除元素

9.3.2.1——remove:
  • ​ 移除指定元素,使用时要指明具体元素,如果在集合中不存在,那就会报错
# 例:移除元素-remove
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
a.remove('boy')  # 正常演示
print(a)  # 结果:--->{'father', 'girl', 'sister', 'family', 'are', 'we', 'mother'}
b = a.remove('sister')  # 看删除时是否会赋值
print(b)  # 结果:--->None     :从结果上是会有一个返回值的,none
print(a)  # 结果:--->{'father', 'girl', 'family', 'are', 'we', 'mother'}
a.remove('a')  # 移除不存在的元素,看是否有报错
print(a)  # 结果:--->报错       :有报错
c = a.remove('b')  # 看赋值的结果是否与集合内的元素有关
print(c)  # 结果:--->报错       :实际上返回的话也会是none,这一点可以用discar这个去看出来
9.3.2.2——discard:
  • ​ 与remove一样,删除指定元素,但是不会报错
# 例:移除元素-discard
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
a.discard('boy')  # 正常演示
print(a)  # 结果:--->{'father', 'girl', 'sister', 'family', 'are', 'we', 'mother'}
b = a.discard('sister')  # 看删除时是否会赋值
print(b)  # 结果:--->None     :从结果上是会有一个返回值的,none
print(a)  # 结果:--->{'father', 'girl', 'family', 'are', 'we', 'mother'}
a.discard('a')  # 移除不存在的元素,看是否有报错
print(a)  # 结果:--->{'father', 'girl', 'family', 'are', 'we', 'mother'}
c = a.discard('b')  # 看赋值的结果是否与集合内的元素有关
print(c)  # 结果:--->None       :其实discard和remove是一样的,从这里就能看出携带值是无关于集合中的内容
9.3.2.3——clear:
  • ​ 清空集合,留下一个空集合,还能存入元素
# 例:移除元素-clear
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
b = a.clear()
print(a)  # 结果:--->set()
print(b)  # 结果:--->None
# 删除后还会剩下一个空集合,还是可以往集合a中存入元素
9.3.2.4——pop:
  • ​ 弹出元素。数字集合会弹出最小的数,字符集合随机弹出,字符与数字的混合集合看各种的占比,如果弹出数字绝对是弹出最小的数。弹出与删除的异同点在:弹出能带回返回值且删除元素,删除是带入目标删除元素。
# 例:移除元素-pop
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
a.pop()
print(a)  # 结果:--->{'father', 'boy', 'mother', 'family', 'we', 'sister', 'are'}
b = a.pop()
print(a)  # 结果:--->{'sister', 'father', 'we', 'boy', 'family', 'are'}
print(b)  # 结果:--->mother
9.3.2.5——del:
  • ​ 消除集合,存储地址被消除,无法再存入元素
# 例:移除元素-del
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
del a
print(a)  # 结果:--->报错       :直接报错,且报错原有是未定义‘a’这个变量
# del相比clear,del是直接把集合去除,而不是把集合中的所有元素去除。

9.3.3——集合中的交并差

  • 交集-intersection()-对应符号:&(与符号)
  • 并集-union()-对应符号:|(或符号)
  • 差集-difference()-对应符号:-
# 例:集合中的交并差
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}
c = {1, 3, 5, 7, 9}
# intersection-交集
print(a.intersection(b))  # 结果:--->{3, 4, 5}
# 交集的符号化表示运算
print(a & b)  # 结果:--->{3, 4, 5}
# union-并集
print(b.union(c))  # 结果:--->{1, 3, 4, 5, 6, 7, 9}
# 并集的符号化表示运算
print(b | c)  # 结果:--->{1, 3, 4, 5, 6, 7, 9}
# difference-差集
d = b.difference(c)
print(d)  # 结果:--->{4, 6}
# 差集的符号化表示运算
e = b - c
print(e)  # 结果:--->{4, 6}
# 集合的概念和数学中集合的概念差不多,然后交集、并集、差集,这些也和数学中的概念相识。
# 符号化是更简便的一种方式,并且这些符号和逻辑符有些相识,可以与逻辑符结合记忆理解。

结合操作方法(感谢菜鸟教程)

方法 描述
add() 为集合添加元素
clear() 移除集合中的所有元素
copy() 拷贝一个集合
difference() 返回多个集合的差集
difference_update() 移除集合中的元素,该元素在指定的集合也存在。
discard() 删除集合中指定的元素
intersection() 返回集合的交集
intersection_update() 返回集合的交集。
isdisjoint() 判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。
issubset() 判断指定集合是否为该方法参数集合的子集。
issuperset() 判断该方法的参数集合是否为指定集合的子集
pop() 随机移除元素
remove() 移除指定元素
symmetric_difference() 返回两个集合中不重复的元素集合。
symmetric_difference_update() 移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中。
union() 返回两个集合的并集
update() 给集合添加元素

小练习

#产生五组不重复的六位随机码
import random

data = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
J = set()

while True:
    code = ''
    for i in range(6):
        r = random.choice(data)  # random.randint-->使用的是下标;random.choice-->使用的是字符串
        code = code + r
    J.add(code)
    if len(J) == 5:
        break

print(J)  # 结果:-->{'IKIXFw', '9T39WR', 'wpTXtK', '3bQbDC', 'Lx84e9'}

小总结

​ 此时,python中的数据类型就基本学完了。其中有基本编程语言都会有的基础数据类型,也有python中存在的列表、元组、字典、集合。基础数据类型较为熟悉,这里就把列表、元组、字典、集合这四个数据类型做个小总结:

特点小结:

  • ​ list :运行重复,有序,有下标[]
  • ​ tuple:允许重复,里面的元素不能增加删除修改,只能查看()
  • ​ dict:键值对存在,键:唯一;值:允许重复 无序 {}
  • ​ set:不允许重复,无序{}

类型转换:

  • ​ list—>tuple,set,(长度可能发生改变),dict:[(),[]]
# 类型转换-list
# list--->tuple
a = ['a', 1, 'b', 2, 'c', 3]
b = tuple(a)
print(b)  # 结果:--->('a', 1, 'b', 2, 'c', 3)
# list--->set
print(set(a))  # 结果:--->{'a', 1, 2, 3, 'c', 'b'}
# 特殊转换:list--->dict
a = [['a', 'b'], ('c', 1), (4, 'd'), ['3', 2]]
print(dict(a))  # 结果:--->{'a': 'b', 'c': 1, 4: 'd', '3': 2}
  • ​ tuple—>list,set,dict:((),[])
# 类型转换-tuple
# tuple--->list
a = ('a', 1, 'b', 2, 'c', 3)
b = list(a)
print(b)  # 结果:--->['a', 1, 'b', 2, 'c', 3]
# tuple--->set
print(set(a))  # 结果:--->{'b', 1, 2, 'c', 3, 'a'}
# 特殊转换:tuple--->dict
a = (['a', 'b'], ('c', 1), (4, 'd'), ['3', 2])
print(dict(a))  # 结果:--->{'a': 'b', 'c': 1, 4: 'd', '3': 2}
  • ​ set—>list,tuple
# 类型转换-set
# set--->list
a = {'a', 1, 'b', 2, 'c', 3}
b = list(a)
print(b)  # 结果:--->[1, 2, 3, 'b', 'c', 'a']
# set--->tuple
print(tuple(a))  # 结果:--->(1, 2, 3, 'b', 'c', 'a')
  • ​ dict—>list,tuple(直接转会只提出key值)
# 类型转换-dict
# dict--->list
a = {'a': 'k', 1: 9, '5': 'b', 'p': 2, 'c': 3}
b = list(a)
print(b)  # 结果:--->['a', 1, '5', 'p', 'c']
# dict--->tuple
print(tuple(a))  # 结果:--->('a', 1, '5', 'p', 'c')
# 只能提出key值
print(list(a.items()))  # 结果:--->[('a', 'k'), (1, 9), ('5', 'b'), ('p', 2), ('c', 3)]
print(tuple(a.items()))  # 结果:--->(('a', 'k'), (1, 9), ('5', 'b'), ('p', 2), ('c', 3))
# 使用dict的方法就能取出键值对

内置方法:

  • print()
  • input()
  • type()
  • id()
  • len()
  • bin()—转二进制
# 内置方法-bin
a = 12345
b = bin(a)
print(b)  # 结果:--->0b11000000111001
# 0b表示二进制,之后的才是转换成的二进制数
  • oct()—转八进制
# 内置方法-oct
a = 12345
print(oct(a))  # 结果:--->0o30071
# 0o表示该数是八进制,之后的才是转换成的八进制数
  • hex()—转十六进制
# 内置方法-hex
a = 12345
print(hex(a))  # 结果:--->0x3039
# 0x表示该数是十六进制,之后的才是转换成的十六进制数
  • chr()—ASCII码转字符
# 内置方法-chr
a = 86
print(chr(a))  # 结果:--->V
# 一次转换一个,可以用循环来转换多个
  • ord()—字符转ASCII码
# 内置方法-ord
a = 'A'
print(ord(a))  # 结果:--->65
# 一次转换一个,可以用循环来转换多个
  • max()—获取最大值
# 内置方法-max
a = [31, 35, 46, 236.9, 98, 3, 54, 79, 75]
print(max(a))  # 结果:--->236.9
# 求列表中的最大值,浮点数,整数都能求,列表,元组,集合都能用
  • min()—获取最小值
# 内置方法-min
a = (31, 35, 46, 236.9, 98, 3, 54, 79, 75, 0.5)
print(min(a))  # 结果:--->0.5
  • sum()—求和
# 内置方法-sum
a = {31, 35, 46, 236.9, 98, 3, 54, 79, 75, 0.5}
print(sum(a))  # 结果:--->658.4
  • abs()—转绝对值
# 内置方法-abs
a = -123456
print(abs(a))  # 结果:--->123456
  • sorted()—数字排序(元组也可以)也可以使用reverse,-----list.sort
# 内置方法-sorted
a = [31, 53, 57, 79, 47, 13, 97]
print(sorted(a))  # 结果:--->[13, 31, 47, 53, 57, 79, 97]
a = (31, 53, 57, 79, 47, 13, 97)
print(sorted(a, reverse=True))  # 结果:--->[97, 79, 57, 53, 47, 31, 13]
  • del 变量名—删除数据

运算符:

  • +:合并操作—>字符串、列表、元组
# 运算符--'+'
# 字符串
a = 'Cynthia'
b = 'beautiful'
print(a + b)  # 结果:--->Cynthiabeautiful
# 列表
a = ['m', 'o', 'o', 'n']
b = ['l', 'o', 'v', 'e']
print(a + b)  # 结果:--->['m', 'o', 'o', 'n', 'l', 'o', 'v', 'e']
# 元组
a = ('m', 'o', 'o', 'n')
b = ('l', 'o', 'v', 'e')
print(a + b)  # 结果:--->('m', 'o', 'o', 'n', 'l', 'o', 'v', 'e')
  • *:复制操作—>字符串、列表、元组
# 运算符--'*'
# 字符串
a = 'Cynthia'
print(a * 2)  # 结果:--->CynthiaCynthia
# 列表
a = ['m', 'o', 'o', 'n']
b = ['l', 'o', 'v', 'e']
print((a, b) * 2)  # 结果:--->(['m', 'o', 'o', 'n'], ['l', 'o', 'v', 'e'], ['m', 'o', 'o', 'n'], ['l', 'o', 'v', 'e'])
# 元组
a = ('C', 'y', 'n', 't', 'h', 'i', 'a')
b = ('b', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l')
print([a,b] * 2)
# 结果:--->[('C', 'y', 'n', 't', 'h', 'i', 'a'), ('b', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l'), ('C', 'y', 'n', 't', 'h', 'i', 'a'), ('b', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l')]
  • in:检查运算是否存在—>字符串、列表、元组、字典
# 运算符--'in'
# 字符串
a = 'Cynthia'
print('l' in a)  # 结果:--->False
# 列表
a = ['m', 'o', 'o', 'n']
print('o' in a)  # 结果:--->True
# 元组
b = ('l', 'o', 'v', 'e')
print('v' in b)  # 结果:--->True
# 字典
b = {1: 2, 3: 4, 5: 6, 7: 8, 9: 0}
print(1 in b)  # 结果:--->True
  • not in:检查运算是否不存在—>字符串、列表、元组、字典
# 运算符--'not in'
# 字符串
a = 'Cynthia'
print('l' not in a)  # 结果:--->True
# 列表
a = ['m', 'o', 'o', 'n']
print('o' not in a)  # 结果:--->False
# 元组
b = ('l', 'o', 'v', 'e')
print('v' not in b)  # 结果:--->False
# 字典
b = {1: 2, 3: 4, 5: 6, 7: 8, 9: 0}
print(1 not in b)  # 结果:--->False

------------------------------------------------------------------

函数

​ 函数主要就是对功能性操作打包,方便复用。在需要时直接调用就可以实现这个功能。

函数定义:

#格式:def	函数名	([参数]):
#		执行代码

注意:当识别到def时,系统会给这个函数分配一个地址,并且开辟一个空间来存储这个函数的代码。此时函数名就代表代表该函数被分配的地址,函数名():这才代表调用该函数。

# 小练习-生成随机码的函数
import random


# 无参函数
def generate_code():
    source = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
    code = ''
    for i in range(6):
        r = random.choice(source)
        code += r
    print(code)


# 传参函数
def generate_code1(n):
    source = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
    code = ''
    for i in range(n):
        r = random.choice(source)
        code += r
    print(code)

generate_code()  # 结果:--->16Y92Q	生成一个六位随机码
generate_code1(4)  # 结果:--->7TYf	生成一个四位随机码
print(generate_code)  # 结果:--->
print(generate_code1)  # 结果:--->
#直接打印函数名,返回的结果是函数的地址,这一点很重要**名字常表示一个地址**

函数参数:

​ 函数可以带参数也可以不带参数,所以从参数的角度可以把函数分为两类:无参函数、有参函数(单个参数或多个参数)。

​ 格式:

  • 无参函数:def 函数名():

    ​ 函数体

  • 有参函数:def 函数名(参数1,参数2,参数3…):

    ​ 函数体

​ 注意:如果函数设置了参数,那么在调用函数时就要传入对应的参数。

# 例:有参函数的定义
def sum(a, b, c, d):
    return a + b + c + d


r = sum(2, 5, 9, 10)
print(r)  # 结果:--->26
w = sum('l', 'i', 'k', 'e')
print(w)  # 结果:--->like
# 错误示范
e = sum(2, 5, 9)
print(e)  # 结果:--->报错
# 在传入参数时,参数要与定义时的参数设置要对应,类型不强制要求,但带入函数体中要求能正常进行,否则也报错

参数类型:

  1. 普通参数:在定义函数时所直接设置的值—如:def name(a):其中a就是普通参数
  2. 默认参数:在定义函数时设置的一个赋值参数—如:def age(b=18):其中b就是默认参数
  3. 可变参数:在定义函数时为接收多个变量在参数前加星号—如:def friends(*c):其中的参数就是可变参数
默认参数:
# 例:默认参数
def age(a, b=18):
    print('{}今年{}岁'.format(a, b))


age('boy')  # 结果:--->boy今年18岁
age('tony', 22)  # 结果:--->tony今年22岁

​ 这里调用函数并没有给参数b赋值,但是打印结果依然有值返回,这就是默认函数的特点,在定义时设置的默认值参数可在调用时传入新的参数,如果不传新参就返回默认值,传就返回传入值。这样就就避免了有些参数不输入就会报错的问题。同样,默认参数在使用时也有一些需要注意的点。

默认参数注意:
# 注意一:默认参数的位置
def print_age(b=18, c='单身',a):
    print('{}今年{}岁还是{}。'.format(a, b, c))


print_age('张三')  # 结果:--->报错

​ 报错的原因就是在定义函数时将默认参数设置在了普通参数前.

# 注意二:多个默认参数的赋值
def print_age(a, b=18, c='单身', d='本地'):
    print('{}是{}人'.format(a, d))
    print('{}今年{}岁还是{}。'.format(a, b, c))


print_age('张三', '四川', 30, '成功人士')  # 结果:--->张三是成功人士人\n张三今年四川岁还是30。
print_age(a='张三', b=30, c='成功人士', d='四川') # 结果:--->张三是四川人\n张三今年30岁还是成功人士。

​ 这样的结果是因为在有多个默认参数时进行赋值,如果不指明具体参数,它会根据定义函数时的默认顺序去带入函数。如果遇到这样的情况,就用关键字传参来避免位置的不对应。

可变参数:
# 可变参数---*
def print_number(*number):
    print(number)
    print('--------')
    print(*number)


print_number(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
# 结果:--->(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
# 1 2 3 4 5 6 7 8 9 0
print_number(111)
# 结果:--->(111,)
# 111

# 利用可变函数做加法运算
def get_sum(*number):
    s = 0
    for i in number:
        s += i
    return s


a = get_sum(1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a)
可变参数的特点:
  • 可变参数没有参数数目限制,任意个参数都可以
  • 直接对参数进行打印会发现,这些参数都被放到一个元组当中;这里的*起到拆包装包的作用
  • 因为会将参数放到元组中,所以对元组的操作同样使用,可以在函数体中使用
# 可变参数---**
def show_book(**book):
    print(book)
    print(*book)


show_book(bookname='斗破苍穹', author='天蚕土豆', number=10)
# 结果:--->{'bookname': '斗破苍穹', 'author': '天蚕土豆', 'number': 10}
# bookname author number
两个星号的可变参数的特点:
  • 两个星号的特点就是把参数放到一个字典中,所以传参的时候要按照字典的要求,用关键字传参,这样才能成对放入字典中
  • 对字典进行拆包打印,显示的只有字典的键
  • 因为会将参数放到字典中,所以可以在函数体中用一些字典的操作(keys,values,items等)
返回值:

​ 返回值与传参是对应的关系,参数是用于像函数中传入参数,而返回值则是函数中将值传出。有返回值的函数在调用时需要有一个变量进行接收。

# 返回值:return
def b(*ff):
    return ff

j = a(1, 2)
print(j)  # 结果:--->(1, 2)
j,r=a(1,2)
print(j,r)	# 结果:--->1 2

o, u, y = b(11, 22, 33, 44, 55, 66, 77, 88, 99)
print(o, u, y)#结果:--->报错
o, u, *y = b(11, 22, 33, 44, 55, 66, 77, 88, 99)
print(o, u, y)  # 结果:--->11 22 [33, 44, 55, 66, 77, 88, 99]

注意:返回值是可以返回单个和多个返回值的。在接收返回值时可以选择用一个变量进行接收,这样的话多个返回值会被一起放入到一个元组中;当然也可以设置多个变量进行接收,需要注意的是,如果用多个变量一一接收,这要求变量和返回值的个数对应,否则会报错。在接收时也可以用一个星号去接收,可避免设置多个变量的麻烦。单独使用return可以起到终止程序进行的作用。

全局变量和局部变量:

​ 函数中的就叫局部变量(临时保存,函数被创建调用时存在,函数执行结束后局部变量也被回收),程序中设置的变量就叫全局变量。

# 全局变量和局部变量
def get_add(number):
    number *= 2
    if number > 10:
        login = True


def TF():
    if login == True:
        print('number*2>10')
    else:
        print('number*2<10')


login = False
a = int(input('input number:'))  # 输入:---> input number:10
get_add(a)
TF()  # 结果:---> number*2<10

​ 上面就是全局变量和局部变量出现的一个问题。当有全局变量且在函数中要修改全局变量时,一般情况下是无法修改的。只能调用不能修改。

​ 在函数中调用变量的机制:在函数中如果要调用某一变量,系统会先从函数内部去查看是否有该变量名的局部变量,如果没有,则会去函数外找是否有对应的全局变量,之后如果也没在程序中找到有对应的全局变量,系统就会在自己的系统中查看是否有对应的系统变量。以上都没有发现对应变量,则程序报错。

​ 在函数调用变量的过程中,如果发现变量是全局变量,则无法对其进行修改(部分变量除外),若要对其进行改动,则需要使用关键字global。

#   添加了global
def get_add(number):
    number *= 2
    if number > 10:
        global login
        login = True


def TF():
    if login == True:
        print('number*2>10')
    else:
        print('number*2<10')


login = False
a = int(input('input number:'))  # 输入:---> input number:10
get_add(a)
TF()  # 结果:---> number*2>10
print(globals())
# 结果:--->{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002BC0C9937C0>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'E:\\寒假-python爬虫\\python-基础语法\\草稿.py', '__cached__': None, 'get_add': , 'TF': , 'login': True, 'a': 11}

​ globlas函数查看全局变量:在python中这个函数可以用于查看全局变量。函数所返回的全局变量包括了系统内置的全局变量和自定义的全局变量两种。

​ global的使用:不是所有数据类型的变量都需要global去进行全局变量的改动,是否加global取决于数据类型是可变还是不可变。

# 可变与不可变
def a(*a):
    for i in a:
        list1.append(i)
    print(list1)


def b(*b):
    for i in b:
        list1.append(i)
    print(list1)


list1 = []
a(1, 2, 3, 4, 5, 6)  # 结果:--->[1, 2, 3, 4, 5, 6]
b('a', 'b', 'c', 'd')  # 结果:--->[1, 2, 3, 4, 5, 6, 'a', 'b', 'c', 'd']

​ 上述例子中,在函数中修改了数据类型为列表的全局变量但并没有使用global,因为global属于可变类型。

  • 不可变:int整形、str字符串、float浮点数、tuple元组
  • 可变类型:list列表、dict字典、set集合

​ 对于数据类型可变与不可变可以这样理解,这些不可变数据类型地址是唯一的。以整形数据为例,整形每一个数都指向一个唯一确定的地址,如果在函数中想要进行修改,那么一定会改变该变量的地址指向,没有global就无法去修改该地址指向。可变型数据的地址虽然也是确定唯一的,但是在进行修改的时候它的地址是不会改变的。(列表、字典、集合相当于一个容器,用于存储数据,在对该类数据进行修改操作时,它是对容器中的数据进行操作,但本身的地址是没有改变的)

变量的引用:

​ 在sys库中,sys.getrefcount()可用来查看变量引用次数(指向该值的指针个数)。值传递的过程就是变量的引用(a=b,赋值传递;id(a),函数调用,等)。某些值在python的系统中本身就被引用了很多次,如a=0这种小的数就被引用了很多次。

​ 变量引用的意义:变量引用的意义在于理解值的传递。在python中,值的传递实质上是地址的传递,变量名是方便我们使用的数据,变量值是系统所处理的数据两者之间靠指针相连,如果变量值是相同的,一般它的地址也是相同的,这时可能会有多个不同变量名指向这同一个地址。(更好的理解全局变量与局部变量的可变与不可变)

# 引用
import sys

a = 5892
b = a
c = a

print(id(a))  # 结果:--->2201458870416
print(id(b))  # 结果:--->2201458870416
print(id(c))  # 结果:--->2201458870416

r = sys.getrefcount(a)
print(r)  # 结果:--->6
a = 9999
print(id(a))  # 结果:--->2201458870480
print(id(b))  # 结果:--->2201458870416
print(id(c))  # 结果:--->2201458870416
r = sys.getrefcount(a)
print(r)  # 结果:--->4

​ 可以看出,b,c在从a那儿获取到地址之后,即使a改变了地址,b,c的地址也没有发生改变。

​ 参数的引用和变量的引用是相同的。

# 参数的引用
def b(k):
    k += 1


m = 9
b(m)
print(m)  # 结果:--->9
#9的地址是唯一的,且m指向9这个地址,函数中传入的k是一个临时变量运算结果为10,指向的是另一个地址。
#打印m,m的地址指向并没有发生改变,所有打印结果还是9

def a(l):
    l.insert(0, 0)


list1 = [1, 2, 3, 4]
a(list1)
print(list1)  # 结果:--->[0, 1, 2, 3, 4]
#list1的地址一直都没有发生改变,变的是该地址中所存储的值

​ 引用总结:

  • 必须要分清楚传递的值是可变类型还是不可变类型,
  • 如果是可变,函数中发生的改变,外层就能看到改变后的内容
  • 如果是不可变的,函数中发生的改变不会影响外部的变量值

嵌套:

​ 函数的嵌套在函数的使用中非常重要,这可以实现函数功能的完备性,也是装饰器的基础。

# 简单的嵌套
def name():
    print('my name is a.')

    def age():
        print('I am 18 years old.')

    print(locals())
    return age()

    name()

# 结果:--->my name is a.
# 结果:--->{'age': .age at 0x000001E8A436CAF0>}
# 结果:--->I am 18 years old
# 嵌套函数的参数传递
def number(*args):
    s = 0
    for i in args:
        s += i

    def add(number):
        number += 1000
        print(number)

    return add(s)


number(1, 2, 3, 4, 5, 6)  # 结果:--->1021
# 内部函数与外部函数的差别
def a():
    x = 100
    y = 1000
    s = x + y

    def b():
        nonlocal s
        m = 0
        m += s
        print(m)
        s += 10000
        print(s)

    return b()


a()
# 结果:--->1100
# 结果:--->11100

​ 在嵌套函数中也有内外部函数之分,这关系到内外部函数中值的调用问题,一般的外部函数的变量是可以给内部函数使用的,但是一般情况下也不允许外部函数对其进行修改。内部函数查找变量的过程:内部 —> 外部 —> 全局 —> 系统变量(builtins)

​ 闭包的条件

  1. 嵌套函数
  2. 内部函数引用外部函数的变量
  3. 返回值是内部函数

文件操作

文件的基本操作

文件操作步骤

  1. 打开文件
  2. 读写等操作
  3. 关闭文件

注意:可以只打开和关闭文件,不进行任何读写操作。

打开

在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件,语法如下:

open(name, mode)

name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。

mode:设置打开文件的模式(访问模式):只读、写入、追加等。

打开文件模式

模式 描述
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
快速体验
f = open('test.txt', 'w')

注意:此时的fopen函数的文件对象。

文件对象方法

  • 语法
对象对象.write('内容')
  • 体验
# 1. 打开文件
f = open('test.txt', 'w')

# 2.文件写入
f.write('hello world')

# 3. 关闭文件
f.close()

注意:

  1. w a模式:如果文件不存在则创建该文件;如果文件存在,w模式先清空再写入,a模式直接末尾追加。
  2. r模式:如果文件不存在则报错。
  • read()
文件对象.read(num)

num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据。

  • readlines()

readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素。

f = open('test.txt')
content = f.readlines()

# ['hello world\n', 'abcdefg\n', 'aaa\n', 'bbb\n', 'ccc']
print(content)

# 关闭文件
f.close()
  • readline()

readline()一次读取一行内容。

f = open('test.txt')

content = f.readline()
print(f'第一行:{content}')

content = f.readline()
print(f'第二行:{content}')

# 关闭文件
f.close()
seek()

作用:用来移动文件指针。

语法如下:

文件对象.seek(偏移量, 起始位置)

起始位置:

  • 0:文件开头
  • 1:当前位置
  • 2:文件结尾

关闭

文件对象.close()

文件备份

需求:用户输入当前目录下任意文件名,程序完成对该文件的备份功能(备份文件名为xx[备份]后缀,例如:test[备份].txt)。

步骤

  1. 接收用户输入的文件名
  2. 规划备份文件名
  3. 备份文件写入数据

代码实现

  1. 接收用户输入目标文件名
old_name = input('请输入您要备份的文件名:')
  1. 规划备份文件名

    2.1 提取目标文件后缀

    2.2 组织备份的文件名,xx[备份]后缀

# 2.1 提取文件后缀点的下标
index = old_name.rfind('.')

# print(index)  # 后缀中.的下标

# print(old_name[:index])  # 源文件名(无后缀)

# 2.2 组织新文件名 旧文件名 + [备份] + 后缀
new_name = old_name[:index] + '[备份]' + old_name[index:]

# 打印新文件名(带后缀)
# print(new_name)

备份文件写入数据

打开源文件 和 备份文件

将源文件数据写入备份文件

关闭文件

# 3.1 打开文件
old_f = open(old_name, 'rb')
new_f = open(new_name, 'wb')

# 3.2 将源文件数据写入备份文件
while True:
    con = old_f.read(1024)
    if len(con) == 0:
        break
    new_f.write(con)

# 3.3 关闭文件
old_f.close()
new_f.close()

文件和文件夹的操作

在Python中文件和文件夹的操作要借助os模块里面的相关功能,具体步骤如下:

  1. 导入os模块
import os
  1. 使用os模块相关功能
os.函数名()

文件重命名

os.rename(目标文件名, 新文件名)

删除文件

os.remove(目标文件名)

创建文件夹

os.mkdir(文件夹名字)

删除文件夹

os.rmdir(文件夹名字)

获取当前目录

os.getcwd()

改变默认目录

os.chdir(目录)

获取目录列表

os.listdir(目录)

总结

  • 文件操作步骤

    • 打开
    文件对象 = open(目标文件, 访问模式)
    
    • 操作

      文件对象.read()
      文件对象.readlines()
      文件对象.readline()
      
      文件对象.write()
      
      • seek()
    • 关闭

    文件对象.close()
    
  • 主访问模式

    • w:写,文件不存在则新建该文件
    • r:读,文件不存在则报错
    • a:追加
  • 文件和文件夹操作

    • 重命名:os.rename()
    • 获取当前目录:os.getcwd()
    • 获取目录列表:os.listdir()

面向对象基础

理解面向对象

面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想。

例如:洗衣服

思考:几种途径可以完成洗衣服?

答: 手洗 和 机洗。

手洗:找盆 - 放水 - 加洗衣粉 - 浸泡 - 搓洗 - 拧干水 - 倒水 - 漂洗N次 - 拧干 - 晾晒。

机洗:打开洗衣机 - 放衣服 - 加洗衣粉 - 按下开始按钮 - 晾晒。

思考:对比两种洗衣服途径,同学们发现了什么?

答:机洗更简单

思考:机洗,只需要找到一台洗衣机,加入简单操作就可以完成洗衣服的工作,而不需要关心洗衣机内部发生了什么事情。

总结:面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。

类和对象

思考:洗衣机洗衣服描述过程中,洗衣机其实就是一个事物,即对象,洗衣机对象哪来的呢?

答:洗衣机是由工厂工人制作出来。

思考:工厂工人怎么制作出的洗衣机?

答:工人根据设计师设计的功能图纸制作洗衣机。

总结:图纸 → 洗衣机 → 洗衣服。

在面向对象编程过程中,有两个重要组成部分:对象

类和对象的关系:用类去创建一个对象。

理解类和对象

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

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

类比如是制造洗衣机时要用到的图纸,也就是说类是用来创建对象

对象

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

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

面向对象实现方法

定义类

Python2中类分为:经典类 和 新式类

  • 语法
class 类名():
    代码
    ......

注意:类名要满足标识符命名规则,同时遵循大驼峰命名习惯

  • 体验
class Washer():
    def wash(self):
        print('我会洗衣服')
  • 拓展:经典类

不由任意内置类型派生出的类,称之为经典类

class 类名:
    代码
    ......
创建对象

对象又名实例。

  • 语法
对象名 = 类名()
  • 体验
# 创建对象
haier1 = Washer()

# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)

# haier对象调用实例方法
haier1.wash()

注意:创建对象的过程也叫实例化对象。

self

self指的是调用该函数的对象。

# 1. 定义类
class Washer():
    def wash(self):
        print('我会洗衣服')
        # <__main__.Washer object at 0x0000024BA2B34240>
        print(self)


# 2. 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier1对象调用实例方法
haier1.wash()


haier2 = Washer()
# <__main__.Washer object at 0x0000022005857EF0>
print(haier2)

注意:打印对象和self得到的结果是一致的,都是当前对象的内存中存储地址。

添加和获取对象属性

属性即是特征,比如:洗衣机的宽度、高度、重量…

对象属性既可以在类外面添加和获取,也能在类里面添加和获取。

类外面添加对象属性

  • 语法
对象名.属性名 =
  • 体验
haier1.width = 500
haier1.height = 800

类外面获取对象属性

  • 语法
对象名.属性名
  • 体验
print(f'haier1洗衣机的宽度是{haier1.width}')
print(f'haier1洗衣机的高度是{haier1.height}')

类里面获取对象属性

  • 语法
self.属性名
  • 体验
# 定义类
class Washer():
    def print_info(self):
        # 类里面获取实例属性
        print(f'haier1洗衣机的宽度是{self.width}')
        print(f'haier1洗衣机的高度是{self.height}')

# 创建对象
haier1 = Washer()

# 添加实例属性
haier1.width = 500
haier1.height = 800

haier1.print_info()

魔法方法

在Python中,__xx__()的函数叫做魔法方法,指的是具有特殊功能的函数。

体验__init__()

思考:洗衣机的宽度高度是与生俱来的属性,可不可以在生产过程中就赋予这些属性呢?

答:理应如此。

__init__()方法的作用:初始化对象。

class Washer():
    
    # 定义初始化功能的函数
    def __init__(self):
        # 添加实例属性
        self.width = 500
        self.height = 800


    def print_info(self):
        # 类里面调用实例属性
        print(f'洗衣机的宽度是{self.width}, 高度是{self.height}')


haier1 = Washer()
haier1.print_info()

注意:

  • __init__()方法,在创建一个对象时默认被调用,不需要手动调用
  • __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
带参数的__init__()

思考:一个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?

答:传参数。

class Washer():
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def print_info(self):
        print(f'洗衣机的宽度是{self.width}')
        print(f'洗衣机的高度是{self.height}')


haier1 = Washer(10, 20)
haier1.print_info()


haier2 = Washer(30, 40)
haier2.print_info()
__str__()

当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__方法,那么就会打印从在这个方法中 return 的数据。

class Washer():
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def __str__(self):
        return '这是海尔洗衣机的说明书'


haier1 = Washer(10, 20)
# 这是海尔洗衣机的说明书
print(haier1)

__del__()

当删除对象时,python解释器也会默认调用__del__()方法。

class Washer():
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def __del__(self):
        print(f'{self}对象已经被删除')


haier1 = Washer(10, 20)

# <__main__.Washer object at 0x0000026118223278>对象已经被删除
del haier1

总结

  • 面向对象重要组成部分

      • 创建类
    class 类名():
      代码
    
    • 对象
    对象名 = 类名()
    
  • 添加对象属性

    • 类外面
    对象名.属性名 =
    • 类里面
    self.属性名 =
  • 获取对象属性

    • 类外面
    对象名.属性名
    
    • 类里面
    self.属性名
    
  • 魔法方法

    • __init__(): 初始化
    • __str__():输出对象信息
    • __del__():删除对象时调用

继承的概念

生活中的继承,一般指的是子女继承父辈的财产。

  • 拓展1:经典类或旧式类

不由任意内置类型派生出的类,称之为经典类。

class 类名:
    代码
    ......
  • 拓展2:新式类
class 类名(object):
  代码

Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法,具体如下:

# 父类A
class A(object):
    def __init__(self):
        self.num = 1

    def info_print(self):
        print(self.num)

# 子类B
class B(A):
    pass


result = B()
result.info_print()  # 1

在Python中,所有类默认继承object类,object类是顶级类或基类;其他子类叫做派生类。

单继承

故事主线:一个煎饼果子老师傅,在煎饼果子界摸爬滚打多年,研发了一套精湛的摊煎饼果子的技术。师父要把这套技术传授给他的唯一的最得意的徒弟。

分析:徒弟是不是要继承师父的所有技术?

# 1. 师父类
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')

        
# 2. 徒弟类
class Prentice(Master):
    pass


# 3. 创建对象daqiu
daqiu = Prentice()
# 4. 对象访问实例属性
print(daqiu.kongfu)
# 5. 对象调用实例方法
daqiu.make_cake()

多继承

故事推进:daqiu是个爱学习的好孩子,想学习更多的煎饼果子技术,于是,在百度搜索到黑马程序员,报班学习煎饼果子技术。

所谓多继承意思就是一个类同时继承了多个父类。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


# 创建学校类
class School(object):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class Prentice(School, Master):
    pass


daqiu = Prentice()
print(daqiu.kongfu)
daqiu.make_cake()

注意:当一个类有多个父类的时候,默认使用第一个父类的同名属性和方法。

子类重写父类同名方法和属性

故事:daqiu掌握了师父和培训的技术后,自己潜心钻研出自己的独门配方的一套全新的煎饼果子技术。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class School(object):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


# 独创配方
class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


daqiu = Prentice()
print(daqiu.kongfu)
daqiu.make_cake()

print(Prentice.__mro__)

子类和父类具有同名属性和方法,默认使用子类的同名属性和方法。

子类调用父类的同名方法和属性

故事:很多顾客都希望也能吃到古法和黑马的技术的煎饼果子。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class School(object):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'

    def make_cake(self):
        # 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    # 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


daqiu = Prentice()

daqiu.make_cake()

daqiu.make_master_cake()

daqiu.make_school_cake()

daqiu.make_cake()

多层继承

故事:N年后,daqiu老了,想要把所有技术传承给自己的徒弟。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class School(object):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'

    def make_cake(self):
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


# 徒孙类
class Tusun(Prentice):
    pass


xiaoqiu = Tusun()

xiaoqiu.make_cake()

xiaoqiu.make_school_cake()

xiaoqiu.make_master_cake()

super()调用父类方法

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class School(Master):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')

        # 方法2.1
        # super(School, self).__init__()
        # super(School, self).make_cake()

        # 方法2.2
        super().__init__()
        super().make_cake()


class Prentice(School):
    def __init__(self):
        self.kongfu = '[独创煎饼果子技术]'

    def make_cake(self):
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)

    # 一次性调用父类的同名属性和方法
    def make_old_cake(self):
        # 方法一:代码冗余;父类类名如果变化,这里代码需要频繁修改
        # Master.__init__(self)
        # Master.make_cake(self)
        # School.__init__(self)
        # School.make_cake(self)

        # 方法二: super()
        # 方法2.1 super(当前类名, self).函数()
        # super(Prentice, self).__init__()
        # super(Prentice, self).make_cake()

        # 方法2.2 super().函数()
        super().__init__()
        super().make_cake()


daqiu = Prentice()

daqiu.make_old_cake()

注意:使用super() 可以自动查找父类。调用顺序遵循 __mro__ 类属性的顺序。比较适合单继承使用。

私有权限

定义私有属性和方法

在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。

故事:daqiu把技术传承给徒弟的同时,不想把自己的钱(2000000个亿)继承给徒弟,这个时候就要为这个实例属性设置私有权限。

设置私有权限的方法:在属性名和方法名 前面 加上两个下划线 __。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class School(object):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'
        # 定义私有属性
        self.__money = 2000000

    # 定义私有方法
    def __info_print(self):
        print(self.kongfu)
        print(self.__money)

    def make_cake(self):
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


# 徒孙类
class Tusun(Prentice):
    pass


daqiu = Prentice()
# 对象不能访问私有属性和私有方法
# print(daqiu.__money)
# daqiu.__info_print()

xiaoqiu = Tusun()
# 子类无法继承父类的私有属性和私有方法
# print(xiaoqiu.__money)  # 无法访问实例属性__money
# xiaoqiu.__info_print()

注意:私有属性和私有方法只能在类里面访问和修改。

获取和修改私有属性值

在Python中,一般定义函数名get_xx用来获取私有属性,定义set_xx用来修改私有属性值。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class School(object):
    def __init__(self):
        self.kongfu = '[黑马煎饼果子配方]'

    def make_cake(self):
        print(f'运用{self.kongfu}制作煎饼果子')


class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'
        self.__money = 2000000

    # 获取私有属性
    def get_money(self):
        return self.__money

    # 修改私有属性
    def set_money(self):
        self.__money = 500

    def __info_print(self):
        print(self.kongfu)
        print(self.__money)

    def make_cake(self):
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


# 徒孙类
class Tusun(Prentice):
    pass


daqiu = Prentice()

xiaoqiu = Tusun()
# 调用get_money函数获取私有属性money的值
print(xiaoqiu.get_money())
# 调用set_money函数修改私有属性money的值
xiaoqiu.set_money()
print(xiaoqiu.get_money())

总结

  • 继承的特点

    • 子类默认拥有父类的所有属性和方法
    • 子类重写父类同名方法和属性
    • 子类调用父类同名方法和属性
  • super()方法快速调用父类方法

  • 私有权限

    • 不能继承给子类的属性和方法需要添加私有权限
    • 语法
    class 类名():
      # 私有属性
      __属性名 =# 私有方法
      def __函数名(self):
        代码
    

面向对象三大特性

  • 封装
    • 将属性和方法书写到类的里面的操作即为封装
    • 封装可以为属性和方法添加私有权限
  • 继承
    • 子类默认继承父类的所有属性和方法
    • 子类可以重写父类属性和方法
  • 多态
    • 传入不同的对象,产生不同的结果

多态

了解多态

多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。

  • 定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
  • 好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
  • 实现步骤:
    • 定义父类,并提供公共方法
    • 定义子类,并重写父类方法
    • 传递子类对象给调用者,可以看到不同子类执行效果不同

体验多态

class Dog(object):
    def work(self):  # 父类提供统一的方法,哪怕是空方法
        print('指哪打哪...')


class ArmyDog(Dog):  # 继承Dog类
    def work(self):  # 子类重写父类同名方法
        print('追击敌人...')


class DrugDog(Dog):
    def work(self):
        print('追查毒品...')


class Person(object):
    def work_with_dog(self, dog):  # 传入不同的对象,执行不同的代码,即不同的work函数
        dog.work()


ad = ArmyDog()
dd = DrugDog()

daqiu = Person()
daqiu.work_with_dog(ad)
daqiu.work_with_dog(dd)

类属性和实例属性

类属性

设置和访问类属性
  • 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有
  • 类属性可以使用 类对象实例对象 访问。
class Dog(object):
    tooth = 10


wangcai = Dog()
xiaohei = Dog()

print(Dog.tooth)  # 10
print(wangcai.tooth)  # 10
print(xiaohei.tooth)  # 10

类属性的优点

  • 记录的某项数据 始终保持一致时,则定义类属性。
  • 实例属性 要求 每个对象 为其 单独开辟一份内存空间 来记录数据,而 类属性 为全类所共有 ,仅占用一份内存更加节省内存空间
修改类属性

类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了一个实例属性。

class Dog(object):
    tooth = 10


wangcai = Dog()
xiaohei = Dog()

# 修改类属性
Dog.tooth = 12
print(Dog.tooth)  # 12
print(wangcai.tooth)  # 12
print(xiaohei.tooth)  # 12

# 不能通过对象修改属性,如果这样操作,实则是创建了一个实例属性
wangcai.tooth = 20
print(Dog.tooth)  # 12
print(wangcai.tooth)  # 20
print(xiaohei.tooth)  # 12

实例属性

class Dog(object):
    def __init__(self):
        self.age = 5

    def info_print(self):
        print(self.age)


wangcai = Dog()
print(wangcai.age)  # 5
# print(Dog.age)  # 报错:实例属性不能通过类访问
wangcai.info_print()  # 5

类方法和静态方法

类方法

类方法特点
  • 需要用装饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数。
类方法使用场景
  • 当方法中 需要使用类对象 (如访问私有类属性等)时,定义类方法
  • 类方法一般和类属性配合使用
class Dog(object):
    __tooth = 10

    @classmethod
    def get_tooth(cls):
        return cls.__tooth


wangcai = Dog()
result = wangcai.get_tooth()
print(result)  # 10

静态方法

静态方法特点
  • 需要通过装饰器@staticmethod来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)
  • 静态方法 也能够通过 实例对象类对象 去访问。
静态方法使用场景
  • 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
  • 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
class Dog(object):
    @staticmethod
    def info_print():
        print('这是一个狗类,用于创建狗实例....')


wangcai = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
wangcai.info_print()
Dog.info_print()

总结

  • 面向对象三大特性
    • 封装
    • 继承
    • 多态
  • 类属性
    • 归属于类对象的属性,所有对象共有的属性
  • 实例属性
  • 类方法
@classmethod
def xx():
  代码
  • 静态方法
@staticmethod
def xx():
  代码

了解异常

当检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"。

例如:以r方式打开一个不存在的文件。

open('test.txt', 'r')

异常的写法

语法

try:
    可能发生错误的代码
except:
    如果出现异常执行的代码

快速体验

需求:尝试以r模式打开文件,如果文件不存在,则以w方式打开。

try:
    f = open('test.txt', 'r')
except:
    f = open('test.txt', 'w')

捕获指定异常

语法
try:
    可能发生错误的代码
except 异常类型:
    如果捕获到该异常类型执行的代码
体验
try:
    print(num)
except NameError:
    print('有错误')

注意:

  1. 如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常。
  2. 一般try下方只放一行尝试执行的代码。
捕获多个指定异常

当捕获多个异常时,可以把要捕获的异常类型的名字,放到except 后,并使用元组的方式进行书写。

try:
    print(1/0)

except (NameError, ZeroDivisionError):
    print('有错误')
捕获异常描述信息
try:
    print(num)
except (NameError, ZeroDivisionError) as result:
    print(result)
捕获所有异常

Exception是所有程序异常类的父类。

try:
    print(num)
except Exception as result:
    print(result)

异常的else

else表示的是如果没有异常要执行的代码。

try:
    print(1)
except Exception as result:
    print(result)
else:
    print('我是else,是没有异常的时候执行的代码')

异常的finally

finally表示的是无论是否异常都要执行的代码,例如关闭文件。

try:
    f = open('test.txt', 'r')
except Exception as result:
    f = open('test.txt', 'w')
else:
    print('没有异常,真开心')
finally:
    f.close()

异常的传递

体验异常传递

需求:

​ 1. 尝试只读方式打开test.txt文件,如果文件存在则读取文件内容,文件不存在则提示用户即可。

​ 2. 读取内容要求:尝试循环读取内容,读取过程中如果检测到用户意外终止程序,则except捕获异常并提示用户。

import time
try:
    f = open('test.txt')
    try:
        while True:
            content = f.readline()
            if len(content) == 0:
                break
            time.sleep(2)
            print(content)
    except:
        # 如果在读取文件的过程中,产生了异常,那么就会捕获到
        # 比如 按下了 ctrl+c
        print('意外终止了读取数据')
    finally:
        f.close()
        print('关闭文件')
except:
    print("没有这个文件")

自定义异常

在Python中,抛出自定义异常的语法为 raise 异常类对象

需求:密码长度不足,则报异常(用户输入密码,如果输入的长度不足3位,则报错,即抛出自定义异常,并捕获该异常)。

# 自定义异常类,继承Exception
class ShortInputError(Exception):
    def __init__(self, length, min_len):
        self.length = length
        self.min_len = min_len

    # 设置抛出异常的描述信息
    def __str__(self):
        return f'你输入的长度是{self.length}, 不能少于{self.min_len}个字符'


def main():
    try:
        con = input('请输入密码:')
        if len(con) < 3:
            raise ShortInputError(len(con), 3)
    except Exception as result:
        print(result)
    else:
        print('密码已经输入完成')


main()

总结

  • 异常语法
try:
  	可能发生异常的代码
except:
  	如果出现异常执行的代码
else:
  	没有异常执行的代码
finally:
  	无论是否异常都要执行的代码
  • 捕获异常
except 异常类型:
  	代码

except 异常类型 as xx:
		代码
  • 自定义异常
# 1. 自定义异常类
class 异常类类名(Exception):
  	代码
    
    # 设置抛出异常的描述信息
    def __str__(self):
      return ...


# 2. 抛出异常
raise 异常类名()

# 捕获异常
except Exception...

模块

Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。

模块能定义函数,类和变量,模块里也能包含可执行的代码。

导入模块

导入模块的方式

  • import 模块名
  • from 模块名 import 功能名
  • from 模块名 import *
  • import 模块名 as 别名
  • from 模块名 import 功能名 as 别名

导入方式详解

import
  • 语法
# 1. 导入模块
import 模块名
import 模块名1, 模块名2...

# 2. 调用功能
模块名.功能名()
  • 体验
import math
print(math.sqrt(9))  # 3.0
from…import…
  • 语法
from 模块名 import 功能1, 功能2, 功能3...
  • 体验
from math import sqrt
print(sqrt(9))
from … import *
  • 语法
from 模块名 import *
  • 体验
from math import *
print(sqrt(9))
as定义别名
  • 语法
# 模块定义别名
import 模块名 as 别名

# 功能定义别名
from 模块名 import 功能 as 别名
  • 体验
# 模块别名
import time as tt

tt.sleep(2)
print('hello')

# 功能别名
from time import sleep as sl
sl(2)
print('hello')

. 制作模块

在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。也就是说自定义模块名必须要符合标识符命名规则。

定义模块

新建一个Python文件,命名为my_module1.py,并定义testA函数。

def testA(a, b):
    print(a + b)

测试模块

在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在py文件中添加一些测试信息.,例如,在my_module1.py文件中添加测试代码。

def testA(a, b):
    print(a + b)


testA(1, 1)

此时,无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行testA函数的调用。

解决办法如下:

def testA(a, b):
    print(a + b)

# 只在当前文件中调用该函数,其他导入的文件内不符合该条件,则不执行testA函数调用
if __name__ == '__main__':
    testA(1, 1)

调用模块

import my_module1
my_module1.testA(1, 1)

注意事项

如果使用from .. import ..from .. import *导入多个模块的时候,且模块内有同名功能。当调用这个同名功能的时候,调用到的是后面导入的模块的功能。

  • 体验
# 模块1代码
def my_test(a, b):
    print(a + b)

# 模块2代码
def my_test(a, b):
    print(a - b)
   
# 导入模块和调用功能代码
from my_module1 import my_test
from my_module2 import my_test

# my_test函数是模块2中的函数
my_test(1, 1)

模块定位顺序

当导入一个模块,Python解析器对模块位置的搜索顺序是:

  1. 当前目录
  2. 如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。
  3. 如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/

模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

  • 注意
    • 自己的文件名不要和已有模块名重复,否则导致模块功能无法使用
    • 使用from 模块名 import 功能的时候,如果功能名字重复,调用到的是最后定义或导入的功能。

__all__

如果一个模块文件中有__all__变量,当使用from xxx import *导入时,只能导入这个列表中的元素。

  • my_module1模块代码
__all__ = ['testA']


def testA():
    print('testA')


def testB():
    print('testB')
  • 导入模块的文件代码
from my_module1 import *
testA()
testB()

包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为__init__.py 文件,那么这个文件夹就称之为包。

制作包

[New] — [Python Package] — 输入包名 — [OK] — 新建功能模块(有联系的模块)。

注意:新建包后,包内部会自动创建__init__.py文件,这个文件控制着包的导入行为。

快速体验

  1. 新建包mypackage
  2. 新建包内模块:my_module1my_module2
  3. 模块内代码如下
# my_module1
print(1)


def info_print1():
    print('my_module1')
# my_module2
print(2)


def info_print2():
    print('my_module2')

导入包

方法一

import 包名.模块名

包名.模块名.目标
体验
import my_package.my_module1

my_package.my_module1.info_print1()

方法二

注意:必须在__init__.py文件中添加__all__ = [],控制允许导入的模块列表。

from 包名 import *
模块名.目标
体验
from my_package import *

my_module1.info_print1()

总结

  • 导入模块方法
import 模块名

from 模块名 import 目标

from 模块名 import *
  • 导入包
import 包名.模块名

from 包名 import *
  • __all__ = [] :允许导入的模块或功能列表

迭代器

迭代是Python最强大的功能之一,是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:iter()next()

字符串,列表或元组对象都可用于创建迭代器:

\>>> list=[1,2,3,4]
 \>>> it = iter(list)   # 创建迭代器对象
 \>>> **print** (next(it))  # 输出迭代器的下一个元素
 1
 \>>> **print** (next(it))
 2
 \>>>
 迭代器对象可以使用常规for语句进行遍历
 
#!/usr/bin/python3
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print (x, end=" ")
结果:1 2 3 4
也可以使用 next() 函数:

#!/usr/bin/python3
 
import sys         # 引入 sys 模块
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
 
while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()
结果:1
2
3
4

创建一个迭代器

把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。

如果你已经了解的面向对象编程,就知道类都有一个构造函数,Python 的构造函数为 init(), 它会在对象初始化的时候执行。

更多内容查阅:Python3 面向对象

iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。

next() 方法(Python 2 里是 next())会返回下一个迭代器对象。

创建一个返回数字的迭代器,初始值为 1,逐步递增 1:

class MyNumbers:  
    def __iter__(self):    
        self.a = 1   
        return self   
    def __next__(self):    
        x = self.a    
        self.a += 1    
        return x  
    myclass = MyNumbers() myiter = iter(myclass)  
    print(next(myiter)) 
    print(next(myiter)) 
    print(next(myiter)) 
    print(next(myiter)) 
    print(next(myiter))
结果:
1
2
3
4
5

StopIteration

StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

在 20 次迭代后停止执行:

class MyNumbers: 
    def __iter__(self):    
        self.a = 1    
        return self  
    def __next__(self):   
        if self.a <= 20:      
            x = self.a      
            self.a += 1     
            return x    
        else:     
            raise StopIteration  myclass = MyNumbers() myiter = iter(myclass) 
            for x in myiter:  print(x)
结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用一个生成器函数,返回的是一个迭代器对象。

以下实例使用 yield 实现斐波那契数列:

\#!/usr/bin/python3  
import sys  def fibonacci(n): # 生成器函数 - 斐波那契   
    a, b, counter = 0, 1, 0    
    while True:       
        if (counter > n):            
            return        
        yield a      
        a, b = b, a + b       
        counter += 1 f = fibonacci(10) # f 是一个迭代器,由生成器返回生成  
        while True:    
            try:        
                print (next(f), end=" ")    
                except StopIteration:        
                    sys.exit()
#输出结果:0 1 1 2 3 5 8 13 21 34 55

Python3 模块

在前面的几个章节中我们基本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。

为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。

模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。

下面是一个使用 python 标准库中模块的例子。

#!/usr/bin/python3 # 文件名: using_sys.py  import sys  print('命令行参数如下:') for i in sys.argv:   print(i)  print('\n\nPython 路径为:', sys.path, '\n')
结果:$ python using_sys.py 参数1 参数2
命令行参数如下:
using_sys.py
参数1
参数2


Python 路径为: ['/root', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
  • 1、import sys 引入 python 标准库中的 sys.py 模块;这是引入某一模块的方法。
  • 2、sys.argv 是一个包含命令行参数的列表。
  • 3、sys.path 包含了一个 Python 解释器自动查找所需模块的路径的列表。

import 语句

想使用 Python 源文件,只需在另一个源文件里执行 import 语句,语法如下:

import module1[, module2[,... moduleN]

当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。

搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support,需要把命令放在脚本的顶端:

\#!/usr/bin/python3 # Filename: support.py  
def print_func( par ):    
    print ("Hello : ", par)    
    return
test.py 引入 support 模块:

#!/usr/bin/python3
# Filename: test.py
 
# 导入模块
import support
 
# 现在可以调用模块里包含的函数了
support.print_func("Runoob")
以上实例输出结果:
$ python3 test.py 
Hello :  Runoob

一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。

当我们使用import语句的时候,Python解释器是怎样找到对应的文件的呢?

这就涉及到Python的搜索路径,搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块。

这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。

搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块中的path变量,做一个简单的实验,在交互式解释器中,输入以下代码:

>>> import sys
>>> sys.path
['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
>>> 

sys.path 输出是一个列表,其中第一项是空串’',代表当前目录(若是从一个脚本中打印出来的话,可以更清楚地看出是哪个目录),亦即我们执行python解释器的目录(对于脚本的话就是运行的脚本所在的目录)。

因此若像我一样在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉。

了解了搜索路径的概念,就可以在脚本中修改sys.path来引入一些不在搜索路径中的模块。

现在,在解释器的当前目录或者 sys.path 中的一个目录里面来创建一个fibo.py的文件,代码如下:

\# 斐波那契(fibonacci)数列模块  
def fib(n):    # 定义到 n 的斐波那契数列    
    a, b = 0, 1    
    while b < n:       
        print(b, end=' ')        
        a, b = b, a+b    
        print()  
        def fib2(n): # 返回到 n 的斐波那契数列    
            result = []    
            a, b = 0, 1    
            while b < n:        
                result.append(b)        
                a, b = b, a+b    
                return result

然后进入Python解释器,使用下面的命令导入这个模块:

>>> import fibo

这样做并没有把直接定义在fibo中的函数名称写入到当前符号表里,只是把模块fibo的名字写到了那里。

可以使用模块名称来访问函数:

>>>fibo.fib(1000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 >>> fibo.fib2(100) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> fibo.__name__ 'fibo'

如果你打算经常使用一个函数,你可以把它赋给一个本地的名称:

>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

from … import 语句

Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,语法如下:

from modname import name1[, name2[, ... nameN]]

例如,要导入模块 fibo 的 fib 函数,使用如下语句:

>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

这个声明不会把整个fibo模块导入到当前的命名空间中,它只会将fibo里的fib函数引入进来。

from … import * 语句

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

from modname import *

这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

深入模块

模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。

每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。

所以,模块的作者可以放心大胆的在模块内部使用这些全局变量,而不用担心把其他用户的全局变量搞混。

从另一个方面,当你确实知道你在做什么的话,你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。

模块是可以导入其他模块的。在一个模块(或者脚本,或者其他地方)的最前面使用 import 来导入一个模块,当然这只是一个惯例,而不是强制的。被导入的模块的名称将被放入当前操作的模块的符号表中。

还有一种导入的方法,可以使用 import 直接把模块内(函数,变量的)名称导入到当前操作模块。比如:

>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

这种导入的方法不会把被导入的模块的名称放在当前的字符表中(所以在这个例子里面,fibo 这个名称是没有定义的)。

这还有一种方法,可以一次性的把模块中的所有(函数,变量)名称都导入到当前模块的字符表:

>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

这将把所有的名字都导入进来,但是那些由单一下划线(_)开头的名字不在此例。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。

__name__属性

一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。

#!/usr/bin/python3
# Filename: using_name.py

if __name__ == '__main__':
   print('程序自身在运行')
else:
   print('我来自另一模块')

运行输出如下:

$ python using_name.py
程序自身在运行
$ python
>>> import using_name
我来自另一模块
>>>

说明: 每个模块都有一个__name__属性,当其值是’main’时,表明该模块自身在运行,否则是被引入。

说明:namemain 底下是双下划线, _ _ 是这样去掉中间的那个空格。

dir() 函数

内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)  
['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',
 '__package__', '__stderr__', '__stdin__', '__stdout__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',
 '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',
 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',
 'call_tracing', 'callstats', 'copyright', 'displayhook',
 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
 'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',
 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',
 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
 'thread_info', 'version', 'version_info', 'warnoptions']

如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir() # 得到一个当前模块中定义的属性列表
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
>>> a = 5 # 建立一个新的变量 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', 'a', 'sys']
>>>
>>> del a # 删除变量名a
>>>
>>> dir()
['__builtins__', '__doc__', '__name__', 'sys']
>>>

标准模块

Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。

有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。

这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。

应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Runoob!')
Runoob!
C> 

包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。

比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。

这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。

不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。

现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。

并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。

这里给出了一种可能的包结构(在分层的文件系统中):

sound/                          顶层包
      __init__.py               初始化 sound 包
      formats/                  文件格式转换子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  声音效果子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  filters 子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。

目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。

最简单的情况,放一个空的 :file:init.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。

用户可以每次只导入一个包里面的特定模块,比如:

import sound.effects.echo

这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

还有一种导入子模块的方法是:

from sound.effects import echo

这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:

echo.echofilter(input, output, delay=0.7, atten=4)

还有一种变化就是直接导入一个函数或者变量:

from sound.effects.echo import echofilter

同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:

echofilter(input, output, delay=0.7, atten=4)

注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。

import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。

反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。


从一个包中导入*

如果我们使用 from sound.effects import * 会发生什么呢?

Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。

但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。

在 Windows 平台上,我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo,或者是 ECHO。

为了解决这个问题,我们只需要提供一个精确包的索引。

导入语句遵循如下规则:如果包定义文件 init.py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

作为包的作者,可别忘了在更新包之后保证 all 也更新了啊。

以下实例在 file:sounds/effects/init.py 中包含如下代码:

__all__ = ["echo", "surround", "reverse"]

这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。

如果 all 真的没有定义,那么使用**from sound.effects import ***这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。

这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。(当然如果定义了 all 就更没问题了)

通常我们并不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。

记住,使用 from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。

如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包 sound.effects 中的模块 echo,你就要写成 from sound.effects import echo。

from . import echo
from .. import formats
from ..filters import equalizer

无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"main",一个Python应用程序的主模块,应当总是使用绝对路径引用。

包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,你得在其他__init__.py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。

这个功能并不常用,一般用来扩展包里面的模块。
read_info’, ‘version’, ‘version_info’, ‘warnoptions’]


如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:

```python
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir() # 得到一个当前模块中定义的属性列表
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
>>> a = 5 # 建立一个新的变量 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', 'a', 'sys']
>>>
>>> del a # 删除变量名a
>>>
>>> dir()
['__builtins__', '__doc__', '__name__', 'sys']
>>>

标准模块

Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。

有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。

这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。

应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Runoob!')
Runoob!
C> 

包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。

比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。

这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。

不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。

现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。

并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。

这里给出了一种可能的包结构(在分层的文件系统中):

sound/                          顶层包
      __init__.py               初始化 sound 包
      formats/                  文件格式转换子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  声音效果子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  filters 子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。

目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。

最简单的情况,放一个空的 :file:init.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。

用户可以每次只导入一个包里面的特定模块,比如:

import sound.effects.echo

这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

还有一种导入子模块的方法是:

from sound.effects import echo

这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:

echo.echofilter(input, output, delay=0.7, atten=4)

还有一种变化就是直接导入一个函数或者变量:

from sound.effects.echo import echofilter

同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:

echofilter(input, output, delay=0.7, atten=4)

注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。

import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。

反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。


从一个包中导入*

如果我们使用 from sound.effects import * 会发生什么呢?

Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。

但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。

在 Windows 平台上,我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo,或者是 ECHO。

为了解决这个问题,我们只需要提供一个精确包的索引。

导入语句遵循如下规则:如果包定义文件 init.py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

作为包的作者,可别忘了在更新包之后保证 all 也更新了啊。

以下实例在 file:sounds/effects/init.py 中包含如下代码:

__all__ = ["echo", "surround", "reverse"]

这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。

如果 all 真的没有定义,那么使用**from sound.effects import ***这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。

这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。(当然如果定义了 all 就更没问题了)

通常我们并不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。

记住,使用 from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。

如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包 sound.effects 中的模块 echo,你就要写成 from sound.effects import echo。

from . import echo
from .. import formats
from ..filters import equalizer

无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"main",一个Python应用程序的主模块,应当总是使用绝对路径引用。

包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,你得在其他__init__.py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。

这个功能并不常用,一般用来扩展包里面的模块。

你可能感兴趣的:(编程语言,学习)