自学Python笔记总结(1 —— 基础)

自学Python笔记总结

网址

python官网下载
使用win+R输入CMD,进入控制台后输入python -V,出现对应的python版本就算安装成功。

Pycharm官网下载
请自行找激活教程

notepad++ 官网下载
sublime text 官网下载
notepad++和sublime text这两个工具可以更好的查看txt类型的文件

Python、Java、C、C++和JavaScript的在线编译器、可视化调试器和人工智能导师

数据类型

类型

Python里的数据类型:

  • 整型(int)
  • 浮点型(float)
  • 复数(complex)
  • 字符串(str)
  • 布尔(bool)
  • 列表(list)
  • 元组(tuple)
  • 字典(dict)
  • 集合(set)
# 数据类型的概念:
# 在Python里数据都有各自对应的类型:

# 数字类型:  整数型int   浮点型 float  复数 complex
print(45)  # int整数类型
print(3.1415)  # float类型
print((-1) ** 0.5)  # complex类型

# 字符串类型: 其实就是一段普通的文字
# python里的字符串要求使用一对单引号,或者双引号来包裹
print('今天天气好晴朗,处处好风光呀好风光')
print("56")

# 布尔类型:用来表示 真假/对错
# 布尔类型里一共只有两个值,一个是 True,另一个是 False
print(4 > 3)  # True
print(1 > 5)  # False

# 列表类型
names = ['廉颇', '小乔', '妲己', '孙尚香', '甄姬', '安琪拉']
# 字典类型
person = {'name': '貂蝉', 'age': 18, 'addr': '三国', '身高': '166cm'}
# 元组类型
nums = (1, 8, 9, 2, 3, 0)
# 集合类型
x = {9, 'hello', 'hi', 'good', True}

print(True)

查看类型,使用type内置类

a = 34
b = 'hello'
c = True
d = ['廉颇', '小乔', '妲己', '孙尚香', '甄姬', '安琪拉']

# 使用type内置类可以查看一个变量对应的数据类型
print(type(a))  # <class 'int'> 整数型
print(type(b))  # <class 'str'> 字符串类型
print(type(c))  # <class 'bool'> 布尔类型
print(type(d))  # <class 'list'> 列表类型

print(type(3.14))  # <class 'float'> 浮点类型

# 在Python里,变量是没有数据类型的,我们所说变量的数据类型,其实是变量对应的值的数据类型
e = 23
print(type(e))  # <class 'int'>
f = "hello"
print(type(f))  # <class 'str'>

标识符

标识符:变量,模块名,函数名,类名
标识符的命名规则与规范:
规则:

  1. 由数字、字母和_组成,不能以数字开头
  2. 严格区分大小写(计算机编程里,一共有52个英语字母)
  3. 不能使用关键字
    关键字:在Python语言里,有特殊含义的单词,例如 if/for/else/while/try等等… …
    关键字:
    False None True and as assert break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield

    规范: 建议遵守,遵守规范会显得专业,并且代码易读
  4. 顾名思义
  5. 遵守一定的命名规范
  6. 小驼峰命名法:第一个单词的首字母小写,以后每个单词的首字母都大写 userNameAndPassword
  7. 大驼峰命名法: 每个单词的首字母都大写 PersonModel
  8. 使用下划线连接: user_name_and_password
    在Python里的变量、函数和模块名使用下划线连接; Python里的类名使用大驼峰命名法

在这里插入图片描述

输出输入语句

Python里使用 print内置函数 来输出内容

print(value, …, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)
sep 参数用来表示输出时,每个值之间使用哪种字符作为分隔。默认使用空格作为分隔符 end
当执行完一个print语句以后,接下来要输出的字符。默认 \n 表示换行

python里使用 input 内置函数接收用户的输入 input() ==> 括号里写提示信息 定义一个变量可以保存用户输入的内容
password = input(“请输入内容”)
print(password)

format函数的语法及用法

语法:‘{}’.format()
用法:用于格式化字符串。可以接受无限个参数,可以指定顺序。返回结果为字符串

# 不设置位置,按默认顺序(从左到右)输出。

print('学习{}中的{}函数'.format('python','format'))   # '学习python中的format函数'


#指定顺序
print('学习{1}中的{0}函数'.format('format','python'))   #'学习python中的format函数'

# 设置参数
list1 = ['hello','say','world','s']  
print('LiMing {0[1]}{0[3]} {0[0]} to {0[2]}'.format(list1))   # 'LiMing says hello to world'
 

list2 = ['hello','say']  
list3 = ['world','s']
print('LiMing {0[1]}{1[1]} {0[0]} to {1[0]}'.format(list2,list3))   # 'LiMing says hello to world'

# {变量名}
z = '大家好,我是{name},我今年{age}岁了,我来自{addr}'.format(age=18, name='jack', addr='襄阳')
print(z)  # 大家好,我是jack,我今年18岁了,我来自襄阳

# 混合使用  {数字}  {变量}
a = '大家好,我是{name},我今年{1}岁了,我来自{0}'.format('泰国', 23, name='tony')
print(a) # 大家好,我是tony,我今年23岁了,我来自泰国

# {}什么都不写  {数字} 不能混合使用

d = ['zhangsan', 18, '上海', 180]
# b = '大家好,我是{},我今年{}岁了,我来自{},身高{}cm'.format(d[0], d[1], d[2], d[3])
b = '大家好,我是{},我今年{}岁了,我来自{},身高{}cm'.format(*d)
print(b)  # 大家好,我是zhangsan,我今年18岁了,我来自上海,身高180cm

info = {'name': 'chris', 'age': 23, 'addr': '北京', 'height': 190}
c = '大家好,我是{name},我来自{addr},身高{height}cm,我今年{age}岁了'.format(**info)
print(c) # 大家好,我是chris,我来自北京,身高190cm,我今年23岁了

#同时使用元组和字典传参
name=["卡卡罗特","界王拳"]
names={"nickname":"孙君","skill":"元气弹"}
print("我是{0},我的绝招是{skill}".format(*name,**names)) # 我是卡卡罗特,我的绝招是元气弹
print("我是{nickname},我的绝招是{1}".format(*name,**names)) # 我是孙君,我的绝招是界王拳

#同时使用位置参数,元组,关键字参数,字典传参。
#注意位置参数要在关键数参数前面
a=["卡卡罗特"]
dic={"name":"超级赛亚人"}
print("我是{0},我也是{0},因为我是正义的战士,所以我变成了{name}".format("卡卡罗特",*a,**dic)) # 我是卡卡罗特,我也是卡卡罗特,因为我是正义的战士,所以我变成了超级赛亚人

数字格式化

  • 保留小数点

    #保留两位小数点
    print( '{:.2f}'.format(314.541) ) # 314.54
    
    #保留一位小数点并携带正负符号
    print( '{:+.1f}'.format(1.2684) )  #  +1.3
    
    print('{:+.1f}'.format(-45.62556))  #    -45.6
     
    #不保留小数点
    print('{:.0f}'.format(-45.62556)) # -46
    
    #说明:加上‘+’可以将数字的符号正确输出
    
  • 百分比格式

    #保留两位小数点的百分比
    print('{:.2%}'.format(0.54036) )   #  54.04%
    
    #不保留小数点的百分比
    print( '{:.0%}'.format(0.54036))    # 54%
    
  • 转进制

    #b二进制,>右对齐,长度为20
    print('{:>20b}'.format(23))   #  '               10111'
    
    #d十进制,<左对齐,长度为15
    print( '{:<15d}'.format(892))    #  '892            '
    
    #x十六进制,^居中对齐,长度为10
    print( '{:^10x}'.format(16894))    # '   41fe   '   
    
    #o八进制,^居中对齐,长度为10
    print('{:^10o}'.format(1394))    #   '   2562   '
    

数据类型的转换

类型转换 将一个类型的数据转换为其他类型的数据

  • int ==> str
  • str ==> int
  • bool ==> int
  • int ==> float
# 数字0,空字符串 ''/"",空列表[],空元组(),空字典{},空集合set(),空数据None会被转换成为False
s = set()  # 空集合
print(bool(s))

运算符

算数运算符

  • 加     +
  • 减     -
  • 乘     *
  • 除    /
  • 幂运算     **
  • 整除     //
  • 取余(取模)运算     %
    在这里插入图片描述
# true 可以当作1  false 可以当作0
print(1 + true)  # 2
print(1 + false)  # 1
print(1 + 1)  # 2
print(4 - 2)  # 2
print(3 * 2)  # 6

# 在Python3里,两个整数相除,得到的结果会是一个浮点数
print(6 / 2)  # 3.0
print(9 / 2)  # 4.5

print(3 ** 3)  # 27
print(81 ** (1 / 2))  # 9.0

print(10 / 3)  # 3.333333333333333
print(10 // 3)  # 3 整除
print(-5 // 2)  # -2.5 ==> -3 争取,向下取整

print(10 % 3)  # 1  取余,只取余数部分


# 字符串里有限度的支持加法和乘法运算符


# 加法运算符:只能用于两个字符串类型的数据,用来拼接两个字符串
print('hello' + 'world')  # 将多个字符串拼接为一个字符串
# 乘法运算符:可以用于数字和字符串之间,用来将一个字符串重复多次
print('hello' * 2)  # hellohello

# -a 对于a 是取反
a = 1
print(-a)  # -1

赋值运算符的特殊场景

在这里插入图片描述

#  等号连接的变量可以传递赋值
a = b = c = d = 'hello'
print(a, b, c, d)      # hello hello hello hello
拆包
m, n = 3, 5  # 拆包
print(m, n) # 3 5
x = 'hello', 'good', 'yes'
print(x)  # ('hello', 'good', 'yes')

# 拆包时,变量的个数和值的个数不一致,会报错
# y, z = 1, 2, 3, 4, 5
# print(y, z)
# o, p, q = 4, 2
# print(o, p, q)
#     Traceback (most recent call last):
#       File "........py", line 14, in <module>
#         y, z = 1, 2, 3, 4, 5
#         ^^^^
#     ValueError: too many values to unpack (expected 2)

*表示可变长度
o, p, *q = 1, 2, 3, 4, 5, 6
print(o, p, q)  # 1 2 [3, 4, 5, 6]

比较运算符

  • 大于    >
  • 小于    <
  • 大于等于    >=
  • 小于等于     <=
  • 不等于        !=
                      <>
  • 等等与       ==

在这里插入图片描述

浮点数 和 整数 也可以比较
print(1.0 == 1)  # True
print(1.0 != 1)  # False   
print(2 > 1)  # True
print(2 < 4)  # True
print(4 >= 3)  # True
print(4 <= 9)  # True
print(5 != 6)  # True
print('hello' == 'hello')  # True

# 比较运算符在字符串里的使用
# 字符串之间使用比较运算符,会根据各个字符的编码值逐一进行比较
# ASCII码表
print('a' > 'b')  # False   97 > 98
print('abc' > 'b')  # False 97 > 98

# 数字和字符串之间,做 == 运算的结果是False,做 != 结果是True,不支持其他的比较运算
# print('a' > 90)
print('a' == 90)  # False
print('a' != 97)  # True

逻辑运算符 与 短路

  • 逻辑与    and
  • 逻辑或    or
  • 逻辑非    not
    在这里插入图片描述
#逻辑与规则:只要有一个运算数是False,结果就是False;只有所有的运算数都是True,结果才是True
print(2 > 1 and 5 > 3 and 10 > 2)  # True
print(3 > 2 and 5 < 4 and 6 > 1)  # False

# 逻辑或规则:只要有一个运算数是True,结果就是True;只有所有的运算数都是False,结果才是False
print(3 > 9 or 4 < 7 or 10 < 3)  # True
print(3 > 5 or 4 < 2 or 8 < 7)  # False

# 逻辑非运算:True ==> False   False ==> True
print(not (5 > 2))  # False


逻辑与运算的短路问题

# 逻辑与运算,只有所有的运算数都是True,结果才为True
# 只要有一个运算数是False,结果就是False
4 > 3 and print('hello world') # hello world
4 < 3 and print('你好世界')  # 逻辑与运算的短路问题 不打印

# 逻辑或运算,只有所有的运算数都是False,结果才是False
# 只要有一个运算数是True,结果就是True
4 > 3 or print('哈哈哈') # 逻辑或运算的短路问题 不打印
4 < 3 or print('嘿嘿嘿') # 嘿嘿嘿

# 逻辑运算的结果,不一定是布尔值 
# 逻辑与运算做取值时,取第一个为False的值;如果所有的运算数都是True,取最后一个值
print(3 and 5 and 0 and 'hello')  # 0
print('good' and 'yes' and 'ok' and 100)  # 100

# 逻辑或运算做取值时,取第一个为True的值;如果所有的运算数都是False,取最后一个值
print(0 or [] or 'lisi' or 5 or 'ok')  # lisi
print(0 or [] or {} or ())  # ()

在这里插入图片描述

逻辑运算符规则:

  • 逻辑与运算:
    1.只要有一个运算数是False,结果就是False;只有所有的运算数都是True,结果才是True
    2.短路:只要遇到了False,就停止,不再继续执行了 取值:取第一个为False,如果所有的运算数都是True,取最后一个运算数

  • 逻辑或运算:
    1.只要有一个运算数是True,结果就是True;只有所有的运算数都是False,结果才是False
    2.短路:只要遇到了True,就停止,不再继续执行了 取值:取第一个为True的值,如果所有的运算数都是False,取最后一个运算数

位运算符

按位与& 按位或| 按位异或^ 按位左移<< 按位右移>> 按位取反~
在这里插入图片描述

按位取反运算归纳总结了一个公式:

~a = (a +1) ×-1

如果a为十进制数178,则~a为十进制数-179;如果a为十进制数-20,则~a为十进制数19

运算符优先级

在位运算优先级中,优先级从高到低大体是:
      算术运算符→位运算符→关系运算符→逻辑运算符→赋值运算符。
在这里插入图片描述

程序流程控制

分支语句

在这里插入图片描述

if 语法:

 if 判断条件:
    条件成立时,执行的代码

if…else 语法:

 if 判断条件:
    条件成立时执行的代码
 else:
    条件不成立时执行的代码

if…elif…else 语法:

 if 判断条件:
    条件成立时执行的代码
 elif:
    条件成立时执行的代码
 else:
    条件都不成立时执行的代码

三元运算 语法:

 条件成立  if  判断条件  else条件不成立时

if 语法:

Alt


if…else语法:
Alt


if…elif…else语法:

Alt


pass 占位

pass 关键字在Python里没有意义,只是单纯的用来占位,保证语句的完整性 可用在任何语法中

pass 语法:

 if 条件判断:
    pass

循环语句 while 和 for

while 循环 语法: 【注:else 可以省略不写】

 while 判断条件::
    条件成立时执行的代码
  else:
  	条件不成立时执行的代码

for… in … 语法::
【注:
          else 可以省略不写
          in 的后面必须要是一个可迭代对象!!!
          目前接触的可迭代对象: 字符串、列表、字典、元组、集合、range
          range 内置类用来生成指定区间的整数序列(列表)
  】

 for 变量 in可迭代对象:
    条件成立时执行的代码
 else:
    条件不成立时执行的代码

while 语法:

Alt


for… in … 语法语法:

Alt


代码运用 打印九九乘法表

j = 0
while j < 9:
    j += 1  # j = 3;
    i = 0  # i=0;
    while i < j:
        i += 1  # i=2;
        print(i, '*', j, '=', (i * j), sep="", end="\t")
    print()

在这里插入图片描述

for i in range(1, 10):
    for e in range(1, i + 1):
        print(i, '*', e, "=", int(i) * int(e), end="\t")
    print()

在这里插入图片描述

一行代码快速 打印九九乘法表

print('\n'.join([' '.join(['%s*%s=%-2s' % (y, x, x*y) for y in range(1, x+1)]) for x in range(1, 10)]))

在这里插入图片描述

跳出语句 break continue 使用

跳转语句能够改变程序的执行顺序,包括break、continue和return。
breakcontinue用于循环体中,而return用于函数中。

break:用来结束整个循环,强行退出循环体,不再执行循环体中剩余的语句。

continue:用来结束本轮循环,开启下一轮循环,直到循环完成

在这里插入图片描述

容器类型的数据

Python内置的数据类型如序列(列表、元组等)、集合和字典等可以容纳多项数据,我们称它们为容器类型的数据
序列包括列表(list)、字符串(str)、元组(tuple)和字节序列(bytes)等
序列中的元素都是有序的,每一个元素都带有序号,这个序号叫作索引。索引有正值索引和负值索引之分。

字符串

如果要使用字符串表示一篇文章,其中包含了换行、缩进等排版字符,则可以使用长字符串表示。对于长字符串,要使用三个单引号(‘’')或三个双引号(""")括起来。

在这里插入图片描述

# 在计算机里,下标都是从 0 开始的。-1最后一个
# 字符串是不可变的数据类型
# 对于字符串的任何操作,都不会改变原有的字符串!!!
word = 'Hello'  # 字符串:一个一个的字符串在一起
# 可以通过下标来获取或者修改指定位置的数据
print(word[4])  # o
print(word[-1])  # o



print(word[30])
# .若索引超过范围,则会发生IndexError错误
# Traceback (most recent call last):
#   File "....py", line 9, in <module>
#     print(word[30])
#           ~~~~^^^^
# IndexError: string index out of range

加和乘操作

加(+)和乘()运算符也可以用于序列中的元素操作。加(+)运算符可以将两个序列连接起来,乘()运算符可以将两个序列重复多次

a = "Hello"
b = "World"

print(a * 2 )  # HelloHello
print(a +" "+ b )  # Hello World

切片操作

切片 语法:str[start:end:step]

 start是开始索引
 end是结束索引,
 step是步长(切片时获取的元素的间隔,可以为正整数,也可以为负整数。步长不能为0
     理解为间隔。每隔 step-1 个取一次, step 为负数,表示从右往左获取)

注意:切下的小切片包括start位置的元素,但不包括end位置的元素,start和end都可以省略。

m = 'zxcvbnm'
print(m[5])  # m[index] ==> 获取指定下标上的数据 打印n

# 切片语法  m[start:end:step]
# 包含start,不包含end
# step 指的是步长,理解为间隔。每隔 step-1 个取一次
# step 为负数,表示从右往左获取
print(m[2:5])  # 包含start,不包含end   打印cvb
print(m[2:])  # 如果只设置了start,会"截取"到最后  打印cvbnm
print(m[:5])  # 如果值设置了end,会从头开始"截取"  打印zxcvb

# 步长默认为 1
print(m[1:5:2])  # xv
print(m[1:6:2])  # xvn
print(m[1:5:1])  # xcvb
# print(m[3:15:0])  # 步长不能为0
print('------------------')
print(m[5:1:-1])  # nbvc
print(m[::])  # zxcvbnm 从头到尾复制
print(m[::-1])  # mnbvcxz 从尾到头倒叙

# start和end如果是负数,表示从右边数
print(m[-5:-2])  # cvb
字符串常见的操作

max() 用于返回最后一个元素
min() 用于返回第一个元素
len() 获取长度

str—a指定检索的字符串
beg—开始检索,默认为0
end—结束检索,默认为字符串的长度

  • 可以获取指定字符的下标 带有 r 的从右边开始查找 否则左边查找 查到第一个就停止
    find(str,start,end)
    index(str,start,end)
    rfind(str,start,end)
    rindex(str,start,end)

判断

  • startswith(str, start) 是否以指定的子字符串开始

  • endswith(str, start) 是否以指定的子字符串结束

  • isalpha() 用于判断字符串中是否只含有字母。

  • isdigit() 用来判断字符串是否全部是数字

  • isalnum() 判断是否由数字和字母组成

  • isspace() 判断字符串是否只含有空字符

    • 返回结果为True的两个必要条件
      ①字符串至少含有一个字符
      ②字符串是空字符,空字符可为可为空格(’ ‘)、横向制表符(’\t’)、回车符(‘\r’)、换行(‘\n’)、换页(‘\f’)等其他情况返回结果为False

替换内容

  • replace(old ,new)用来替换字符串,把old替换成 new

计算出现次数

  • count(搜索的子字符串, start, end )
    • start — 字符串开始搜索的位置,默认为第一个字符,第一个字符索引值为0
    • end — 字符串中结束搜索的位置,字符中第一个字符的索引为0,。默认为字符串的最后一个位置。
    • 返回值 — 返回子字符串在字符串中出现的次数

切割字符串

  • split(str, num) 从左向右寻找
    • str :(可选)指定分隔符,默认为空字符
      num :(可选)分割次数,默认 -1,即分割所有
      不给参数时,「默认」以「空字符串」作为分隔符
      分隔符必须是「字符串类型」或者不指定,否则会报错 TypeError: must be str or None
  • rsplit(str, num) 从右向左寻找 同上
  • splitlines()
    • 按照行(‘\r’, ‘\r\n’,‘ \n’)分隔,返回一个包含各行作为元素的列表,不用传参
      如果是‘\n\r’,则会识别成一个空元素
  • partition() 从左往右找分隔符
  • rpartition() 从右往左找分隔符

修改⼤⼩写

  • capitalize() 第⼀个单词的⾸字⺟⼤写。
    -title() 每个单词的⾸字⺟⼤写。
    -upper() 所有都变成⼤写
    -lower() 所有都变成⼩写。

空格处理

  • ljust() 返回指定⻓度的字符串,并在右侧使⽤空⽩字符补全(左对⻬)。
    -rjust() 返回指定⻓度的字符串,并在左侧使⽤空⽩字符补全(右对⻬)
    -center() 返回指定⻓度的字符串,并在两端使⽤空⽩字符补全(居中对⻬)
    -lstrip() 删除左边的空⽩字符
    -rstrip() 删除右边的空⽩字符。
    -strip() 删除两边的空⽩字符。

字符串拼接

  • 字符.join(参数) 把参数进⾏遍历,取出参数⾥的每⼀项,然后再在后⾯加上字符,最后一个不添加
word = 'Hello' 
# max()函数用于返回最后一个元素
# min()函数用于返回第一个元素
# len()函数获取长度
print(max(word))   # o
print(min(word))  # H
print(len(word))  # 5
x = 'abcdefghijklmndsfasdfsadfadl'

# 查找内容相关的方法  find/index/rfind/rindex  可以获取指定字符的下标
print(x.find('l'))  # 11
print(x.index('l'))  # 11

print(x.find('p'))  # -1 如果字符在字符串里不存在,结果是 -1
# print(x.index('p'))  # 使用index,如果字符不存在,会报错

print(x.find('l', 4, 9))  # -1

print(x.rfind('l'))  # 27
print(x.rindex('l'))# 27

# startswith,endswith,isalpha,isdigit,isalnum,isspace
# is开头的是判断,结果是一个布尔类型
print('hello'.startswith('he'))  # True
print('hello'.endswith('o'))  # True
print('he45llo'.isalpha())  # False  alpha字母
print('good'.isdigit())  # False
print('123'.isdigit())  # True
print('⁴'.isdigit())  # True
print('123⁴'.isdigit())  # True
print('3.14'.isdigit())  # False
print('⑴'.isdigit()) # False
print('(1)'.isdigit()) # False
print('+1'.isdigit()) # False
print('-1'.isdigit()) # False

#「bytes」也是字符串的一种类型,它也可以使用 isdigit() ,并在纯数字的时候返回 True
byte1 = b'123'
print(type(byte1))  # <class 'bytes'>
print(byte1.isdigit()) # True
print(b'abc'.isdigit()) # False

# alnum 判断是否由数字和字母组成
print('ab12hello'.isalnum())  # True
print('hello'.isalnum())  # True
print('1234'.isalnum())  # True
print('4 - 1'.isalnum())  # False

print('h    o'.isspace())  # False


# replace方法:用来替换字符串
word = 'hello'
m = word.replace('l', 'x')  # replace 将字符串里 l 替换成 x
print(word)  # hello  字符串是不可变数据类型!!!
print(m)  # hexxo   原来的字符串不会改变,而是生成一个新的字符串来保存替换后的结果

# 切割字符串
a = "dlrblist"
a1 = a.split("l", 1)
print(a1) # ['d', 'rblist']


b = "dlrblist"
b1 = b.rsplit("l", 1)
print(b1) # ['dlrb', 'ist']

c = "hello\nworld\r\ndlrb\r你好\n\r世界"
c1 = c.splitlines()
print(c1)  # ['hello', 'world', 'dlrb', '你好', '', '世界']


p= "www.ilovefishc.com"
print(p.partition("."))  # ('www', '.', 'ilovefishc.com')
print(p.rpartition("."))  # ('www.ilovefishc', '.', 'com')
# 修改大小写

# capitalize 让第一个单词的首字母大写
print('hello world.good morning\nyes'.capitalize())
		# Hello world.good morning
		# yes
		
# upper 全大写
print('hello'.upper())  # HELLO

# lower 全小写
print('WoRLd'.lower())  # 面向对象里,我们称之为方法   world

# title 每个单词的首字母大写
print('good morning'.title())    # Good Morning

# 空格处理
# ljust(width,fillchar)
# width 长度   fillchar 填充字符,默认是空格
# 让字符串以指定长度显示,如果长度不够,默认在右边使用空格补齐
print('Monday'.ljust(10, '+'))   # Monday++++
print('Tuesday'.rjust(12, '-'))   # -----Tuesday
print('Wednesday'.center(20, '*'))  # *****Wednesday******

print('+++++apple+++'.lstrip('+'))  # apple+++
print('    pear     '.rstrip())  #     pear
print('    banana     '.strip())  # banana

# 以某种固定格式显示的字符串,我们可以将它切割成为一个列表
x = 'zhangsan+lisi+wangwu+jack+tony+henry+chris'
names = x.split('+')
print(names)  # ['zhangsan', 'lisi', 'wangwu', 'jack', 'tony', 'henry', 'chris']

# 将列表转换成为字符串
fruits = ['apple', 'pear', 'peach', 'banana', 'orange', 'grape']
print('-'.join(fruits))  # apple-pear-peach-banana-orange-grape
print('*'.join('hello'))  # iterable可迭代对象  h*e*l*l*o
print('+'.join(('yes', 'ok')))  # yes+ok

# 字符串的运算符
# 字符串和字符串之间可以使用加法运算,作用是拼接两个字符串
# 字符串和数字之间可以使用乘法运算,目的是将指定的字符串重复多次
# 字符串和数字之间做 == 运算结果是False,做 != 运算,结果是True
# 字符串之间做比较运算,会逐个比较字符串的编码值
# 不支持其他的运算符
# count
a = "balala woo gaga"
print(a.count("a", 2))  # 4

print(a.count("a",2,7)) # 2
# 字符串拼接join
mystr = 'a'
print(mystr.join('hxmdq')) #haxamadaq 把hxmd⼀个个取出,并在后⾯添加字符a. 最后的 q 保留,没有加 a
print(mystr.join(['hi','hello','good'])) #hiahelloagood 作⽤:可以把列表或者元组快速的转变成为字符串,并且以指定的字符分隔。
txt = '_'
print(txt.join(['hi','hello','good'])) #hi_hello_good
print(txt.join(('good','hi','hello'))) #good_hi_hell
成员测试

成员测试运算符有两个:in和not in

 in用于测试是否包含某一个元素
 not in用于测试是否不包含某一个元素
a = "hello"
print("e" in a)  # True
print("e" not in a)  # False
常用的转义符
字符表示Unicode编码说明
\t\u0009水平制表符
\n\u000a换行
\r\U000d回车
\"\u0022双引号
\'\u0027单引号
\\u005c反斜线
r"内容"原始字符串中的\n 表示\和n 两个字符

列表

列表(list)是一种可变序列类型,我们可以追加、插入、删除和替换列表中的元素。
使用 [ ] 来表示一个列表,列表里的每一个数据我们称之为元素
元素之间使用逗号进行分割
和字符串一样,都可以使用下标来获取元素和对元素进行切片
同时,我们还可以使用下标来修改列表里的元素
也可以通过下标来实现切片

创建列表

创建列表有两种方法。

1、 list(iterable)函数:参数iterable是可迭代对象(字符串、列表、 元组、集合和字典等)。
2、 [元素1,元素2,元素3,⋯]:指定具体的列表元素,元素之间以 逗号分隔,列表元素需要使用中括号括起来

添加元素

添加元素的方法

 append()   在列表中追加单个元素,在列表的最后面追加一个数据
 
 extend()  在列表中追加多个元素
 使用 +=  在列表中追加多个元素
           
 insert (index,object)   插入元素
         index 表示下标,在哪个位置插入数据
         object 表示对象,具体插入哪个数据
heros= ['阿珂', '嬴政', '韩信', '露娜', '后羿', '亚瑟', '李元芳']
heros1= ['阿珂', '嬴政', '韩信', '露娜', '后羿', '亚瑟', '李元芳']
heros2= ['阿珂', '嬴政', '韩信', '露娜', '后羿', '亚瑟', '李元芳']
heros3= ['阿珂', '嬴政', '韩信', '露娜']
heros4= ['后羿', '亚瑟', '李元芳']
# 
heros.append('黄忠') 
print(heros)  #  ['阿珂', '嬴政', '韩信', '露娜', '后羿', '亚瑟', '李元芳', '黄忠']

# insert(index,object)  需要两个参数
# index 表示下标,在哪个位置插入数据
# object 表示对象,具体插入哪个数据
heros1.insert(3, '李白') # ['阿珂', '嬴政', '韩信', '李白', '露娜', '后羿', '亚瑟', '李元芳']
print(heros1)

x = ['马可波罗', '米莱迪', '狄仁杰']
# extend(iterable)  需要一个可迭代对象
# A.extend(B) ==> 将可迭代对象 B 添加到 A 里
heros2.extend(x)
print(heros2) # ['阿珂', '嬴政', '韩信', '露娜', '后羿', '亚瑟', '李元芳', '马可波罗', '米莱迪', '狄仁杰']
print(x) # ['马可波罗', '米莱迪', '狄仁杰']

heros3 += heros4
print(heros3) # ['阿珂', '嬴政', '韩信', '露娜', '后羿', '亚瑟', '李元芳']
替换 修改 元素

想替换列表中的元素时,将列表下标索引元素放在赋值符号(=) 的左边,进行赋值即可。

x = ['马可波罗', '米莱迪', '狄仁杰', '李白']
x[2] = '露娜'	
print(x) # ['马可波罗', '米莱迪', '露娜', '李白']
列表删除和查询元素

删除数据有三个相关的方法

 pop()   默认会删除列表里最后一个数据,并且返回这个数据  ,
         还可以传入index参数,用来删除指定位置上的数据 
         
 remove() 用来删除指定的元素
          如果找到匹配的元素x,则删除该元素,
          如果找到多个匹配的元素,则只删除第一个匹配的元素。
          如果数据在列表中不存在,会报错
 
 使用del 也可以删除一个指定数据,或者删除整个元素(毁尸灭迹)
          
 clear() 用来清空一个列表(还可以找到只是个空数据)
masters = ['王昭君', '甄姬', '貂蝉', '妲己', '小乔', '大乔']


x = masters.pop(3)
print(masters)  # ['王昭君', '甄姬', '貂蝉', '小乔', '大乔']

# remove用来删除指定的元素
masters.remove('小乔')
# masters.remove('妲己')  
print(masters) # ['王昭君', '甄姬', '貂蝉', '大乔']

# 使用del 也可以删除一个数据
del masters[2]
print(masters) # ['王昭君', '甄姬', '大乔']

# clear 用来清空一个列表
masters.clear()
print(masters)

# a = 100
# del a
# print(a)
# 会报错
# Traceback (most recent call last):
# File "…….py", line 24, in <module>
# print(a)
#       ^
# NameError: name 'a' is not defined


tanks = ['亚瑟', '程咬金', '盾山', '张飞', '廉颇', '程咬金']
# 查询相关的方法
print(tanks.index('盾山'))  # 2
# print(tanks.index('庄周'))  如果元素不存在,会报错
print(tanks.count('程咬金'))  # 2
# in 运算符
print('张飞' in tanks)  # True
print('苏烈' in tanks)  # False


遍历 冒泡排序

将所欲的数据都访问一遍。遍历针对的是可迭代对象

# while循环遍历  / for...in 循环遍历
killers = ['李白', '兰陵王', '韩信', '赵云', '阿珂', '孙悟空']

# for...in循环的本质就是不断的调用迭代器的 next 方法查找下一个数据
for k in killers:
    print(k)

i = 0
while i < len(killers):
    print(killers[i])
    i += 1
nums = [1, 2, 3, 4, 5, 6, 7, 9, 8]

count = 0
j = 0
# 第一趟比较时, j=0,多比价了0次
# 第二趟比较时, j=1,多比较了1次
# 第三趟比较时, j=2,多比价了2次
while j < len(nums) - 1:

    # 在每一趟里都定义一个flag
    flag = True  # 假设每一趟都没有换行
    i = 0
    while i < len(nums) - 1 - j:
        count += 1
        if nums[i] > nums[i + 1]:
            # 只要交换了,假设就不成立
            flag = False
            nums[i], nums[i + 1] = nums[i + 1], nums[i]
        i += 1
    if flag:
        # 这一趟走完以后,flag依然是True,说明这一趟没有进行过数据交换
        break
    j += 1

print(nums)   # [1, 2, 3, 4, 5, 6, 7, 8, 9]

print('比较了%d次' % count)   # 比较了15次
排序
	nums1 = [6, 5, 3, 1, 8, 7, 2, 4]
	nums2 = [6, 5, 3, 1, 8, 7, 2, 4]
	
	# 调用列表的 sort 方法可以直接对列表进行排序
	# 直接对原有的列表进行排序
	
	nums3 = nums1
	nums1.sort()
	nums3.sort(reverse=True)
	print(nums1)  # [1, 2, 3, 4, 5, 6, 7, 8]
	print(nums3)  # [8, 7, 6, 5, 4, 3, 2, 1]
	
	# 内置函数sorted,不会改变原有的列表数据,会生成一个新的有序数据
	x = sorted(nums2)
	print(nums2)  # [6, 5, 3, 1, 8, 7, 2, 4]
	print(x)      # [1, 2, 3, 4, 5, 6, 7, 8]
列表随机 嵌套 与创建
import random

# 一个学校,有3个办公室,现在有10位老师等待工位的分配,请编写程序,完成随机的分配
teachers = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
rooms = [[], [], []]

for teacher in teachers:
    room = random.choice(rooms)  # choice 从列表里随机选择一个数据
    room.append(teacher)

print(rooms)
# 第0个房间有3个人,分别是...


# 带下标我们一般都使用while
# for循环也可以带下标
for i, room in enumerate(rooms):
    print('房间%d里一共有%d个老师,分别是:' % (i, len(room)),end='')
    for teacher in room:
        print(teacher, end=' ')
    print()

# 列表推导式作用是使用简单的语法创建一个列表
nums = [i for i in range(10)]
print(nums)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

x = [i for i in range(10) if i % 2]
print(x)  # [1, 3, 5, 7, 9]

# points 是一个列表。这个列表里的元素都是元组
points = [(x, y) for x in range(5, 7) for y in range(10, 12)]
print(points)   # [(5, 10), (5, 11), (6, 10), (6, 11)]


# 了解即可
# 请写出一段 Python 代码实现分组一个 list 里面的元素,比如 [1,2,3,...100]变成 [[1,2,3],[4,5,6]....]
m = [i for i in range(1, 21)]
print(m)     # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# m[0:3]  j ==> 0
# m[3:6]  j ==> 3
#         j ==> 6

n = [m[j:j + 3] for j in range(0, 20, 3)]
print(n)    # [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
   
拷贝 复制

浅拷贝

 赋值
 自带copy()方法,
 引入  import copy    copy.copy() 

深拷贝

 引入  import copy    copy.deepcopy() 

1、通过直接赋值的方式

  •  old_list = [1, 2, 3]
     new_list = old_list
     print(id(old_list), id(new_list))  # 1530306843208 1530306843208
     
     old_list.append(6)
     print(old_list, new_list)   # [1, 2, 3, 6] [1, 2, 3, 6]
     print(id(old_list), id(new_list)) #  1530306843208 1530306843208
     
     # 可见结果是完全一样的(包括值和地址)
    

2、浅拷贝:使用copy()
调用copy方法,可以复制一个列表
这个新列表和原有的列表内容一样,但是指向不同的内存空间

  •  old_list = [1, 2, 3]
    new_list2 = old_list.copy()
    print(old_list, new_list2)   # [1, 2, 3] [1, 2, 3]
    print(id(old_list), id(new_list2))   # 2089174717000 2089174717512
    
    old_list.append(6)
    new_list2.append("浅拷贝")
    print(old_list, new_list2)   # [1, 2, 3, 6] [1, 2, 3, '浅拷贝']
    print(id(old_list), id(new_list2))   # 2089174717000 2089174717512
    
    # 可以看出只有最开始的两个列表的值是一样的,其他没有什么联系,改变了某一个列表的值也不会对另一个列表产生影响(浅拷贝)再看另一个list包含另一个list的情况:
    
  • old_list = [1, 2, 3, [4, 5]]
    new_list2 = old_list.copy()
    print(old_list, new_list2)  # [1, 2, 3, [4, 5]] [1, 2, 3, [4, 5]]
    print(id(old_list), id(new_list2))  # 2722822378568 2722822943112
     
    old_list[3][0] = 5
    print(old_list, new_list2)  # [1, 2, 3, [5, 5]] [1, 2, 3, [5, 5]]
    print(id(old_list), id(new_list2))   # 2722822378568 2722822943112
    
    # 替换列表中的第四位元素中的第一个元素,可以看到地址都没有变化,但是值都变了,原因是浅拷贝只是拷贝了第四个元素,而不管里面的值。
    

3、深拷贝:使用copy库中的deepcopy()
包含对象里面的子对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变

  •  import copy
      
     old_list = [1, 2, 3, [4, 5]]
     new_list3 = copy.deepcopy(old_list)
     print(old_list, new_list3)   # [1, 2, 3, [4, 5]] [1, 2, 3, [4, 5]]
     print(id(old_list), id(new_list3))    # 2145661532104 2145661482312
      
     old_list.append(6)
     print(old_list, new_list3)    # [1, 2, 3, [4, 5], 6] [1, 2, 3, [4, 5]]
     print(id(old_list), id(new_list3))    # 2145661532104 2145661482312
    
    #该部分和浅拷贝一样,因为都对第一层的对象做了拷贝
    
  •  import copy
      
     old_list = [1, 2, 3, [4, 5]]
     new_list3 = copy.deepcopy(old_list)
     print(old_list, new_list3)   # [1, 2, 3, [4, 5]] [1, 2, 3, [4, 5]]
     print(id(old_list), id(new_list3))    # 1208770731400 1208770803784
      
    old_list[3][0] = 5
     print(old_list, new_list3)    # [1, 2, 3, [5, 5]] [1, 2, 3, [4, 5]]
     print(id(old_list), id(new_list3))    # 1208770731400 1208770803784
    
    #该部分和浅拷贝一样,因为都对第一层的对象做了拷贝
    

交换两个变量的值

 a = 13
b = 20

# 方法一:使用第三个变量实现
# c = b
# b = a
# a = c

# 方法二:使用运算符来实现.只能是数字
# a = a + b
# b = a - b
# a = a - b

# 方法三:使用异或运算符
# a = a ^ b
# b = a ^ b
# a = a ^ b

# 方法四:使用Python特有
a, b = b, a

print(a)  # 20
print(b)  # 13

元组

元组和列表很像,都是用来保存多个数据
元素之间以逗号分隔。使用一对小括号 () 来表示一个元组,也可以省略小括号。
元组和列表的区别在于,列表是可变的,而元组是不可变数据类型
如果元组里只有一个元素,要在最后面加 ,
创建元组时有两种方法。

 tuple(iterable)函数:参数iterable是可迭代对象(字符串、列表、元组、集合和字典等)。
(元素1,元素2,元素3,⋯):指定具体的元组元素,元素之间以逗号分隔。对于元组元素,可以使用小括号括起来,也可以省略小括号。

words = ['hello', 'yes', 'good', 'hi']  # 列表,使用 [] 表示
nums = (9, 4, 3, 1, 9, 7, 6, 9, 3, 9)  # 元组,使用 () 来表示
nums1 = 9, 4, 3, 1, 9, 7, 6, 9, 3, 9  # 元组,也可以省略小括号
print(type(nums))  # <class 'tuple'>
print(type(nums1))  # <class 'tuple'>

# 和列表一样,也是一个有序的存储数据的容器
# 可以通过下标来获取元素
print(nums[3]) # 1
# nums[3] = 40  # 元组是不可变数据类型,不能修改
print(nums.index(7)) # 5
print(nums.count(9))  # 4

# 特殊情况:如何表示只有一个元素的元组?
# ages = (18)  # 这种书写方式,ages是一个整数,并不是一个元组
ages1 = (18)  # 这种书写方式,ages是一个整数,并不是一个元组
ages2 = (18,)  # 如果元组里只有一个元素,要在最后面加 ,
print(type(ages1))  # <class 'int'>
print(type(ages2))  # <class 'tuple'>

# tuple 内置类
# print(tuple(18))
print(tuple('hello'))  # ('h', 'e', 'l', 'l', 'o')

# 怎样把列表转换成为元组?元组转换成为列表?
print(tuple(words))  # tuple list set 都是这样使用的  ('hello', 'yes', 'good', 'hi')
print(list(nums1))  # [9, 4, 3, 1, 9, 7, 6, 9, 3, 9]

heights = ("189", "174", "170")
print('*'.join(heights))   # 189*174*170
print("".join(('h', 'e', 'l', 'l', 'o')))  # hello

# 元组也可以遍历
for i in nums:
    print(i)

j = 0
while j < len(nums):
    print(nums[j])
    j += 1

集合

创建集合

集合(set)是一种可迭代的、无序的、不能包含重复元素的容器类型的数据。

两种方式创建集合:

set(iterable)函数:参数iterable是可迭代对象(字符串、列表、元组、集合和字典等)。   
{元素1,元素2,元素3,⋯}:指定具体的集合元素,元素之间以逗号分隔。对于集合元素,需要使用大括号括起来。
z = set("hello")
a = {10, 5, 7, 5, 4, 3}
b = {}

print(z)  # {'o', 'h', 'l', 'e'}
print(a)   # {3, 4, 5, 7, 10}
print(type(b))    # <class 'dict'>
修改集合

add(elem):添加元素,如果元素已经存在,则不能添加,不会 抛出错误。
remove(elem):删除元素,如果元素不存在,则抛出错误。
clear():清除集合。

# 无序  每次打印都会不一样
ames = {'lisi', 'zhangsan', 'tony', 'jack'}
print(names)   # {'zhangsan', 'lisi', 'jack', 'tony'}
# set 进行增删改查
names.add('阿珂')  # 添加一个元素  无序 所以可能添加到任何地方,每次打印都会不一样
print(names) # {'zhangsan', 'jack', '阿珂', 'tony', 'lisi'}

names.pop()  # 删除一个  无序 随机删除每次的第一个
print(names)  # {'jack', '阿珂', 'tony', 'lisi'}

names.remove('jack')  # 删除一个指定的元素 指定可以删除任何位置
print(names)  # {'阿珂', 'tony', 'lisi'}

print('jack' in names)  #  False


# union 将多个集合合并生成一个新的集合
# A.update(B) 将B拼接到A里
names.update(['刘能', '赵四'])
print(names)  # {'阿珂', '赵四', 'tony', 'lisi', '刘能'}

# 空集合的表示方式不是 {} , {} 表示的是空字典
# 空集合 set()
names.clear()  # 清空一个集合
print(names)  # set() 
使用 运算符
first = {'李白', '白居易', '李清照', '杜甫', '王昌龄', '王维', '孟浩然', '王安石'}
second = {'李商隐', '杜甫', '李白', '白居易', '岑参', '王昌龄'}

# set 支持很多算数运算符
print(first - second)  # A - B 求A和B的 差集
print(second - first)
print(first & second)  # A & B 求A和B的 交集
print(first | second)  # A | B 求A和B的 并集
print(first ^ second)  # A ^ B 求A和B差集的并集
# print(first + second)  #  会报错



# {'孟浩然', '王维', '王安石', '李清照'}
# {'岑参', '李商隐'}
# {'白居易', '李白', '杜甫', '王昌龄'}
# {'岑参', '白居易', '孟浩然', '王安石', '李清照', '王昌龄', '李商隐', '王维', '李白', '杜甫'}
# {'岑参', '李清照', '李商隐', '王维', '孟浩然', '王安石'}


# print(first + second)  #  会报错
去重排序
nums = [5, 8, 7, 6, 4, 1, 3, 5, 1, 8, 4]

# 去重
x = set(nums)
y = list(x)
# 排序
y.sort(reverse=True)
print(y)  # [8, 7, 6, 5, 4, 3, 1]

字典

字典不仅可以保存值,还能对值进行描述
使用大括号来表示一个字典,不仅有值value,还有值的描述key
字典里的数据都是以键值对key:value的形式保留的 key和value之间使用冒号 :
连接 多个键值对之间使用逗号 , 来分割
字典里的key不允许重复,如果key重复了,后一个key对应的值会覆盖前一个
字典里的value可以是任意数据类型,但是key只能使用不可变数据类型,一般使用字符串
字典的数据在保存时,是无序的,不能通过下标来获取
使用字典的get方法,如果key不存在,会默认返回 None,而不报错

创建字典

创建字典。
1 dict()函数。
2 {key1:value1,key2:value2,…,key_n:value_n}:指定具体的字典键值对,键值对之间以逗号分隔,最后用大括号括起来。

增删改查

items():返回字典的所有键值对。
keys():返回字典键
values(): 返回字典值

  •  person = {'name': 'zhangsan', 'age': 18, 'sex': 'boy'}
     
     print(person.items())
     # 返回字典的所有键值对
     # dict_items([('name', 'zhangsan'), ('age', 18), ('sex', 'boy')])
     
     print( list ( person.items() ) )
     # dict_items可以使用list()函数返回键值对列表
     # [('name', 'zhangsan'), ('age', 18), ('sex', 'boy')]
     
     print(person.keys())
     # 返回字典键  dict_keys
     # dict_keys(['name', 'age', 'sex'])
     
     print( list( person.keys() ) )
     # dict_keys可以使用list()函数返回键列表
     # ['name', 'age', 'sex']
     
     print(person.values())
     # 返回字典值  dict_values
     # dict_values(['zhangsan', 18, 'boy'])
     
     print( list( person.values() ) )
     # ddict_values可以使用list()函数返回值列表
     # ['zhangsan', 18, 'boy']
    

使用key获取到对应的value
使用字典的get()方法,如果key不存在,会默认返回 None,而不报错
get()方法如果根据key获取不到value,可以给定默认值

  •  person = {'name': 'zhangsan', 'age': 18, 'x': 'y'}
     
     # 查找数据(字典的数据在保存时,是无序的,不能通过下标来获取)
     print(person['name'])  # 使用key获取到对应的value   zhangsan
     # print(person['height'])  # 如果要查找的key不存在,会直接报错
     
     # 需求:获取一个不存在的key时,不报错,如果这个key不存在,使用默认值
     # 使用字典的get方法,如果key不存在,会默认返回 None,而不报错
     print(person.get('height'))  # None
     # 如果根据key获取不到value,使用给定的默认值
     print(person.get('gender', 'female')) # female
     print(person.get('name', 'lisi'))  # zhangsan
     
     # 不会修改原数据
     print(person) # {'name': 'zhangsan', 'age': 18, 'x': 'y'}
    

直接使用key可以修改对应的value
如果key存在,是修改key对应的value;如果key在字典里不存在,会往字典里添加一个新的key:value
pop() : 把 键名 对应的键值对删除了,执行结果是被删除的value
popitem() : 删除一个元素,结果是被删除的这个元素组成的键值对
del … : del 字典[" 键名"] 把 键名 对应的键值对删除了
clear() : 用来清空一个字典
update() 方法 可以将两个字典合并成为一个字典
字典之间不支持加法运算

  •  person = {'name': 'zhangsan', 'age': 18, 'addr': '襄阳'}
     
     # 直接使用key可以修改对应的value
     person['name'] = 'lisi' 
     print(person)   # {'name': 'lisi', 'age': 18, 'addr': '襄阳'}
     
     # 如果key存在,是修改key对应的value;
     # 如果key在字典里不存在,会往字典里添加一个新的key-value
     person['gender'] = 'female'
     print(person)  # {'name': 'lisi', 'age': 18, 'addr': '襄阳', 'gender': 'female'}
     
     # pop()把name对应的键值对删除了,执行结果是被删除的value
     x = person.pop('name')
     print(x)  # lisi
     print(person) # {'age': 18, 'addr': '襄阳', 'gender': 'female'}
     
     # popitem 删除一个元素,结果是被删除的这个元素组成的键值对
     result = person.popitem()
     print(result)  # ('gender', 'female')
     print(person) # {'age': 18, 'addr': '襄阳'}
     
     # del ... 
     del person['age']
     print(person) # {'addr': '襄阳'}
     
     person.clear()  # 用来清空一个字典
     print(person)  # {}
     
     
     person1 = {'name': 'zhangsan', 'age': 18}
     person2 = {'addr': '襄阳', 'height': 180}
     person1.update(person2)
     print(person1) # {'name': 'zhangsan', 'age': 18, 'addr': '襄阳', 'height': 180}
     
     # 字典之间不支持加法运算
     # print(person1 + person2)
    
遍历字典
person = {'name': 'zhangsan', 'age': 18, 'height': '180cm'}

# 特殊在列表和元组是一个单一的数据,但是字典是键值对的形式

# 第一种遍历方式: 直接for...in循环字典
for x in person:  # for...in循环获取的是key
    print(x, '=', person[x])
# name = zhangsan
# age = 18
# height = 180cm


# 第二种方式:获取到所有的key,然后再遍历key,根据key获取value
# print(person.keys())  # dict_keys(['name', 'age', 'height'])
for k in person.keys():
    print(k, '=', person[k])
# name = zhangsan
# age = 18
# height = 180cm

# 第三种方式:获取到所有的value.
# 只能拿到值,不能拿到key
for v in person.values():
    print(v)
# zhangsan
# 18
# 180cm


# 第四种遍历方式:
# print(person.items())  # dict_item([('name', 'zhangsan'), ('age', 18), ('height', '180cm')])

# for item in person.items():  # 列表里的元素是元组,把元组当做整体进行遍历
#     print(item[0], '=', item[1])

for k, v in person.items():
    print(k, '=', v)
# name = zhangsan
# age = 18
# height = 180cm
练习
students = [
    {'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'},
    {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'},
    {'name': '王五', 'age': 21, 'score': 95, 'tel': '1365588889', 'gender': 'unknown'},
    {'name': 'jerry', 'age': 20, 'score': 90, 'tel': '156666789', 'gender': 'unknown'},
    {'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'},
    {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'},
    {'name': 'tony', 'age': 15, 'score': 98, 'tel': '1388888888', 'gender': 'unknown'}
]

# (1) 统计不及格学生的个数
# (2) 打印不及格学生的名字和对应的成绩
# (3) 统计未成年学生的个数
# (4) 打印手机尾号是8的学生的名字
# (5) 打印最高分和对应的学生的名字
count = 0
teenager_count = 0
max_score = students[0]['score']  # 假设第0个学生的成绩是最高分
# max_index = 0  # 假设最高分的学生下标是 0
for i, student in enumerate(students):
    if student['score'] < 60:
        count += 1
        print('%s不及格,分数是%d' % (student['name'], student['score']))
    if student['age'] < 18:
        teenager_count += 1
    # if student['tel'].endswith('8'):
    if student['tel'][-1] == '8':
        print('%s的手机号以8结尾' % student['name'])

    if student['score'] > max_score:  # 遍历时,发现了一个学生的成绩大于假设的最大数
        max_score = student['score']
        # max_index = i  # 修改最高分的同时,把最高分的下标也修改

print('不及格的学生有%d个' % count)
print('未成年的学生有%d个' % teenager_count)
print('最高成绩是%d' % max_score)

for student in students:
    if student['score'] == max_score:
        print('最高分是%s' % student['name'])

# print('最高分名字是%s' % students[max_index]['name'])


# (6) 删除性别不明的所有学生(这个地方有个坑,跳不出来的话大家可以在群里套路,或者等老师的解答)
# 方法一,将不需要删除的数据添加到新列表
new_students = [x for x in students if x['gender'] != 'unknown']
print(new_students)

# 方法二,使用for循环倒着删除要删除的数据,避免“坑”
i = 0
for i in range(len(students) - 1, -1, -1):
    if students[i]['gender'] == 'unknown':
        students.remove(students[i])
print(students)

# 方法三,使用while循环删除需要删除的数据,并及时补齐因删除数据而导致的列表数据索引变化,避免漏删数据
i = 0
while i < len(students):
    if students[i]['gender'] == 'unknown':
        students.remove(students[i])
        i -= 1
    i += 1
print(students)

# 方法四,遍历在新的列表操作,删除是在原来的列表操作(students[:]是studens的切片,所以修改students对切片无影响)
i = 0
for student in students[:]:
    if student['gender'] == 'unknown':
        students.remove(student)
print(students)

# 方法五,使用内建函数filter()和匿名函数
new_students = filter(lambda x: x['gender'] != 'unknown', students)
print(list(new_students))

print('-------------------------------')
# (7) 将列表按学生成绩从大到小排序(选做)
for j in range(0, len(students) - 1):
    for i in range(0, len(students) - 1 - j):
        if students[i]['score'] < students[i + 1]['score']:
            students[i], students[i + 1] = students[i + 1], students[i]
print(students)





# 张三不及格,分数是52
# 张三的手机号以8结尾
# jack不及格,分数是52
# jack的手机号以8结尾
# tony的手机号以8结尾
# 不及格的学生有2个
# 未成年的学生有2个
# 最高成绩是98
# 最高分是chris
# 最高分是tony
# [{'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'}, {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'}, {'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'}, {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'}]
# [{'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'}, {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'}, {'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'}, {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'}]
# [{'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'}, {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'}, {'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'}, {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'}]
# [{'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'}, {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'}, {'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'}, {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'}]
# [{'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'}, {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'}, {'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'}, {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'}]
# -------------------------------
# [{'name': 'chris', 'age': 17, 'score': 98, 'tel': '13777775523', 'gender': 'male'}, {'name': '李四', 'age': 28, 'score': 89, 'tel': '1388666666', 'gender': 'male'}, {'name': '张三', 'age': 18, 'score': 52, 'tel': '1388888998', 'gender': 'female'}, {'name': 'jack', 'age': 23, 'score': 52, 'tel': '13999999928', 'gender': 'female'}]

# 用三个元组表示三门学科的选课学生姓名(一个学生可以同时选多门课)

sing = ('李白', '白居易', '李清照', '杜甫', '王昌龄', '王维', '孟浩然', '王安石')
dance = ('李商隐', '杜甫', '李白', '白居易', '岑参', '王昌龄')
rap = ('李清照', '刘禹锡', '岑参', '王昌龄', '苏轼', '王维', '李白')
# (1) 求选课学生总共有多少人
# 元组之间支持加法运算
# 使用集合set可以去重
total = set(sing + dance + rap)
print(len(total))

# (2) 求只选了第一个学科的人的数量和对应的名字
sing_only = []
for p in sing:
    if p not in dance and p not in rap:
        sing_only.append(p)
print('只选择了第一个学科的有{}人,是{}'.format(len(sing_only), sing_only))

# (3) 求只选了一门学科的学生的数量和对应的名字
# (4) 求只选了两门学科的学生的数量和对应的名字
# (5) 求只选了三门学生的学生的数量和对应的名字

p_dict = {}  # 空字典
all_persons = sing + dance + rap
print(all_persons)
# ('李白', '白居易', '李清照', '杜甫', '王昌龄', '王维', '孟浩然', '王安石', '李商隐', '杜甫', '李白', '白居易', '岑参', '王昌龄', '李清照', '刘禹锡', '岑参', '王昌龄', '苏轼', '王维', '李白')
for name in all_persons:
    if name not in p_dict:
        p_dict[name] = all_persons.count(name)
print(p_dict)

for k, v in p_dict.items():
    if v == 1:
        print('报了一门的有', k)
    elif v == 2:
        print('报了两门的有', k)
    elif v == 3:
        print('报了三门的有', k)


# 12
# 只选择了第一个学科的有2人,是['孟浩然', '王安石']
# ('李白', '白居易', '李清照', '杜甫', '王昌龄', '王维', '孟浩然', '王安石', '李商隐', '杜甫', '李白', '白居易', '岑参', '王昌龄', '李清照', '刘禹锡', '岑参', '王昌龄', '苏轼', '王维', '李白')
# {'李白': 3, '白居易': 2, '李清照': 2, '杜甫': 2, '王昌龄': 3, '王维': 2, '孟浩然': 1, '王安石': 1, '李商隐': 1, '岑参': 2, '刘禹锡': 1, '苏轼': 1}
# 报了三门的有 李白
# 报了两门的有 白居易
# 报了两门的有 李清照
# 报了两门的有 杜甫
# 报了三门的有 王昌龄
# 报了两门的有 王维
# 报了一门的有 孟浩然
# 报了一门的有 王安石
# 报了一门的有 李商隐
# 报了两门的有 岑参
# 报了一门的有 刘禹锡
# 报了一门的有 苏轼

转换相关的方法

将字符串转换为数字,可以使用int()和float()实现,如果成功则返回数字,否则引发异常
将数字转换为字符串,可以使用str()函数,

Python里有一个比较强大的内置函数eval,可以执行字符串里的代码
import json

 json.dumps()把列表、元组、字典等转换成为JSON字符串
 json.loads()  将json字符串转换成为Python里的数据
PythonJSON
Truetrue
Falsefalse
字符串字符串
字典对象
列表、元组串数组
# 内置类  list  tuple  set
nums = [9, 8, 4, 3, 2, 1]
x = tuple(nums)  # 使用tuple内置类转换成为元组
print(x) # (9, 8, 4, 3, 2, 1)

y = set(nums)  # 使用set内置类转换成为集合
print(y) # {1, 2, 3, 4, 8, 9}

z = list({'name': 'zhangsan', 'age': 18, 'score': 98})
print(z) # ['name', 'age', 'score']

# Python里有一个比较强大的内置函数eval,可以执行字符串里的代码
a = 'input("请输入您的用户名")'  # a是一个字符串
b = '1+1'
print(eval(b)) # 2

import json

# JSON的使用,把列表、元组、字典等转换成为JSON字符串
person = {'name': 'zhangsan', 'age': 18, 'gender': 'female'}
# 字典如果想要把它传给前端页面或者把字典写入到一个文件里
m = json.dumps(person)  # dumps将字典、列表、集合、元组等转换成为JSON字符串
print(m)  # '{"name": "zhangsan", "age": 18, "gender": "female"}'
print(type(m))  # <class 'str'>
# print(m['name'])  不能这样使用,m是一个字符串,不能再像字典一样根据key获取value



print(json.dumps(['hello', 'good', 'yes', True])) # ["hello", "good", "yes", true]
print(json.dumps(('hello', 'good', 'yes', False))) # ["hello", "good", "yes", false]


n = '["hello","good"]'
p = eval(n)
print(type(p))  # <class 'list'>
s = json.loads(n)  # loads可以将json字符串转换成为Python里的数据
print(s)  # ['hello', 'good']
print(type(s)) # <class 'list'>
# + :可以用来拼接,用于 字符串、元组、列表
print('hello' + 'world')
print(('good', 'yes') + ('hi', 'ok'))
print([1, 2, 3] + [4, 5, 6])

# -:只能用户集合,求差集
print({1, 2, 3} - {3})

# *:可以用于字符串元组列表,表示重复多次。不能用于字典和集合
print('hello' * 3)
print([1, 2, 3] * 3)
print((1, 2, 3) * 3)

# in:成员运算符
print('a' in 'abc')
print(1 in [1, 2, 3])
print(4 in (6, 4, 5))

# in 用于字典是用来判断key是否存在
print('zhangsan' in {'name': 'zhangsan', 'age': 18, 'height': '180cm'})
print('name' in {'name': 'zhangsan', 'age': 18, 'height': '180cm'})
print(3 in {3, 4, 5})

# nums = [19, 82, 39, 12, 34, 58]
nums = (19, 82, 39, 12, 34, 58)
# nums = {19, 82, 39, 12, 34, 58}
# 带下标的遍历 enumerate 类的使用,一般用户列表和元组等有序的数据

for i, e in enumerate(nums):
    print('第%d个数据是%d' % (i, e))

person = {'name': 'zhangsan', 'age': 18, 'height': '180cm'}
for i, k in enumerate(person):
    print(i, k)



# helloworld
# ('good', 'yes', 'hi', 'ok')
# [1, 2, 3, 4, 5, 6]
# {1, 2}
# hellohellohello
# [1, 2, 3, 1, 2, 3, 1, 2, 3]
# (1, 2, 3, 1, 2, 3, 1, 2, 3)
# True
# True
# True
# False
# True
# True
# 第0个数据是19
# 第1个数据是82
# 第2个数据是39
# 第3个数据是12
# 第4个数据是34
# 第5个数据是58
# 0 name
# 1 age
# 2 height

内置函数

Python内置函数

函数名分类备注
bool()数据类型布尔型(True,False)
int ()数据类型整型(整数)
float()数据类型浮点型(小数)
complex ()数据类型浮点型(小数)
bin()进制转换将给的参数转换成二进制
oct()进制转换将给的参数转换成八进制
hex()进制转换将给的参数转换成十六进制
divmode()数学运算返回商和余数
round()数学运算四舍五入
pow()数学运算pow(a, b) 求a的b次幂, 如果有三个参数. 则求完次幂后对第三个数取余
min()数学运算求最小值
max()数学运算求最大值
sum()数学运算求和
dict()数据集合创建一个字典
set()数据集合创建一个集合
无序排序且不重复,是可变的,有add(),remove()等方法。既然是可变的,所以它不存在哈希值。基本功能包括关系测试和消除重复元素。集合对象还支持union(联合),intersection(交集),difference(差集)和sysmmetric_difference(对称差集)等数学运算。作为一个无序的集合,set不记录元素位置或者插入点。因此,set不支持indexing,或其它类序列的操作。
len()返回一个对象中的元素的个数
sorted()对可迭代对象进行排序操作 (lamda),sorted(Iterable, key=函数(排序规则), reverse=False)Iterable: 可迭代对象key() 排序规则(排序函数), 在sorted内部会将可迭代对象中的每一个元素传递给这个函数的参数. 根据函数运算的结果进行排序
reverse()是否是倒叙. True: 倒叙, False: 正序
enumerate()获取集合的枚举对象
all()可迭代对象中全部是True, 结果才是True
any()可迭代对象中有一个是True, 结果就是True
zip()函数用于将可迭代的对象作为参数, 将对象中对应的元素打包成一个元组, 然后返回由这些元组组成的列表. 如果各个迭代器的元素个数不一致, 则返回列表长度与最短的对象相同
fiter()过滤 (lamda)语法:fiter(function. Iterable)function: 用来筛选的函数. 在filter中会自动的把iterable中的元素传递给function. 然后根据function返回的True或者False来判断是否保留留此项数据 , Iterable: 可迭代对象
map()会根据提供的函数对指定序列列做映射(lamda) 语法 : map(function, iterable) 可以对可迭代对象中的每一个元素进行映射. 分别去执行 function
list()数据结构_将一个可迭代对象转换成列表
tuple()数据结构_将一个可迭代对象转换成元组
reversed()将一个序列翻转, 返回翻转序列的迭代器
slice()列表的切片
str()将数据转化成字符串
format()与具体数据相关, 用于计算各种小数, 精算
bytes()把字符串转化成bytes类型
bytearray()返回一个新字节数组. 这个数字的元素是可变的, 并且每个元素的值得范围是[0,256)
ord()输入字符找带字符编码的位置
chr()输入位置数字找出对应的字符
ascii()是ascii码中的返回该值 不是就返回u
repr()返回一个对象的string形式
locals()作用域相关返回当前作用域中的名字
globals()作用域相关返回全局作用域中的名字
range()迭代器生成器相关生成数据
next()迭代器生成器相关迭代器向下执行一次, 内部实际使用了__ next__()方法返回迭代器的下一个项目
iter()迭代器生成器相关获取迭代器, 内部实际使用的是__ iter__()方法来获取迭代器
eval()字符串类型代码的执行执行字符串类型的代码. 并返回最终结果

语法:
        eval(expression[, globals[, locals]])

参数
        expression – 表达式。
        globals – 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
        locals – 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。

返回值:表达式计算结果。
exec()字符串类型代码的执行执行字符串类型的代码
compile()字符串类型代码的执行将字符串类型的代码编码. 代码对象能够通过exec语句来执行或者eval()进行求值
print()输入输出打印输出
input()输入输出获取用户输出的
hash()内存相关获取到对象的哈希值(int, str, bool, tuple). hash算法:(1) 目的是唯一性 (2) dict 查找效率非常高, hash表.用空间换的时间 比较耗费内存
open()文件操作相关用于打开一个文件, 创建一个文件句柄
__ import__()模块相关用于动态加载类和函数
help()帮 助函数用于查看函数或模块用途的详细说明
callable()调用相关用于检查一个对象是否是可调用的. 如果返回True, object有可能调用失败, 但如果返回False. 那调用绝对不会成功
dir()查看内置属性查看对象的内置属性, 访问的是对象中的__dir__()方法
包含参数的方括号 [ ] 表示参数是可选参数。
当提供参数对象时,它返回对象内可用的属性,当不提供参数时,它返回当前作用域中可用的名称。
当对象内定义了 dir() 方法时,调用 dir(object) 函数时,对象内的 dir() 方法被调用,并返回在 dir() 方法内定义的属性列表。
如对象内没定义了 dir() 方法,调用 dir(object) 函数时,返回 dict 属性(如果定义了)和对象的数据类型内包含的属性(包括方法)。

dir([object])
参数:object:任意一个变量对象。
返回值:当提供参数对象时,它返回对象内可用的属性,当不提供参数时,它返回当前作用域中可用的名称。
setattr()setattr() 函数的功能相对比较复杂,它最基础的功能是修改类实例对象中的属性值。其次,它还可以实现为实例对象动态添加属性或者方法。setattr() 函数对应函数getattr(),用于设置属性值,若属性不存在,则先创建在赋值。给对象的属性赋值,若属性不存在,先创建再赋值。

setattr() 函数的语法格式如下:
        setattr(obj, name, value)

参数
        object – 对象
        name – 字符串,属性名
        value – 属性值。

返回值 :无
getattr()函数获取某个类实例对象中指定属性的值。没错,和 hasattr() 函数不同,该函数只会从类对象包含的所有属性中进行查找。

getattr() 函数的语法格式如下:
        getattr(obj, name[, default])
参数 :
        object – 对象
        name – 字符串,对象属性
        default-- 默认返回值,如果不提供该参数,在没有对应属性时,将触发AttrbuteError.

返回值 :返回对象属性值
hasattr()函数用来判断某个类实例对象是否包含指定名称的属性或方法。
判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。
需要注意的是name是一个字符串字面值或字符串类型变量。

语法格式如下:
        asattr(obj, name)

参数:
        object – 对象
        name – 字符串,属性名

返回值 :如果对象有该属性返回 True,否则返回 False。
delattr()delattr() 函数用来删除指定对象的指定名称的属性,和setattr函数作用相反,属性必须存在,否则发出AttributeError。delattr(object, name)
id()用于获取对象的 “identity” (唯一身份标示值,其实际值是内存地址),这个值是在对象的生命周期内是唯一且恒定的。如果两个对象的生命周期没有重叠,那么这两个对象使用 id() 函数返回的标示符(内存地址)可能相同。
id(object)
参数解释:object:任意一个对象,可以是变量,函数,类。
返回值解释:函数返回当前对象的参数的”identity” (内存地址)唯一标示符。
object()
staticmethod()静态方法staticmethod是一种装饰器,它可以将一个方法转换为静态方法。静态方法是属于类的方法,可以通过类名或实例名直接调用,而不需要传入self参数。通过使用staticmethod,我们可以将不需要访问实例属性或类属性的方法定义为静态方法,从而提高代码的灵活性和复用性。
语法格式如下:
   @staticmethod
   def 静态方法名():
         pass
classmethod()类方法python中 定义 class 时,只能有一个 初始化方法,不能按照不同情况初始化类。可以借助 class 方法来实现这个需求。
isinstance()isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。

语法:
        isinstance(object, classinfo)
参数含义
       object – 实例对象。
       classinfo – 可以是直接或间接类名、基本类型或者由它们组成的元组。
返回值:
        如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。
issubclass()issubclass 函数主要用于判断一个对象是否为另外一个对象的子类,

语法如下:
       issubclass(child_class,father_class)
参数:
        child_class — 类对象;
        father_class — 类对象;
返回值:
       如果child_class 是 father_class 的子类,返回True,否则返回 False;
super()用于调用父类(超类)的一个方法。super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

语法
       super(type[, object-or-type])
参数
       type – 类
       object-or-type – 类,一般是 self


Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx
property()
语法:
    property(fget=None, fset=None, fdel=None, doc=None)
参数说明:
    fget 是获取属性值的函数。
    fset 是设置(修改)属性值的函数。
    fdel 是删除属性值的函数。
    doc 是属性描述信息。如果省略,会把 fget 方法的文档字符串(docstring)拿来用(如果有的话)。
返回 property 属性(attribute)



@property装饰器可以做到和内置函数property() 相同的事情
被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名。
被 @属性名.setter 装饰的方法是设置属性值的方法。
被 @属性名.deleter 装饰的方法是删除属性值的方法
type()有两种语法形式,
type(object)
        此种形式的 tpye() 函数用于获取特定对象的数据类型。
        必须参数解释:object:任意一个对象,可以是变量,函数,类。
        返回值解释:函数返回当前参数变量的数据类型,形式为 <class ‘int’>。

type(name, bases, dict)
        此种形式的 tpye() 函数用于创建一个新的数据类型,它是类定义的动态形式。
        必须参数解释:
                name:要创建的新类(class)的名称。
                bases:参数是一个元组(tuple),它的元素是新类的基类,它表示创建的新类继承自这些类。
                dict:参数是一个字典,字典元素的键值对表示,新类的 属性名:属性值。
        返回值解释:函数返回创建的新类(数据类型)
vars()语法为:vars([object])
参数
          object – 对象
返回值
          返回对象object的属性和属性值的字典对象,如果没有参数,就打印当前调用位置的属性和属性值 类似 locals()。
memoryview()class memoryview(object)

函数

  1. 函数的声明,使用关键字 def 来声明一个函数
  2. 函数的格式 def 函数名(形参1,形参2…)
  3. 函数的调用 函数名(实参1,实参2…)
  4. 函数返回值 使用 return 语句返回函数的执行结果
  5. 函数返回多个结果,就是将多个数据打包成一个整体返回。 可以使用列表和字典,通常情况下选择使用元组

函数名也是一个标识符。
由数字、字母下划线组成,不能以数字开头;
严格区分大小写;不能使用关键字 遵守命名规范,使用下划线连接;顾名思义

注意:

函数的三要素:  函数名、参数和返回值 
在有一些编程语言里,允许函数重名,在Python里不允许函数的重名 
如果函数重名了,后一个函数会覆盖前一个函数
def 函数名(参数):
    函数体,函数要执行的操作
        
调用函数:  函数名(参数)

# 函数定义好了以后并不会自动执行
# 函数声明时,括号里的参数我们称之为形式参数,简称形参
# 形参的值是不确定的,只是用来占位的
# 调用函数时传递数据
# 函数调用时传入的参数,才是真正参与运算的数据,我们称之为实参


def tell_story(person1, person2):
    print('person1的值' + person1)
    print('person2的值' + person2)
tell_story('老道', '道童')  # 会把实参一一对应的传递,交给形参处理
# 还可以通过定义变量名的形式给形参赋值,“关键字=实参”的形式,其中,关键字的名称就是定义函数时形参的名称。
tell_story(person2='青年', person1='禅师')



# 返回值就是函数执行的结果,并不是所有的函数都必须要有返回值
def add(a, b):
    c = a + b  # 变量c在外部是不可见的,只能在函数内部使用
    return c  # return 表示一个函数的执行结果
# 多个返回值
def test(a, b):
    x = a // b
    y = a % b

    # 一般情况下,一个函数最多只会执行一个return语句
    # 特殊情况(finally语句)下,一个函数可能会执行多个return语句

    # return x  # return语句表示一个函数的结束
    # return {'x': x, 'y': y}
    # return [x, y]
    # return (x, y)
    return x, y  # 返回的本质是一个元组



def add(a: int, b: int):
    """
    这个函数用来将两个数字相加
    :param a: 第一个数字
    :param b: 第二个数字
    :return: 两个数字相加的结果
    """
    return a + b
全局变量 局部变量

变量可以在模块中创建,作用域(变量的有效范围)是整个模块,
被称为全局变量。变量也可以在函数中创建,在默认情况下作用域是整
个函数,被称为局部变量。

内置函数 globals()可以查看全局变量 locals()可以查看局部变量
使用 global 变量对变量进行声明,可以通过函数修改全局变量的值

a = 100  # 这个变量是全局变量,在整个py文件里都可以访问
word = '你好'


def test():
    x = 'hello'  # 这个变量是在函数内部定义的变量,它是局部变量,只能在函数内部使用
    print('x = {}'.format(x))

    # 如果局部变量的名和全局变量同名,会在函数内部又定义一个新的局部变量
    # 而不是修改全局变量
    a = 10
    print('函数内部a = {}'.format(a))


    # 使用global对变量进行声明,可以通过函数修改全局变量的值
    global word
    word = 'ok'

    print('locals = {},globals = {}'.format(locals(), globals()))


test()
# print(x)  # x只能在函数内部使用
print('函数外部a = {}'.format(a))
print('函数外部word={}'.format(word))

# 内置函数  globals()可以查看全局变量  locals()可以查看局部变量

# 在Python里,只有函数能够分割作用域
if 3 > 2:
    m = 'hi'

print(m)

print(globals())


# x = hello
# 函数内部a = 10
# locals = {'x': 'hello', 'a': 10},globals = {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001C835039700>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '位置.py', '__cached__': None, 'a': 100, 'word': 'ok', 'test': <function test at 0x000001C834EE9080>}
# 函数外部a = 100
# 函数外部word=ok
# hi
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001C835039700>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '位置.py', '__cached__': None, 'a': 100, 'word': 'ok', 'test': <function test at 0x000001C834EE9080>, 'm': 'hi'}

默认参数
 # 缺省参数要放在后面
def say_hello(name, age, city="襄阳"):  # 形参city设置了一个默认值
    print("大家好,我是{},我今年{}岁了,我来自{}".format(name, age, city))


say_hello('jack', 19)  # 如果没有传递参数,会使用默认值
say_hello(name='tony', age=23, city='北京')  # 如果传递参数,就使用传递的实参

# 如果有位置参数和关键字参数,关键字参数一定要放在位置参数的后面
say_hello('jerry', age=24, city='南京')  # 可以直接传递单个参数,也可以使用变量赋值的形式传参
say_hello(name='henry', city='成都', age=21)

# 缺省参数:
# 有些函数的参数是,如果你传递了参数,就使用传递的参数
# 如果没有传递参数,就使用默认的值


# print函数里end就是一个缺省参数
# print('hello', '你好', sep="____")
可变参数

*可变参数 表示可变位置参数
**可变参数 表示可变的关键字参数

def add(a, b, *args, mul=1, **kwargs):
    print('a = {},b={}'.format(a, b))
    print('args = {}'.format(args))  # 多出来的可变参数会以元组的形式保存到args里
    print('mul = {}'.format(mul))   # 没有传值  默认是1
    print('kwargs = {}'.format(kwargs))  # 多出来的关键字参数会以字典的形式保存
    
    c = a + b
    for arg in args:
        c += arg
    return c * mul



print(add(1, 3, 5, 7, mul=2, x=0, y=4))
# a = 1,b=3
# args = (5, 7)
#  mul= 2
# kwargs = {'x': 0, 'y': 4}
# 32


print(add(9, 5, 4, 2, 0, p=9, q=10))
# a = 9,b=5
# args = (4, 2, 0)
#  mul= 1
# kwargs = {'p': 9, 'q': 10}
# 20



print(add(8, 9, 7, 5, 7, 9, 8, 7, 5, 3, t=0, m=5))
# a = 8,b=9
# args = (7, 5, 7, 9, 8, 7, 5, 3)
#  mul= 1
# kwargs = {'t': 0, 'm': 5}
# 68
可变类型和不可变类型传参
def test(a):
    print('修改前a的内存地址0x%X' % id(a))
    a = 100
    print('修改后a的内存地址0x%X' % id(a))


def demo(nums):
    print('修改前nums的内存地址0x%X' % id(nums))
    nums[0] = 10
    print('修改后nums的内存地址0x%X' % id(nums))


x = 1
print('调用前x的内存地址0x%X' % id(x))
test(x)
print('调用后x的内存地址0x%X' % id(x))
print(x)  # 1

y = [3, 5, 6, 8, 2]
print('调用前y的内存地址0x%X' % id(y))
demo(y)
print('调用后y的内存地址0x%X' % id(y))
print(y)  # [10, 5, 6, 8, 2]



# 调用前x的内存地址0x7FFF907EC9B8
# 修改前a的内存地址0x7FFF907EC9B8
# 修改后a的内存地址0x7FFF907ED618
# 调用后x的内存地址0x7FFF907EC9B8
# 1
# 调用前y的内存地址0x2854620D100
# 修改前nums的内存地址0x2854620D100
# 修改后nums的内存地址0x2854620D100
# 调用后y的内存地址0x2854620D100
# [10, 5, 6, 8, 2]
递归函数

递归简单来说,就是函数内部自己调用自己
递归最重要的就是找到出口(停止的条件)

# 使用递归求 n!  n!=n*(n-1)!
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)


print(factorial(6))   # 720


# 使用递归求斐波那契数列的第 n 个数字
# 1,1,2,3,5,8,13,21,34,55,89,144
def fibonacci(n):
    if n == 1 or n == 2:
        return 1
    return fibonacci(n - 2) + fibonacci(n - 1)


print(fibonacci(9)) # 34

lambda() 匿名函数

除了使用 def 关键字定义一个函数以外,我们还能使用 lambda 表达式定义一个函数

匿名函数 用来表达一个简单的函数,函数调用的次数很少,基本上就是调用一次 调用匿名函数两种方式:

  1. 给它定义一个名字(很少这样使用)
  2. 把这个函数当做参数传给另一个函数使用(使用场景比较多)

注意: lambda体部分不能是一个代码块,不能包含多条语句,只有一条语句,语句会计算一个结果并返回给lambda()函数,但与有名称的函数不同的是,不需要使用return语句返回。

# 有名称的函数  定义的add()
def add(a, b):
    c = a + b  # 变量c在外部是不可见的,只能在函数内部使用
    return c  # return 表示一个函数的执行结果
x = add(4, 5)  # 函数名(实参) 作用是调用函数,获取到函数的执行结果,并赋值给变量 x
print(x)

#lambda() 匿名函数
mul = lambda a, b: a + b
print(mul(4, 5))
def calc(a, b, fn):
    c = fn(a, b)
    return c


# def add(x, y):
#     return x + y


# def minus(x, y):
#     return x - y


# 回调函数
# x1 = calc(1, 2, add)  # a=1,b=2,fn=add
# x2 = calc(10, 5, minus)  # a=10,b=5,fn=minus

x3 = calc(5, 7, lambda x, y: x + y)
x4 = calc(19, 3, lambda x, y: x - y)
x5 = calc(2, 7, lambda x, y: x * y)
x6 = calc(12, 3, lambda x, y: x / y)

print(x3, x4, x5, x6, sep='\n')

# 12
# 16
# 14
# 4.0

内置函数的使用

sort() 方法 : 会直接对列表进行排序 字典排序时需要传递参数 key(键名) 指定比较规则
sorted() 内置函数 : 不会改变原有的数据,而是生成一个新的有序的列表

  • nums = [4, 8, 2, 1, 7, 6]
    ints = (5, 9, 2, 1, 3, 8, 7, 4)
     
     nums.sort()
     print(nums)   # [1, 2, 4, 6, 7, 8] 	
     
     x = sorted(ints)
     print(ints)  # (5, 9, 2, 1, 3, 8, 7, 4)
     print(x)    #  [1, 2, 3, 4, 5, 7, 8, 9]
     
     students = [
        {'name': 'zhangsan', 'age': 18, 'score': 98, 'height': 180},
        {'name': 'lisi', 'age': 21, 'score': 97, 'height': 185},
        {'name': 'jack', 'age': 22, 'score': 100, 'height': 175},
        {'name': 'tony', 'age': 23, 'score': 90, 'height': 176},
        {'name': 'henry', 'age': 20, 'score': 95, 'height': 172}
    ]
    
    
    # students.sort()  
    
    # foo() takes 0 positional arguments but 1 was given
    # foo这个函数需要 0 个位置参数,但是在调用的时候传递了一个参数
    # def foo(ele):
    #     # print("ele = {}".format(ele))
    #     return ele['height']  # 通过返回值告诉sort方法,按照元素的那个属性进行排序
    
    
    # 需要传递参数 key 指定比较规则
    # key参数类型是函数
    
    # 在sort内部实现的时候,调用了foo方法,并且传入了一个参数,参数就是列表里的元素
    # students.sort(key=foo)
    
    students.sort(key=lambda ele: ele['age'])
    print(students)
    # [{'name': 'zhangsan', 'age': 18, 'score': 98, 'height': 180}, {'name': 'henry', 'age': 20, 'score': 95, 'height': 172}, {'name': 'lisi', 'age': 21, 'score': 97, 'height': 185}, {'name': 'jack', 'age': 22, 'score': 100, 'height': 175}, {'name': 'tony', 'age': 23, 'score': 90, 'height': 176}]
    

filter() : 对可迭代对象进行过滤,得到的是一个filter对象
两个参数,第一个参数是函数,第二个参数是可迭代对象

  • # filter 对可迭代对象进行过滤,得到的是一个filter对象
    
    ages = [12, 23, 30, 17, 16, 22, 19]
    # filter可以给定两个参数,第一个参数是函数,第二个参数是可迭代对象
    # filter结果是一个 filter 类型的对象,filter对象也是一个可迭代对象
    x = filter(lambda ele: ele > 18, ages)
    print(x)  # <filter object at 0x000002670373E908>
    
    adult = list(x)
    print(adult)  # [23, 30, 22, 19]
    

map() : 第一个参数接受一个函数名,后面的参数接受一个或多个可迭代的序列,返回的是一个集合。
把函数依次作用在list中的每一个元素上,得到一个新的list并返回。注意,map不改变原list,而是返回一个新list。
通过map还可以实现类型转换

  • # map() 函数+ lambda表达式
    bonuses = [100, 200, 300]
    iterator = map(lambda x: x*2, bonuses)
    print(list(iterator))  # 输出:[200, 400, 600]
    
    # map() 函数输入多个可迭代对象iterable
    b1 = [100, 200, 300]
    b2 = [1, 2, 3]
    
    iterator = map(lambda x,y : x*y, b1, b2)
    print(list(iterator))  # 输出:[100, 400, 900]
    
    
    def double_func(x):>
        return x* 2
    
    bonuses = [100, 200, 300]
    
    iterator = map(double_func, bonuses)
    print(list(iterator))  # 输出:[200, 400, 600]
    
    # print(iterator)
    # 输出:<map object at 0x000001171E2A5540>
    
    # 通过lambda函数使返回值是一个元组:
    c = map(lambda x, y : (x**y,x+y),[2,4,6],[3,2,1])
    print(list(c)) # [(8, 5), (16, 6), (6, 7)]
    
    
    
    # 通过map还可以实现类型转换
    #将元组转换为list:
    
    v = map(int, (1, 2, 3))
    print(list(v)) #[1, 2, 3]
    
    
    #将字符串转换为list:
    
    x = map(int, '1234')
    print(list(x)) # [1, 2, 3, 4]
    
    #提取字典中的key,并将结果放在一个list中:
    f = map(int, {1: 2, 2: 3, 3: 4})
    print(list(f)) # [1, 2, 3]
    

reduce() : 函数原本在python2中也是个内置函数,不过在python3中被移到functools模块中
from functools import reduce 导入模块的语法
reduce函数先从列表(或序列)中取出2个元素执行指定函数,并将输出结果与第3个元素传入函数,输出结果再与第4个元素传入函数,…,以此类推,直到列表每个元素都取完。
与内置函数map和filter不一样的是,在性能方面,reduce相比较for循环来说没有优势,甚至在实际测试中reduce比for循环更慢。

  • from functools import reduce  # 导入模块的语法
    
    
    # reduce 以前是一个内置函数
    # 内置函数和内置类都在 builtins.py文件里
    
    def foo(x, y):  # x=100,y=89;x=189,y=76;x=265,y=87
        return x + y  
    
    scores = [100, 89, 76, 87]
    print(reduce(foo, scores))  # 352
    
    students = [
        {'name': 'zhangsan', 'age': 18, 'score': 98, 'height': 180},
        {'name': 'lisi', 'age': 21, 'score': 97, 'height': 185},
        {'name': 'jack', 'age': 22, 'score': 100, 'height': 175},
        {'name': 'tony', 'age': 23, 'score': 90, 'height': 176},
        {'name': 'henry', 'age': 20, 'score': 95, 'height': 172}
    ]
    
    def bar(x, y):
        # x= 0
       # y = {'name': 'zhangsan', 'age': 18, 'score': 98, 'height': 180},
       return x + y['age']
       print(reduce(bar, students, 0))   # 104
       
       print(reduce(lambda x, y: x + y['age'], students, 0))   # 104
       
       
       #  for循环
       def sum_func(arr):
           if len(arr) <= 0:
               return 0
           else:
               out = arr[0]
               for v in arr[1:]:
                   out += v
               return out
       a = [1, 2, 3, 4, 5]
       print(sum_func(a))    # 15
       
       
       
       # reduce    
       a = [1, 2, 3, 4, 5]
       def add(x, y): return x + y 
       print(reduce(add, a))   # 15
    
# staticmethod的使用示例
# 下面通过一个实际的示例来演示staticmethod的使用方法。假设我们有一个名为MathUtil的类,其中包含了一个计算两个数相加的静态方法add:​​​​​​​

class MathUtil:
    @staticmethod
    def add(x, y):
        return x + y
#在上面的示例中,我们使用@staticmethod装饰器将add方法定义为静态方法。这样一来,我们就可以通过类名或实例名直接调用add方法,而不需要创建MathUtil的实例对象:

print(MathUtil.add(3, 5))  # 输出8



#使用staticmethod有以下几个好处:
#1. 提高代码的可读性:将不需要访问实例属性或类属性的方法定义为静态方法,可以更清晰地表达方法的用途,提高代码的可读性。
#2. 提高代码的复用性:静态方法可以在不创建类实例的情况下直接调用,从而提高方法的复用性。
#3. 减少不必要的内存消耗:静态方法不需要访问实例属性,因此不会创建实例对象,从而减少不必要的内存消耗。

#在使用staticmethod时,需要注意以下几点:
#1. 静态方法中无法访问实例属性或类属性,因此在定义静态方法时,需要确保方法的功能与实例属性或类属性无关。
#2. 静态方法的调用可以通过类名或实例名来进行,但通常建议使用类名进行调用,以表明该方法与类本身相关,而不是与特定的实例对象相关。
#3. 静态方法通常用于定义一些通用的功能性方法,例如数学计算、类型转换等,它们与特定的实例对象无关。
# 无参数情况下的使用:

# 当在全局作用域内调用 vars() 函数时,它返回当前全局作用域中的变量和值的字典。
# 当在函数内部调用 vars() 函数时,它返回当前函数的局部变量和值的字典。
# 有参数情况下的使用:

# 如果传递一个对象作为参数给 vars() 函数,它将返回该对象的属性和属性值的字典。
# 对于自定义类的实例对象,vars() 函数返回的字典将包含实例的属性和属性值。
# 对于内置类的实例对象(如列表、字符串等),vars() 函数返回的字典通常只包含内置属性和方法,并不包含实例化时添加的自定义属性。


# 1. 获取对象的属性和属性值:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age 
person = Person("Alice", 25)
attributes = vars(person)
print(attributes)  # {'name': 'Alice', 'age': 25}

# 2. 获取当前作用域的局部变量:
def my_function():
    x = 10
    y = 20
    local_vars = vars()
    print(local_vars)  # {'x': 10, 'y': 20} 
my_function()

# 3. 获取全局作用域的变量:
global_var = 100
 
def print_global_vars():
    global_vars = vars()
    print(global_vars)  # {'global_var': 100, ... (其他全局变量)}
print_global_vars()

# 4. 获取模块的全局变量:
# my_module.py
x = 10
y = 20 
module_vars = vars()
print(module_vars)  # {'__name__': '__main__', '__doc__': None, '__package__': None, 'x': 10, 'y': 20}

高级函数 函数嵌套

# 1. 一个函数作为另一个函数的参数
# 2. 一个函数作为另一个函数的返回值
# 3. 函数内部再定义一个函数


def foo():
    print('我是foo,我被调用了')
    return 'foo'


def bar():
    print('我是bar,我被调用了')
    return foo


x()

y = bar()()
print(y)


# 装饰器
def outer():
    m = 100

    def inner():
        n = 90
        print('我是inner函数')

    print('我是outer函数')
    return inner


outer()()

函数练习代码

# 编写一个函数,求多个数中的最大值
import random


def get_max(*args):
    x = args[0]
    for arg in args:
        if arg > x:
            x = arg
    return x


# 编写一个函数,实现摇骰子的功能,打印N个骰子的点数和
def get_sum(n):
    m = 0
    for i in range(n):
        x = random.randint(1, 6)
        m += x
    return m


# 编写一个函数,提取指定字符串中所有的字母,然后拼接在一起产生一个新的字符串
def get_alphas(word):
    new_str = ''
    for w in word:
        if w.isalpha():
            new_str += w
    return new_str


# 写一个函数,默认求10的阶乘,也可以求其他数字的阶乘
def get_factorial(n=10):
    x = 1
    for i in range(1, n + 1):
        x *= i
    return x


# 写一个函数,求多个数的平均值
def get_average(*args):
    x = 0
    for arg in args:
        x += arg
    return x / len(args)


# 写一个自己的capitalize函数,能够将指定字符串的首字母变成大写字母
def my_capitalize(word):
    c = word[0]
    if 'z' >= c >= 'a':
        new_str = word[1:]
        return c.upper() + new_str
    return word


# 写一个自己的endswith函数,判断一个字符串是否以指定的字符串结束
def my_endswith(old_str, str1):
    return old_str[-len(str1):] == str1


# 写一个自己的isdigit函数,判断一个字符串是否是纯数字字符串
def my_digit(old_str):
    for s in old_str:
        if not '0' <= s <= '9':
            return False
    return True


# 写一个自己的upper函数,将一个字符串中所有的小写字母变成大写字母
# a==>97  A ==> 65   32
def my_upper(old_str):
    new_str = ''
    for s in old_str:
        if 'a' <= s <= 'z':
            upper_s = chr(ord(s) - 32)
            new_str += upper_s
        else:
            new_str += s
    return new_str


# 写一个函数实现自己in操作,判断指定序列中,指定的元素是否存在
def my_in(it, ele):
    for i in it:
        if i == ele:
            return True
    else:
        return False


# 写一个自己的replace函数,将指定字符串中指定的旧字符串转换成指定的新字符串
# 方法一
def my_replace1(all_str, old_str, new_str):
	return new_str.join(all_str.split(old_str))
	
#方法二
def my_replace(all_str, old_str, new_str):
    result = ''
    i = 0
    while i < len(all_str):
        temp = all_str[i:i + len(old_str)]
        if temp != old_str:
            result += all_str[i]
            i += 1
        else:
            result += new_str
            i += len(old_str)
    return result


# 写一个自己的max函数,获取指定序列中元素的最大值。如果序列是字典,取字典值的最大值
def get_max2(seq):
    # if type(seq) == dict: # 另一种写法
    if isinstance(seq, dict):  # 看对象seq是否是通过dict类创建出来的实例
        seq = list(seq.values())
    x = seq[0]
    for i in seq:
        if i > x:
            x = i
    return x


print(get_max(1, 9, 6, 3, 4, 5))
print(get_sum(5))
print(get_alphas('hello123good456'))
print(get_factorial())
print(get_average(1, 2, 3, 4, 5, 6))
print(my_capitalize('hello'))
print(my_capitalize('34hello'))
print(my_endswith('hello', 'lxo'))
print(my_digit('12390'))
print(my_upper('hel34lo'))
print(my_in(['zhangsan', 'lisi', 'wangwu'], 'jack'))
print(my_in({'name': 'zhangsan', 'age': '18'}, 'name'))
print(my_replace1('how you and you fine you ok', 'you', 'me'))
print(my_replace('how you and you fine you ok', 'you', 'me'))
# ['zhangsan','lisi','wangwu']  ==> zhangsan_lisi_wangwu

print(get_max2([2, 4, 8, 1, 9, 0, 7, 5]))
print(get_max2({'x': 10, 'y': 29, 'z': 32, 'a': 23, 'b': 19, 'c': 98}))

闭包的概念

闭包定义:

 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,
 我们把这个使用外部函数变量的内部函数称为闭包

闭包的构成条件:

在函数嵌套(函数里面在定义函数)的前提下
内部函数使用了外部函数的变量(还包括外部函数的参数)
外部函数返回了内部函数

闭包书写步骤:

定义外部函数
定义外部函数,在内部函数中使用外部函数的变量
外部函数返回内部函数的地址

闭包的使用
案例:根据配置信息使用闭包实现不同人的对话信息,例如对话:
      张三:到北京了吗?
      李四:已经到了,放心吧。
实现步骤说明
      定义外部函数接受不同的配置信息参数,参数是人名
      定义内部函数接受对话信息参数
      在内部函数里面把配置信息和对话信息进行拼接输出

  • def outer(name):
        # 定义内部函数,参数是 说话的信息
        print(f'{name}:到北京了吗?')
     
        def inner(name1):
            # 内部函数中,将name和info进行拼接输出
            print(f'{name1}:已经到了,放心吧。')
    
        # 外部函数返回内部函数的地址
        return inner
     
     
    # 创建闭包实例
    if __name__ == '__main__':
        func = outer('张三')
        func('李四')
    # 注意点:由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
    

闭包修改外部函数变量(理解)
函数内部想要修改全局变量,使用global关键字
在闭包函数内部,想要修改外部函数的局部变量,需要使用nonlocal关键字

  • def outer():
        num = 10
    
        def inner():
             #不写 nonlocal  num = 100就不是修改外部变量的值,重新定义的局部变量
            nonlocal num  # 声明使用外部变量 num 不重新定义 只能写在最上面
            print('      inner里的num = ', num)
            num = 100
            print('      inner外面的 num 被修改成 ', num)
    
        print(f'调用inner之前:{num}')
        inner()
        print(f'调用inner之后:{num}')
        return inner
    
    
    print(f'^^^^^^^^^^^^^^^写法一^^^^^^^^^^^^^^^')
    func = outer()
    func()
    
    print(f'^^^^^^^^^^^^^^^写法二^^^^^^^^^^^^^^^') 
    outer()()
    
    # ^^^^^^^^^^^^^^^写法一^^^^^^^^^^^^^^^
    # 调用inner之前:10
    #       inner里的num =  10
    #       inner外面的 num 被修改成  100
    # 调用inner之后:100
    #       inner里的num =  100
    #       inner外面的 num 被修改成  100
    # ^^^^^^^^^^^^^^^写法二^^^^^^^^^^^^^^^
    # 调用inner之前:10
    #       inner里的num =  10
    #       inner外面的 num 被修改成  100
    # 调用inner之后:100
    #       inner里的num =  100
    #       inner外面的 num 被修改成  100
    

计算时间的代码

import time  # time模块可以获取当前的时间


def cal_time(fn):
    start = time.time()  # time模块里的time方法,可以获取当前时间的时间戳
    fn()
    end = time.time()
    print('代码运行耗时{}秒'.format(end - start))


def demo():
    x = 0
    for i in range(1, 100000000):
        x += i
    print(x)


def foo():
    print('hello')
    time.sleep(3) #  暂停 3秒 执行下边的代码
    print('world')


cal_time(demo)
cal_time(foo)

装饰器

Python 中的装饰器是一种语法糖,可以在运行时,动态的给函数或类添加功能。
装饰器本质上是一个函数,使用 @ + 函数名就是可实现绑定给函数的第二个功能 。
将一些通用的、特定函数的功能抽象成一个装饰器,可以重复利用这些功能
用于在不修改原始函数或类代码的情况下,对其进行功能扩展或修改。装饰器基于函数式编程的概念,通过将函数作为参数传递给另一个函数,并返回一个新的函数来实现。

def 装饰器函数名 (fn):
       def 内函数名 (a, b):
            fn(a,b)
            函数块执行代码
      return 内函数名


@装饰器函数名
def function( a,b):
       pass

def msg_service(fn):
    def servemoney2(name,x):
        print("欢迎存钱")
        fn(name,x)
        print("存钱结束")
    return servemoney2
    
@msg_service
def servemoney(name,x):
    print(name,'存了',x,'元')
servemoney("小张",100)

# @msg_service的作用相当于servemoney = msg_service(servemoney)

# 执行结果
# 欢迎存钱
# 小张 存了 100 元
# 存钱结束

import time


def cal_time(fn):
    print('我是外部函数,我被调用了!!!')
    print('fn = {}'.format(fn))

    def inner():
        start = time.time()
        fn()
        end = time.time()
        print('代码耗时', end - start)

    return inner


@cal_time  # 第一件事调用cal_time;第二件事把被装饰的函数传递给fn
def demo():
    x = 0
    for i in range(1, 100000000):
        x += i
    print(x)


# 第三件事:当再次调用demo函数时,才是的demo函数已经不再是上面的demo
print('装饰后的demo = {}'.format(demo))
demo()



# 我是外部函数,我被调用了!!!
# fn = <function demo at 0x000002293D2DFCE0>
# 装饰后的demo = <function cal_time.<locals>.inner at 0x000002293D159080>
# 4999999950000000
# 代码耗时 3.033639430999756
# 产品经理: 提需求 / 改需求.
# 如果超过22点不让玩儿游戏,如果不告诉时间,默认让玩儿游戏
# 开放封闭原则

def can_play(fn):
    def inner(x, y, *args, **kwargs):
        # print(args)
        # clock = kwargs['clock']   # 不传值拿不到clock 会报错
        clock = kwargs.get('clock', 23) # 用.get() 拿不到值不报错  还可以设置默认值
        if clock <= 22:
            fn(x, y)
        else:
            print('太晚了,赶紧睡')

    return inner


@can_play
def play_game(name, game):
    print('{}正在玩儿{}'.format(name, game))


play_game('张三', '王者荣耀', m='hello', n='good', clock=18)
print('----------------')
play_game('张三', '王者荣耀', m='hello', n='good', clock=24)
print('----------------')
play_game('李四', '吃鸡')

# 张三正在玩儿王者荣耀
# ----------------
# 太晚了,赶紧睡
# ----------------
# 太晚了,赶紧睡

在这里插入图片描述


# 可以通过 修改 user_permission 变量来改变权限 不能超过 15(8+4+2+1)
#  user_permission 通过转化成二进制 来看是否包含 权限因子DEL_PERMISSION READ_PERMISSION WRITE_PERMISSION EXE_PERMISSION
user_permission = 11  # 1011

# 权限因子
# 用户权限  &  权限因子 != 0
DEL_PERMISSION = 8  # 1011 & 1000  ==>  1000
READ_PERMISSION = 4  # 1011 & 0100 ==> 0000
WRITE_PERMISSION = 2  # 1011 & 0010 ==> 0010
EXE_PERMISSION = 1  # 1011 & 0001 ==> 0001


def check_permission(x, y):
    def handle_action(fn):
        def do_action():
            if x & y != 0:  # 有权限,可以执行
                fn()
            else:
                print('对不起,您没有响应的权限')

        return do_action

    return handle_action


@check_permission(user_permission, READ_PERMISSION)
def read():
    print('我正在读取内容')


@check_permission(user_permission, WRITE_PERMISSION)
def write():
    print('我正在写入内容')


@check_permission(user_permission, EXE_PERMISSION)
def execute():
    print('我正在执行内容')


@check_permission(user_permission, DEL_PERMISSION)
def delete():
    print('我正在删除内容')


read()
write()
execute()
delete()
# 对不起,您没有响应的权限
# 我正在写入内容
# 我正在执行内容
# 我正在删除内容

模块

模块的概念

  • 模块是python程序架构的一个核心概念。
    • 每一个以扩展名py结尾的python源代码文件都是一个模块
    • 模块名同样也是一个标识符,需要符合标识符的命名规则
    • 在模块中定义的全局变量、函数、类都是提供给外界直接使用的工具
    • 模块就好比工具包,要想使用这个工具包中的工具,就需要先的导入这个模块

模块的导入方式

在导入模块时,每个导入的模块应该独占一行(推荐使用)
不仅可以引入函数,还可以引入一些全局变量、类等。
【下方 工具名 包含函数、全局变量、类】

  • 语法格式:
    1. import 模块名导入整个模块
    2. import 模块名 as 模块别名导入整个模块的同时给该模块取个较短的别名 可以逗号隔开引用多个
    3. from 模块名 import * 从模块导入所有工具
    4. from 模块名 import 工具名 从模块导入某一个
    5. from 模块名 import 工具名 as 模块别名 从模块导入某一个,可能不同的模块名存在相同的工具名可以取个别名便于区分

  • 使用方式
    模块名或模块别名. 工具名

一个模块本质上就是一个py文件
自己定义一个模块,其实就是自己写一个py文件
import 我的模块 如果一个py文件想要当做一个模块被导入,文件名一定要遵守命名规范
由数字、字母下划线组成,不能以数字开头
导入了一个模块,就能使用这个模块里变量和函数

安装三方模块

一、Python修改镜像源

python默认的官方仓库服务器在国外。因此,我们可以将镜像源修改为国内服务器镜像源

镜像站名镜像站网址
清华大学开源软件镜像站https://mirrors.tuna.tsinghua.edu.cn/   在这里插入图片描述
中国科学技术大学http://mirrors.aliyun.com/    在这里插入图片描述
阿里云开源镜像站在这里插入图片描述
网易开源镜像站http://mirrors.163.com/ 
搜狐开源镜像http://mirrors.sohu.com/
浙江大学开源镜像站在这里插入图片描述   http://mirrors.zju.edu.cn/
腾讯开源镜像站http://mirrors.cloud.tencent.com/pypi/simple
# windows:
# 配置中科大镜像
pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple

# 配置阿里源
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
# 配置腾讯源
pip config set global.index-url http://mirrors.cloud.tencent.com/pypi/simple
# 配置豆瓣源
pip config set global.index-url http://pypi.douban.com/simple/

## 你只需配置其中一个即可

修改镜像源

# 临时修改镜像源
#临时修改镜像源使用下面的命令
pip install 库名 -i 镜像地址

#比如我要安装 numpy 库 并使用 阿里云 的镜像源
pip3 install numpy -i https://mirrors.aliyun.com/pypi/simple


# 永久修改镜像源
# windows
# 打开我的电脑,在地址栏中输入 %APPDATA% 按回车跳转到目标目录。在目录下创建一个
# pip文件,再其内部创建一个pip.ini 文件。
#编辑文件输入以下信息。以豆瓣为例!
# ![在地址栏中输入 %APPDATA% 按回车 示例](https://img-blog.csdnimg.cn/direct/76e21f5226014d2fa975ce9c1eb3ba38.png)


[global]timeout = 6000 index-url = https://pypi.douban.com/simple/trusted-host = pypi.douban.com
timeout = 6000 
index-url = https://pypi.douban.com/simple/
trusted-host = pypi.douban.com
二、Python安装三方模块

方法一:

使用命令安装
windows系统:pip install 模块名
mac、linux系统:pip3 install 模块名


卸载 : pip uninstall 模块名


显示安装列表: pip list


列出当前环境安装的模块名和版本号: pip freeze

pip freeze > file_name  将安装的模块名和版本号重定向输出到指定的文件
pip install -r file_name  读取文件里模块名和版本号并安装

方法二:使用pycharm自带的可视化界面安装 【推荐 安装过程可以关闭安装的弹窗,在后台进行安装,不影响接下来的编码工作。】
setting --> project:项目名 --> python interpreter --> + --> 搜索模块名 --> install package
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

常用的内置模块

  • os 模块

    •  # os全称 OperationSystem操作系统
       # os 模块里提供的方法就是用来调用操作系统里的方法
       import os
       
       # os.name ==> 获取操作系统的名字    windows系列 ==>nt / 非windows ==>posix
       print(os.name)  # nt
       print(os.sep)  # 路径的分隔符  windows \   非windows /
      
       # os模块里的 path 经常会使用
       # abspath ==> 获取文件的绝对路径
       print(os.path.abspath('文件.py'))
      
       # isdir判断是否是文件夹
       print(os.path.isdir('文件.py'))  # False
       print(os.path.isdir('新建文件夹'))  # True
       
       # isfile 判断是否是文件
       print(os.path.isfile('文件.py'))  # True
       print(os.path.isfile('新建文件夹'))  # False
      
       # exists 判断是否存在
       print(os.path.exists('存在的.py'))  # True
       print(os.path.exists('mmm.py'))  # False
      
       file_name = '2020.2.21.demo.py'
       print(file_name.rpartition('.')) # ('2020.2.21.demo', '.', 'py')
       print(os.path.splitext(file_name)) # ('2020.2.21.demo', '.py')
      
       # os里其他方法的介绍
       # os.getcwd()  # 获取当前的工作目录,即当前python脚本工作的目录
       # os.chdir('test') # 改变当前脚本工作目录,相当于shell下的cd命令
       # os.rename('毕业论文.txt','毕业论文-最终版.txt') # 文件重命名
       # os.remove('毕业论文.txt') # 删除文件
       # os.rmdir('demo')  # 删除空文件夹
       # os.removedirs('demo') # 删除空文件夹
       # os.mkdir('demo')  # 创建一个文件夹
       # os.chdir('C:\\') # 切换工作目录
       # os.listdir('C:\\') # 列出指定目录里的所有文件和文件夹
       # os.name # nt->widonws posix->Linux/Unix或者MacOS
       # os.environ # 获取到环境配置
       # os.environ.get('PATH') # 获取指定的环境配置
       
      
  • os 模块

    •   # sys 系统相关的功能
       import sys
       
      
       
       print(sys.path)  # 结果是一个列表,表示查找模块的路径
       
       # sys.stdin  # 可以像input一样,接收用户的输入。接收用户的输入,和 input 相关
       
       # sys.stdout 和 sys.stderr 默认都是在控制台
       # sys.stdout  # 修改sys.stdout 可以改变默认输出位置
       # sys.stderr  # 修改sys.stderr 可以改变错误输出的默认位置
       
        print('hello world')  
       # 程序退出,和内置函数exit功能一致 并给一个退出码 100   正常的退出码应该是0 
       sys.exit(100)   # 进程已结束,退出代码为 100     
       # 呵呵呵呵不会打印
       print('呵呵呵呵') 
       	
      
  • math 数学计算相关的模块

    •  # 数学计算相关的模块
       import math
       
              
       print(math.fabs(-100)) # 取绝对值
       print(math.ceil(34.01))  #向上取整
       print(math.factorial(5)) # 计算阶乘
       print(math.floor(34.98))  # 向下取整
       # round()   内置函数,实现四舍五入到指定位数
       print(math.pi)   # π的值,约等于 3.141592653589793
       print(math.pow(2, 10)) # 2的10次方
       print(math.sin(math.pi / 6))  # 正弦值
       print(math.cos(math.pi / 3))  # 余弦值
       print(math.tan(math.pi / 2))  # 正切值
       # 100.0
       # 35
       # 120
       # 34
       # 3.141592653589793
       # 1024.0
       # 0.49999999999999994
       # 0.5000000000000001
       # 1.633123935319537e+16
      
      
  • random模块

    •  # random 模块主要用于生成随机数或者从一个列表里随机获取数据。
       
       print(random.random())  # 生成 [0,1)的随机浮点数
       print(random.uniform(20, 30))  # 生成[20,30]的随机浮点数
       print(random.randint(10, 30))  # 生成[10,30]的随机整数
       print(random.randrange(20, 30))  # 生成[20,30)的随机整数
       print(random.choice('abcdefg'))  # 从列表里随机取出一个元素
       print(random.sample('abcdefghij', 3)) # 从列表里随机取出指定个数的元素
      
  • datetime模块

    •  # datetime模块主要用来显示日期时间,这里主要涉及 date类,用来显示日期;time类,用来显示时间;dateteime类,用来显示日期时间;timedelta类用来计算时间。
      
      import datetime
      print(datetime.date(2020, 1, 1))  # 创建一个日期
      print(datetime.time(18, 23, 45)) # 创建一个时间
      print(datetime.datetime.now())  # 获取当前的日期时间
      print(datetime.datetime.now() + datetime.timedelta(3))  # 计算三天以后的日期时间
      
  • time模块

    •  # 除了使用datetime模块里的time类以外,Python还单独提供了另一个time模块,用来操作时间。time模块不仅可以用来显示时间,还可以控制程序,让程序暂停(使用sleep函数)
       
       print(time.time())  # 获取从1970-01-01 00:00:00 UTC 到现在时间的秒数
       print(time.strftime("%Y-%m-%d %H:%M:%S")) # 按照指定格式输出时间
       print(time.asctime()) #Mon Apr 15 20:03:23 2019
       print(time.ctime()) # Mon Apr 15 20:03:23 2019
       
       print('hello')
       print(time.sleep(10)) # 让线程暂停10秒钟
       print('world')
      
  • calendar模块

    •  # calendar模块用来显示一个日历,使用的不多,了解即可。
       
       # 周一到周        日分别对应 0 ~ 6
       print( calendar.setfirstweekday(calendar.SUNDAY)) # 设置每周起始日期码。 6
       print(calendar.firstweekday()) # 返回当前每周起始日期的设置。默认情况下,首次载入        calendar模块时返回0,即星期一。
       
       c = calendar.calendar(2023)  # 生成2023年的日历,并且以周日为其实日期码
       print(c)  #打印2023年日历
       print(calendar.isleap(2014)) # True.闰年返回True,否则返回False
       count = calendar.leapdays(1996,2014) # 获取1996年到2014年一共有多少个闰年
       print(calendar.month(2024, 1))  # 打印2024年1月的日历
      
  • hashlib模块模块 hmac模块

    •  # 这两个模块都是用来进行数据加密
       # hashlib模块里主要支持两个算法  md5 和  sha 加密
       # 加密方式: 单向加密:只有加密的过程,不能解密md5/sha   对称加密   非对称加密rsa
       
       # hashlib是一个提供字符加密功能的模块,包含MD5和SHA的加密算法,具体支持md5,sha1, sha224, sha256, sha384, sha512等算法。 该模块在用户登录认证方面应用广泛,对文本加密也很常见。
       import hashlib
      
       # 待加密信息
       str = '这是一个测试'
       
       # 创建md5对象
       hl = hashlib.md5('hello'.encode(encoding='utf8'))
       print('MD5加密后为 :' + hl.hexdigest())
       
       h1 = hashlib.sha1('123456'.encode())
       print(h1.hexdigest())# 7c4a8d09ca3762af61e59520943dc26494f8941b
       h2 = hashlib.sha224('123456'.encode())# 224位  一个十六进制占4位
       print(h2.hexdigest())# f8cdb04495ded47615258f9dc6a3f4707fd2405434fefc3cbf4ef4e6
       h3 = hashlib.sha256('123456'.encode())
       print(h3.hexdigest())# 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
       h4 = hashlib.sha384('123456'.encode())
       print(h4.hexdigest())# 0a989ebc4a77b56a6e2bb7b19d995d185ce44090c13e2984b7ecc6d446d4b61ea9991b76a4c2f04b1b4d244841449454
       
       
       
      # HMAC算法也是一种一种单项加密算法,并且它是基于上面各种哈希算法/散列算法的,只是它可以在运算过程中使用一个密钥来增增强安全性。hmac模块实现了HAMC算法,提供了相应的函数和方法,且与hashlib提供的api基本一致。
      
       import hmac
       
       print("===hmac字符加密示例===")
       # 创建哈希对象
       # key和digestmod参数必须指定,key和msg(需要加密的内容)均为bytes类型,digestmod指定加密算法,比如‘md5’,'sha1’等
       hm =  hmac.new(b'hash',b'helloworld',digestmod='sha1')
       print("hmac二进制加密:", hm.digest())
       print("hmac十六进制加密:", hm.hexdigest())
       
       print("===hmac中文加密示例===")
       ch = hmac.new("你好".encode(encoding="utf-8"), "世界".encode(encoding="utf-8"),digestmod='sha1')
       print("hmac二进制加密:", ch.digest())
       print("hmac十六进制加密:", ch.hexdigest())
       
       # ===hmac字符加密示例===
       # hmac二进制加密: b'\x85Bv\x94/\xbd\xab!wgQPW\xa13GO\xb0\xfd\x83'
       # hmac十六进制加密: 854276942fbdab217767515057a133474fb0fd83
       # ===hmac中文加密示例===
       # hmac二进制加密: b'\xf3\x9b*g"\xf2\x92iB\xa6\x0c\x80U@\x19X\xb7\x13\xd8\x1e'
       # hmac十六进制加密: f39b2a6722f2926942a60c8055401958b713d81e
      
  • copy模块

    •  # copy模块里有copy和deepcopy两个函数,分别用来对数据进行深复制和浅复制。
       
      import copy
      
      nums = [1, 5, 3, 8, [100, 200, 300, 400], 6, 7]
      nums1 = copy.copy(nums)  # 对nums列表进行浅复制
      nums2 = copy.deepcopy(nums)  # 对nums列表进行深复制
      
  • uuid模块

    • 方法作用
      uuid.uuid1()基于MAC地址,时间戳,随机数来生成唯一的uuid,可以保证全球范围内的唯一性。
      uuid.uuid2()算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。不过需要注意的是python中没有基于DCE的算法,所以python的uuid模块中没有uuid2这个方法。
      uuid.uuid3(namespace,name)通过计算一个命名空间和名字的md5散列值来给出一个uuid,所以可以保证命名空间中的不同名字具有不同的uuid,但是相同的名字就是相同的uuid了。namespace并不是一个自己手动指定的字符串或其他量,而是在uuid模块中本身给出的一些值。比如uuid.NAMESPACE_DNS,uuid.NAMESPACE_OID,uuid.NAMESPACE_OID这些值。这些值本身也是UUID对象,根据一定的规则计算得出。
      uuid.uuid4()通过伪随机数得到uuid,是有一定概率重复的
      uuid.uuid5(namespace,name)uuid.uuid5(namespace,name) 和uuid3基本相同,只不过采用的散列算法是sha1
    •  # 一般而言,在对uuid的需求不是很复杂的时候,uuid1或者uuid4方法就已经够用了,使用方法如下:
      
       import uuid
      
       print(uuid.uuid1())  # 根据时间戳和机器码生成uuid,可以保证全球唯一
       print(uuid.uuid4())  # 随机生成uuid,可能会有重复
      
       # 使用命名空间和字符串生成uuid.
       # 注意一下两点:
       # 1. 命名空间不是随意输入的字符串,它也是一个uuid类型的数据
       # 2. 相同的命名空间和想到的字符串,生成的uuid是一样的
       print(uuid.uuid3(uuid.NAMESPACE_DNS, 'hello'))
       print(uuid.uuid5(uuid.NAMESPACE_OID, 'hello'))
      
      

自定义模块

除了使用系统提供的内置模块以外,我们还能自己写一个模块供自己的程序使用。一个py文件就是一个模块,所以,自定义模块很简单,基本上相当于创建一个py文件。但是,需要注意的是,如果一个py文件要作为一个模块被别的代码使用,这个py文件的名字一定要遵守标识符的命名规则。

__all__的使用
使用from <模块名> import *导入一个模块里所有的内容时,本质上是去查找这个模块的__all__属性,将__all__属性里声明的所有内容导入。如果这个模块里没有设置__all__属性,此时才会导入这个模块里的所有内容。

模块里的私有成员
模块里以一个下划线_开始的变量和函数,是模块里的私有成员,当模块被导入时,以_开头的变量默认不会被导入。但是它不具有强制性,如果一个代码强行使用以_开头的变量,有时也可以。但是强烈不建议这样使用,因为有可能会出问题。

# test1.py 文件:模块里没有__all__属性

a = 'hello'
def fn():
    print('我是test1模块里的fn函数')
# test2.py 文件:模块里有__all__属性

x = '你好'
y = 'good'
def foo():
    print('我是test2模块里的foo函数')
__all__ = ('x','foo')
# test3.py 文件:模块里有以_开头的属性

m = '早上好'
_n = '下午好'
def _bar():
    print('我是test3里的bar函数')
# demo.py 文件

from test1 import *
from test2 import *
from test3 import *

print(a)
fn()

print(x)
# print(y) 会报错,test2的__all__里没有变量 y
foo()


print(m)
# print(_n)  会报错,导入test3时, _n 不会被导入

import test3
print(test3._n)  # 也可以强行使用,但是强烈不建议

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

 # test1.py 文件

def add(a,b):
    return a+b

# 这段代码应该只有直接运行这个文件进行测试时才要执行
# 如果别的代码导入本模块,这段代码不应该被执行
ret = add(12,22)
print('测试的结果是',ret)
# demo.py 文件

import test1.py   # 只要导入了tets1.py,就会立刻执行 test1.py 代码,打印测试内容

为了解决这个问题,python在执行一个文件时有个变量__name__.在Python中,当直接运行一个py文件时,这个py文件里的__name__值是__main__,据此可以判断一个一个py文件是被直接执行还是以模块的形式被导入。
name:当直接运行这个py文件的时候,值是__main__
如果这个py文件作为一个模块导入的时候,值是文件名

def add(a,b):
    return a+b

if __name__ == '__main__':  # 只有直接执行这个py文件时,__name__的值才是 __main__
    # 以下代码只有直接运行这个文件才会执行,如果是文件被别的代码导入,下面的代码不会执行
    ret = add(12,22)
    print('测试的结果是',ret)

包的使用

一个模块就是一个 py 文件,在 Python里为了对模块分类管理,就需要划分不同的文件夹。多个有联系的模块可以将其放到同一个文件夹下,为了称呼方便,一般把 Python里的一个代码文件夹称为一个包。里面的各个模块可以正常创建

  • PyCharm 中创建 Python 包

    右键点击 PyCharm 中的 Python 工程根目录 , 选择 " New / Python Package " 选项 ,
    

    在这里插入图片描述

     输入 Python 包名称 , 然后点击回车 , 创建 Python 包 ;
    

    在这里插入图片描述

     创建完成后 , 自动生成了一个 my_package 目录 , 该目录下自动生成了一个 __init__.py 文件 ;
    

    在这里插入图片描述

__init__.py 代码
__init__.py 源码文件可以空着 , 但是必须要有 , 这是 Python 包的标志 ;
有了 __init__.py 源码文件之后 , 该目录才会被当做包对待
所以也可以直接去手动创建 __init__.py 空文件

普通目录图标与 包目录图标会与区别——中间有个点 (如下图)
在这里插入图片描述

现有以下包newmsg,包里由两个模块,分别是sendmsg.py、recvmsg.py文件。在包的上级文件夹里,有一个test.py文件,
使用__init__.py文件,结合__all__属性,导入包里的所有模块。

 # 在newmsg包里的__init__.py文件里编写代码:
 __all__ = ["sendmsg","recvmsg"]  # 指定导入的内容
# test.py文件代码:

from newmsg import *  # 将newmsg里的__inint__.py文件里,__all__属性对应的所有模块都导入
sendmsg.sendmsg()
recvmsg.recvmsg()

类与对象【面向对象】

面向过程:根据业务逻辑从上到下写代码。
面向对象:将变量与函数绑定到一起,分类进行封装,每个程序只要负责分配给自己的分类,这样能够更快速的开发程序,减少了重复代码。
在这里插入图片描述

面向对象编程有三大特性: 封装、继承和多态

封装: 函数是对语句的封装;类是对函数和变量的封装
继承: 类和类之间可以认为手动的建立父子关系,父类的属性和方法,子类可以使用
多态: 是一种技巧,提高代码的灵活度
定义简单的类(只包含方法)

面向对象是更大的封装,在一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!

定义类 :使用 class 来定义一个类
在Python中要定义一个只包含方法的类,语法格式如下:
class 类名:
        def 方法1(self,参数列表):
                 pass
         def 方法2(self,参数列表):
                 pass


方法的定义格式和之前学习过的函数一样
方法里的 第一个参数必须是self,大家暂时先记住,稍后介绍 self.
类名要遵守大驼峰命名法。

魔法方法

也叫魔术方法,是内里的特殊的一些方法

  • 特点:
    1.不需要手动调用,会在合适的时机自动调用
    2.这些方法,都是使用 __ 开始,使用 __ 结束(两个下_开始,两个_结束的方法)
    3.方法名都是系统规定好的,在合适的时机自己调用

  • __init__方法
    1.__init__()方法在创建对象时,会默认被调用,不需要手动的调用这个方法。
    2.__init__()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引用直接赋值给self
    3.在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。
    4.如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。
    5.方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法。

  • __del__方法
    1.创建对象后,python解释器默认调用__init__()方法
    2.而当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法。

  • __str__方法
    1.__str__方法返回对象的描述信息,使用print()函数打印对象时,其实调用的就是这个对象的__str__方法。
    2.如果想要修改对象的输出的结果,可以重写 __str__ 方法。
    3.一般情况下,我们在打印一个对象时,可能需要列出这个对象的所有属性。
    class Person:
    def __init__(self,name,age):
         self.name = name
         self.age = age
    
     def __str__(self):
         return '哈哈'
    
    p  = Person('张三',18)
    print(p)   # 哈哈  打印对象时,会自动调用对象的 __str__ 方法
    
    
    
    class Student:
      def __init__(self,name,score):
          self.name = name
          self.score = score
      def __str__(self):
          return '姓名是:{},成绩是{}分'.format(self.name,self.score)
    
    s = Student('lisi',95)
    print(s)   # 姓名是:lisi,成绩是95分
    

  • __repr__方法
    1.__repr__方法和__str__方法功能类似,都是用来修改一个对象的默认打印内容。在打印一个对象时,如果没有重写__str__方法,它会自动来查找__repr__方法。如果这两个方法都没有,会直接打印这个对象的内存地址。
    class Student:
        def __init__(self, name, score):
            self.name = name
            self.score = score
    
        def __repr__(self):
            return 'helllo'
    
    
    class Person:
        def __repr__(self):
            return 'hi'
    
        def __str__(self):
            return 'good'
    
    
    s = Student('lisi', 95)
    print(s)  # hello
    
    p = Person()
    print(p)  # good
    
    

  • __call__方法
    1.对象后面加括号,触发执行。
    class Foo:
        def __init__(self):
            pass
    
        def __call__(self, *args, **kwargs):
            print('__call__')
    
    
       obj = Foo()  # 执行 __init__
        obj()  # 执行 __call__
    


当创建一个对象时,会自动调用__init__方法,当删除一个对象时,会自动调用__del__方法。
使用__str__和__repr__方法,都会修改一个对象转换成为字符串的结果。一般来说,__str__方法的结果更加在意可读性,而__repr__方法的结果更加在意正确性(例如:datetime模块里的datetime类)

class Person(object):
    def __init__(self, name, age):
        # 在创建对象时,会自动调用这个方法
        print('__init__方法被调用了')
        self.name = name
        self.age = age

    def __del__(self):
        # 当对象被销毁时,会自动调用这个方法
        print('__del__ 方法被调用了')

    def __repr__(self):
        return 'hello'

    def __str__(self):
        return '姓名:{},年龄:{}'.format(self.name, self.age)

    def __call__(self, *args, **kwargs):
        # print('__call__ 方法被调用了')
        # args = (1, 2, 4, 5),kwargs = {'m':'good', 'n':'hehehe', 'p':'heiheihei'}
        print('args={},kwargs={}'.format(args, kwargs))


p = Person('zhangsan', 18)

# 如果不做任何的修改,直接打印一个对象,是文件的 __name__.类型 内存地址
# print(p)  # <__main__.Person object at 0x00000217467AEA08>

# 当打印一个对象的时候,会调用这个对象的 __str__ 或者 __repr__ 方法
# 如果两个方法都写了,选择 __str__
print(p)

# print(repr(p))  # 调用内置函数 repr 会触发对象的 __repr__ 方法
# print(p.__repr__())  # 魔法方法,一般不手动的调用

p(1, 2, 4, 5, m='good', n='hehehe', p='heiheihei')  # 对象名() ==> 调用这个对象的 p.__call__() 方法


# 打印结果如下
# __init__方法被调用了
# 姓名:zhangsan,年龄:18
# args=(1, 2, 4, 5),kwargs={'m': 'good', 'n': 'hehehe', 'p': 'heiheihei'}
# __del__ 方法被调用了

self的使用
给对象添加属性
python支持动态属性,当一个对象创建好了以后,直接使用 对象.属性名 = 属性值 就可以很方便的给对象添加一个属性。
哪个对象调用了方法,方法里的self指的就是谁。 通过 self.属性名 可以访问到这个对象的属性;通过 self.方法名() 可以调用这个对象的方法。


调用 __new__ 方法,用来申请内存空间


调用__init__方法传入参数,将 self 指向创建好的内存空间,填充数据


__slots__这个属性直接定义在类里,是一个元组,用来规定对象可以存在的属性

class Student(object):
    # 这个属性直接定义在类里,是一个元组,用来规定对象可以存在的属性
    # 这里面没有的属性  下方都不可以调用,会报错
    __slots__ = ('name', 'age', 'city')

    def __init__(self, x, y):
        self.name = x
        self.age = y

    def say_hello(self):
        print('大家好,我是', self.name)


# Student('张三',18) 这段代码具体做了什么呢?
# 1. 调用 __new__ 方法,用来申请内存空间
# 2. 调用 __init__ 方法传入参数,将 self 指向创建好的内存空间,填充数据
# 3. 变量 s 也指向创建好的内存空间
s = Student('张三', 18)

print(s.name) # 张三
s.say_hello() # 大家好,我是 张三

# 没有属性,会报错
# print(s.height)

# 直接使用等号给一个属性赋值
# 如果这个属性以前不存在,会给对象添加一个新的属性
# 动态属性
s.city = '上海'  # 给对象添加了一个city属性
print(s.city) # 上海

# 如果这个属性以前存在,会修改这个属性对应的值
s.name = 'jack'
print(s.name)  # jack

  • __eq__ 方法
    运算相关的魔法方法
    == 运算符本质其实是调用对象的 __eq__ 方法,获取 __eq__方法的返回结果
    != 本质是调用 __ne__ 方法 或者 __eq__ 方法取反
class Person:
  def __init__(self,name,age):
    self.name = name
    self.age = age

p1 = Person('zhangsan',18)
p2 = Person('zhangsan',18)
p2 = Person('zhangsan',19)

# p1 和 p2 不是同一个对象,比较内存地址
print('0x%X' % id(p1))  # 0x20CE2A8EE88
print('0x%X' % id(p2))  # 0x20CE2A8EF08

# is 身份运算符 可以用来判断两个对象是否是同一个对象
print('p1 is p2', p1 is p2)  # False

print(p1 == p2) # false
print(p1 == p3) # false

比较运算符相关魔法方法
__eq__ 如果不重写,默认比较依然是内存地址

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 使用== 运算符会自动调用这个方法
    def __eq__(self, other):
        return self.name == other.name and self.age == other.age

     # 使用 != 运算符会自动调用这个方法
    # def __ne__(self, other):  
 
    # 使用 <运算符会自动调用这个方法
    def __lt__(self, other):   
        return self.age < other.age

    # greater than 使用 > 会自动调用这个方法 
     # def __gt__(self, other):    

     # 使用 <=运算符会自动调用这个方法
    def __le__(self, other):  
        return self.age <= other.age
        
    #  使用 >= 运算符会自动调用
    # def __ge__(self, other):       


s1 = Student('zhangsan', 18)
s2 = Student('zhangsan', 18)
s3 = Student('lisi', 20)
print(s1 == s2)
print(s1 != s2)
print(s1 > s2)
print(s1 >= s2)
print(s1 <= s2)
print(s1 <= s2)

# True
# False
# False
# True
# True
# True

算数运算符相关魔法方法

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

     #  使用 >= 运算符会自动调用
    def __add__(self, other):   
        return self.age + other

    #  使用 - 运算符会自动调用
    def __sub__(self, other):
        return self.age - other

    #  使用 * 运算符会自动调用
    def __mul__(self, other):
        return self.age * other

    #  使用 / 运算符会自动调用
    def __truediv__(self, other):
        return self.age / other
 
    #  使用  % 运算符会自动调用
    def __mod__(self, other):
        return self.age % other
 
    #  使用** 运算符会自动调用
    def __pow__(self, power, modulo=None):
        return self.age ** power


s = Student('zhangsan', 18)
print(s + 1)  # 19
print(s - 2)  # 16
print(s * 2)  # 36
print(s / 5)  # 3.6
print(s % 5)  # 3
print(s ** 2)  # 324

类型转换相关魔法方法

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __int__(self):
        return self.age

    def __float__(self):
        return self.age * 1.0

    def __str__(self):
        return self.name

    def __bool__(self):
        return self.age > 18


s = Student('zhangsan', 18)
print(int(s)) # 18
print(float(s)) # 18.0
print(str(s)) # zhangsan
print(bool(s)) # False

if s:
    print('hello')
面向对象 练习
# 房子(House) 有 户型、总面积 、剩余面积(等于总面积的60%) 和 家具名称列表 属性
# 新房子没有任何的家具
# 将 家具的名称 追加到 家具名称列表 中
# 判断 家具的面积 是否 超过剩余面积,如果超过,提示不能添加这件家具

# 家具(Furniture) 有 名字 和 占地面积属性,其中
# 席梦思(bed) 占地 4 平米
# 衣柜(chest) 占地 2 平米
# 餐桌(table) 占地 1.5 平米
# 将以上三件 家具 添加 到 房子 中
# 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表


class House(object):
    # 缺省参数
    def __init__(self, house_type, total_area, fru_list=None):
        if fru_list is None:  # 如果这个值是None
            fru_list = []  # 将fru_list设置为空列表
        self.house_type = house_type
        self.total_area = total_area
        self.free_area = total_area * 0.6
        self.fru_list = fru_list

    def add_fru(self, x):  # x = bed
        if self.free_area < x.area:
            print('剩余面积不足,放不进去了')
        else:
            self.fru_list.append(x.name)
            self.free_area -= x.area

    # def __repr__(self):
    def __str__(self):
        return '户型={},总面积={},剩余面积={},家具列表={}'.format(self.house_type, self.total_area, self.free_area, self.fru_list)


class Furniture(object):
    def __init__(self, name, area):
        self.name = name
        self.area = area


# 创建房间对象的时候,传入户型和总面积
house = House('一室一厅', 20)  # 12

sofa = Furniture('沙发', 10)
bed = Furniture('席梦思', 4)
chest = Furniture('衣柜', 2)
table = Furniture('餐桌', 1.5)

# 把家具添加到房间里(面向对象关注点:让谁做)
house.add_fru(sofa)
house.add_fru(bed)
house.add_fru(chest)
house.add_fru(table)


print(house)  # print打印一个对象的时候,会调用这个对象的__repr__或者__str__ 方法,获取它们的返回值


# 剩余面积不足,放不进去了
# 剩余面积不足,放不进去了
# 户型=一室一厅,总面积=20,剩余面积=0.0,家具列表=['沙发', '衣柜']
内置属性

使用内置函数dir()可以查看一个对象支持的所有属性和方法,Python中存在着很多的内置属性。
类名.__dir__() 等同于dir(类名)



  • ___slots__
    Python中支持动态属性,可以直接通过点语法直接给一个对象添加属性,代码更加的灵活。但是在某些情况下,我们可能需要对属性进行控制,此时,就剋使用__slots__实现。
    class Person(object):
        __slots__ = ('name', 'age')
        def __init__(self, name, age):
            self.name = name
            self.age = age
    p = Person('张三', 18)
    p.name = '李四'
    
    # 对象p只能设置name和age属性,不能再动态添加属性
    # p.height = 180 # 报错
    


  • __doc__
    表示类的描述信息。
    class Foo:
        """ 描述类信息,这是用于…… """
        def func(self):
            pass
    
    print(Foo.__doc__)
    #输出: 描述类信息,这是用于……
    


  • __module__ __class__
    __module__ 表示当前操作的对象在那个模块;
    __class__ 表示当前操作的对象的类是什么。
    #  test.py
    class Person(object):
        def __init__(self):
          self.name = 'laowang'
     #  main.py
     from test import Person
     
    obj = Person()
    print(obj.__module__)  # 输出 test 即:输出模块
    print(obj.__class__)  # 输出 test.Person 即:输出类
    


  • __dict__
    以字典的形式,显示对象所有的属性和方法。
    class Province(object):
        country = 'China'
    
        def __init__(self, name, count):
            self.name = name
            self.count = count
    
        def func(self, *args, **kwargs):
            print('func')
    
    # 获取类的属性,即:类属性、方法、
    print(Province.__dict__)
    # 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}
    
    obj1 = Province('山东', 10000)
    print(obj1.__dict__)
    # 获取 对象obj1 的属性
    # 输出:{'count': 10000, 'name': '山东'}
    
    obj2 = Province('山西', 20000)
    print(obj2.__dict__)
    # 获取 对象obj1 的属性
    # 输出:{'count': 20000, 'name': '山西'}
    


  • __getitem__ __setitem____delitem__方法
    这三个方法,是将对象当做字典一样进行操作。
    class Foo(object):
    
        def __getitem__(self, key):
            print('__getitem__', key)
    
        def __setitem__(self, key, value):
            print('__setitem__', key, value)
    
        def __delitem__(self, key):
            print('__delitem__', key)
    
    obj = Foo()
    
    result = obj['k1']      # 自动触发执行 __getitem__
    obj['k2'] = 'laowang'   # 自动触发执行 __setitem__
    del obj['k1']           # 自动触发执行 __delitem__
    
    
类属性和对象属性

实例属性
通过类创建的对象被称为 实例对象,对象属性又称为实例属性,记录对象各自的数据,不同对象的同名实例属性,记录的数据各自独立,互不干扰。

class Person(object):
    def __init__(self,name,age):
        # 这里的name和age都属于是实例属性,每个实例在创建时,都有自己的属性
        self.name = name
        self.age = age

# 每创建一个对象,这个对象就有自己的name和age属性
p1 = Person('张三',18)
p2 = Person("李四",20)

类属性
类属性就是类对象所拥有的属性,它被该类的所有实例对象所共有,类属性可以通过类对象或者实例对象访问。

class Dog:
    type = "狗"  # 类属性

dog1 = Dog()
dog2 = Dog()

# 不管是dog1、dog2还是Dog类,都可以访问到type属性
print(Dog.type)  # 结果:狗
print(dog1.type)  # 结果:狗
print(dog2.type)  # 结果:狗

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

  • 注意点:
    1. 尽量避免类属性和实例属性同名。如果有同名实例属性,实例对象会优先访问实例属性。

       class Dog(object):
           type = "狗"  # 类属性
       
           def __init__(self):
               self.type = "dog"  # 对象属性
       
       # 创建对象 dog1 = Dog()
       
       print(dog1.type)     # 结果为 “dog”   类属性和实例属性同名,使用 实例对象 访问的是 实例属性
      
    2. 类属性只能通过类对象修改,不能通过实例对象修改

      lass Dog(object):
       type = "狗"  # 类属性
       
       # 创建对象 dog1 = Dog() dog1.type = "dog"   # 使用 实例对象 创建了对象属性type
       
       print(dog1.type)     # 结果为 “dog”   类属性和实例属性同名,访问的是实例属性
       print(Dog.type)      # 结果为 "狗"   访问类属性
       
       # 只有使用类名才能修改类属性 Dog.type = "土狗" print(Dog.type)  # 土狗 dog2 = Dog() print(dog2.type)  # 土狗 
      
    3. 类属性也可以设置为私有,前边添加两个下划线。 如:

       class Dog(object):
           count = 0  # 公有的类属性
           __type = "狗"  # 私有的类属性
       
       print(Dog.count)       # 正确 
       print(Dog.__type)      # 错误,私有属性,外部无法访问。
      
私有属性和方法

在实际开发中,对象的某些属性或者方法可能只希望在对象的内部别使用,而不希望在外部被访问到,这时就可以定义私有属性和私有方法。

定义方法
在定义属性或方法时,在属性名或者方法名前增加两个下划线__,定义的就是私有属性或方法。

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__money = 2000  # 使用 __ 修饰的属性,是私有属性

    def __shopping(self, cost):
        self.__money -= cost  # __money 只能在对象内部使用
        print('还剩下%d元'%self.__money)

    def test(self):
        self.__shopping(200)  # __shopping 方法也只能在对象内部使用

p = Person('张三',18)
# print(p.__money)   这里会报错,不能直接访问对象内部的私有属性
p.test()
# p.__shopping()  这里会报错,__shopping 只能在对象内部使用,外部无法访问

访问私有属性和方法
私有属性不能直接使用,私有方法不能直接调用。但是,通过一些代码,我们也可以在外部访问一个对象的私有属性和方法。

直接访问
使用方式:在私有属性名或方法名前添加 _类名

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__money = 2000

    def __shopping(self, cost):
        self.__money -= cost


p = Person('李四',20)
print(p._Person__money)  # 使用对象名._类名__私有属性名 可以直接访问对象的私有属性
p._Person__shopping(100)  # 使用对象名._类名__函数名 可以直接调用对象的私有方法
print(p._Person__money)

注意:在开发中,我们强烈不建议使用 对象名._类名__私有属性名 的方式来访问对象的私有属性!

定义方法访问私有变量
在实际开发中,如果对象的变量使用了__ 来修饰,就说明它是一个私有变量,不建议外部直接使用和修改。如果硬要修改这个属性,可以使用定义get和set方法这种方式来实现。

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__money = 2000  # __money 是私有变量,外部无法访问

    def get_money(self):  # 定义了get_money 方法,在这个方法里获取到 __money
        return self.__money  # 内部可以访问 __money 变量

    def set_money(self,money): # 定义了set_money 方法,在这个方法里,可以修改 __money
        self.__money = money

p = Person('王五',21)

# 外部通过调用 get_money 和 set_money 这两个公开方法获取和修改私有变量
print(p.get_money())
p.set_money(8000)
print(p.get_money())
类方法、静态方法
  1. 类方法
    第一个形参是类对象的方法
    需要用装饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数。

    class Dog(object):
        __type = "狗"
    
        # 类方法,用classmethod来进行修饰
        @classmethod
        def get_type(cls):
            return cls.__type
    print(Dog.get_type())
    

    使用场景:

    当方法中 需要使用类对象 (如访问私有类属性等)时,
    定义类方法 类方法一般和类属性配合使用

  2. 静态方法
    需要通过装饰器@staticmethod来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。
    静态方法 也能够通过 实例对象 和 类对象 去访问。

    class Dog(object):
        type = "狗"
    
        def __init__(self):
            name = None
    
        # 静态方法    
        @staticmethod
        def introduce():  # 静态方法不会自动传递实例对象和类对象
            print("犬科哺乳动物,属于食肉目..")
    
    dog1 = Dog()
    Dog.introduce()    # 可以用 实例对象 来调用 静态方法
    dog1.introduce()    # 可以用 类对象 来调用 静态方法
    

    使用场景:

    当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
    取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗

  3. 注意点:
    类中定义了同名的方法时,调用方法会执行最后定义的方法

    class Dog:
    
        def demo_method(self):
            print("对象方法")
    
        @classmethod
        def demo_method(cls):
            print("类方法")
    
        @staticmethod
        def demo_method():  # 被最后定义
            print("静态方法")
    
    dog1 = Dog()
    Dog.demo_method()  # 结果: 静态方法
    dog1.demo_method()  # 结果: 静态方法
    
class Person(object):
    type = 'human'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self, food):  # 对象方法有一个参数self,指的是实例对象
        print(self.name + '正在吃' + food)

    # 如果一个方法里没有用到实例对象的任何属性,可以将这个方法成static
    @staticmethod
    def demo():
        print('hello')

    @classmethod
    def test(cls):  # 如果这个函数只用到了类属性,我们可以把定义成为一个类方法
        # 类方法会有一个参数 cls,也不需要手动的传参,会自动传参
        # cls 指的是类对象    cls is Person
        print(cls.type)
        print('yes')


p1 = Person('张三', 18)
# 实例对象在调用方法时,不需要给形参self传参,会自动的把实例对象传递给self

p2 = Person('李四', 19)

# eat 对象方法,可以直接使用实例对象.方法名(参数)调用
# 使用对象名.方法名(参数)调用的方式,不需要传递self
# 会自动将对象名传递给self
p1.eat('红烧牛肉泡面')  # 直接使用实例对象调用方法
 # 张三正在吃红烧牛肉泡面

# 对象方法还可以使用 类对象来调用类名.方法名()
# 这种方式,不会自动给self传参,需要手动的指定self
Person.eat(p2, '西红柿鸡蛋盖饭')
# 李四正在吃西红柿鸡蛋盖饭

# print(p1.eat)
# print(p2.eat)
# print(Person.eat)

# 静态方法:没有用到实例对象的任何属性
Person.demo() # hello
p1.demo() # hello
#
# # 类方法:可以使用实例对象和类对象调用
p1.test()
# human
# yes
Person.test()
# human
# yes

在这里插入图片描述

子类重写父类方法
# 继承特点:如果一个类A继承自类B,由类A创建出来的实例对象都能直接使用类B里定义的方法


class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sleep(self):
        print(self.name + '正在睡觉')


class Student(Person):
    def __init__(self, name, age, school):
        # self.name = name
        # self.age = age
        # 子类在父类实现的基础上,又添加了自己新的功能
        # 调用父类方法的两种方式:
        # 1. 父类名.方法名(self,参数列表)
        # Person.__init__(self, name, age)
        
        # 2. 使用super直接调用父类的方法。推荐使用第二种方式
        super(Student, self).__init__(name, age)
        self.school = school

    def sleep(self):
        print(self.name + '正在课间休息时睡觉')

    def study(self):
        print(self.name + '正在学习')


s = Student('jerry', 20, '春田花花幼稚园')  # 调用了父类的 __init__ 方法
s.sleep()  # 调用了父类的 sleep方法
print(Student.__mro__)

# 1. 子类的实现和父类的实现完全不一样,子类可以选择重写父类的方法。
# 2. 子类在父类的基础上又有更多的实现

单例设计模式

__new____init__方法

class A(object):
    def __init__(self):
        print("这是 init 方法")

    def __new__(cls):
        print("这是 new 方法")
        return object.__new__(cls)

A()
  • . __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
  • .__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object__new__出来的实例
  • .__init__有一个参数self,就是这个__new__返回的实例,__init____new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
  • 举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。

    确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。

    # 实例化一个单例
    class Singleton(object):
       __instance = None
       __is_first = True
    
       def __new__(cls, age, name):
           if not cls.__instance:
               cls.__instance = object.__new__(cls)
           return cls.__instance
    
       def __init__(self, age, name):
           if self. __is_first: # 不会再创建第二个对象
               self.age = age
               self.name = name
               Singleton. __is_first = False
    
    
    a = Singleton(18, "张三")
    b = Singleton(28, "张三")
    
    print(id(a))
    print(id(b))
    
    print(a.age) # 18
    print(b.age) # 18
    
    a.age = 19
    print(b.age)
    
class Singleton:
    __instance = None  # 类属性
    __is_first = True

    @classmethod
    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            # 申请内存,创建一个对象,并把对象的类型设置为cls
            cls.__instance = object.__new__(cls)

        return cls.__instance

    def __init__(self, a, b):
        if self.__is_first:
            self.a = a
            self.b = b
            self.__is_first = False


# 调用 __new__ 方法申请内存
# 如果不重写 __new__ 方法,会调用 object 的 __new__ 方法
# object的 __new__ 方法会申请内存
# 如果重写了 __new__ 方法,需要自己手动的申请内存
s1 = Singleton('呵呵', '嘿嘿嘿')
s2 = Singleton('哈哈', '嘻嘻嘻')
s3 = Singleton('嘎嘎', '嘤嘤嘤')

print(s1 is s2)  # True
print(s1.a, s1.b) # 呵呵 嘿嘿嘿

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

继承的基本使用

语法
class 类名(object):
        pass

  • 在程序中,继承描述的是多个类之间的所属关系。
  • 如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里。
  • 那么类A就是基类,也叫做父类;类B就是派生类,也叫做子类。

单继承:子类只继承一个父类

子类继承自父类,可以享受父类中已经封装好的方法,不需要再次定义
子类中应该根据职责,封装子类特有的属性和方法。
子类拥有父类以及父类的父类中封装的所有属性和方法。
class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sleep(self):
        print(self.name + '正在睡觉')


class Dog(Animal):
    def bark(self):
        print(self.name + '正在叫')


class Student(Animal):
    def study(self):
        print(self.name + '正在好好学习')


# Dog() 调用 __new__ 方法,再调用 __init__ 方法
# Dog 里没有 __new__ 方法,会查看父类是否重写了 __new__ 方法
# 父类里也没有重写 __new__ 方法,查找父类的父类,找到了 object

# 调用 __init__ 方法,Dog类没有实现,会自动找 Animal 父类
d1 = Dog('大黄', 3)

# 父类里定义的属性,子类可以直接使用
print(d1.name) #大黄
d1.sleep()  # 父类的方法子类实例对象可以直接调用 #大黄正在睡觉
d1.bark() #大黄正在叫

s1 = Student('小明', 18)
s1.sleep() # 小明正在睡觉
s1.study() # 小明正在好好学习

多继承

Python中针对类提供了一个内置属性__mro__可以用来查看方法的搜索顺序。
MRO 是method resolution order的简称,主要用于在多继承时判断方法属性的调用顺序。

class A(object):
    def demo_a(self):
        print('我是A类里的方法demo_a')

    def foo(self):
        print('我是A类里的foo方法')


class B(object):
    def demo_b(self):
        print('我是B类里的方法demo_b')

    def foo(self):
        print('我是B类里的foo方法')


# Python里允许多继承
class C(A, B):
    pass


c = C()
c.demo_a() # 我是A类里的方法demo_a
c.demo_b() #我是B类里的方法demo_b

# 如果两个不同的父类有同名方法,有一个类属性可以查看方法的调用顺序
c.foo() # 我是A类里的foo方法
print(C.__mro__)  # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

继承顺序

在调用方法时,按照__mro__的输出结果从左至右的顺序查找。
如果再当前类中找到方法,就直接执行,不再向下搜索。
如果没有找到,就顺序查找下一个类中是否有对应的方法,如果找到,就直接执行,不再继续向下搜索。
如果找到了最后一个类,依然没有找到方法,程序就会报错。
class A(object):
    pass


class B(object):
    def foo(self):
        print('我是B类里的foo方法')


class C(A):
    def foo(self):
        print('我是C类里的foo方法')


class D(B):
    pass


class E(object):
    pass


class X(C, D, E):
    pass


x = X()
x.foo()
print(X.__mro__)
# (<class '__main__.X'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class 'object'>)

自己类里定义的私有方法 对象名._类名__私有方法名()

class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__money = 1000

    def eat(self):
        print(self.name + '正在吃东西')

    def __test(self):
        print('我是Animal类里的test方法')


class Person(Animal):
    def __demo(self):
        print('我是Person里的私有方法')


p = Person('张三', 18)
print(p.name)
p.eat()
p._Person__demo()  # 自己类里定义的私有方法   对象名._类名__私有方法名()
p._Animal__test()  # 可以通过 对象名._父类名__私有方法调用()

# 私有属性和方法,子类不会继承
# p._Person__test()  # 父类的私有方法,子类没有继承
# print(p._Person__money)


print(p._Animal__money)

新式类和经典类的概念:

  1. 新式类:继承自 object 的类我们称之为新式类
  2. 经典类:不继承自 object 的类

在python2里,如果不手动的指定一个类的父类是object,这个类就是一个经典类
python3里不存在 经典类,都是新式类

在这里插入图片描述

在这里插入图片描述

对象相关的内置函数

Python中的身份运算符用来判断两个对象是否相等;isinstance用来判断对象和类之间的关系;issublcass用啊里判断类与类之间的关系。

身份运算符
身份运算符用来比较两个对象的内存地址,看这两个对象是否是同一个对象。

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


p1 = Person('张三', 18)
p2 = Person('张三', 18)
p3 = p1

print(p1 is p2)  # False
print(p1 is p3)  # True

isinstance
instance内置函数,用来判断一个实例对象是否是由某一个类(或者它的子类)实例化创建出来的。

c

lass Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


class Student(Person):
    def __init__(self, name, age, score):
        super(Student, self).__init__(name, age)
        self.score = score


class Dog(object):
    def __init__(self, name, color):
        self.name = name
        self.color = color


p = Person('tony', 18)
s = Student('jack', 20, 90)
d = Dog('旺财', '白色')

print(isinstance(p, Person))  # True.对象p是由Person类创建出来的
print(isinstance(s, Person))  # True.对象s是有Person类的子类创建出来的
print(isinstance(d, Person))  # False.对象d和Person类没有关系

issubclass
issubclass 用来判断两个类之间的继承关系。

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


class Student(Person):
    def __init__(self, name, age, score):
        super(Student, self).__init__(name, age)
        self.score = score


class Dog(object):
    def __init__(self, name, color):
        self.name = name
        self.color = color


print(issubclass(Student, Person))  # True
print(issubclass(Dog, Person))  # False
子类重写父类方法
# 继承特点:如果一个类A继承自类B,由类A创建出来的实例对象都能直接使用类B里定义的方法


class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sleep(self):
        print(self.name + '正在睡觉')


class Student(Person):
    def __init__(self, name, age, school):
        # self.name = name
        # self.age = age
        # 子类在父类实现的基础上,又添加了自己新的功能
        # 调用父类方法的两种方式:
        # 1. 父类名.方法名(self,参数列表)
        # Person.__init__(self, name, age)
        # 2. 使用super直接调用父类的方法。推荐使用第二种方式  self.name   self.age  可省略不写
        super(Student, self).__init__(name, age)
        self.school = school

    def sleep(self):
        print(self.name + '正在课间休息时睡觉')

    def study(self):
        print(self.name + '正在学习')


s = Student('jerry', 20, '春田花花幼稚园')  # 调用了父类的 __init__ 方法
s.sleep()  # 调用了父类的 sleep方法
print(Student.__mro__)

# jerry正在课间休息时睡觉
# (<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>)

# 1. 子类的实现和父类的实现完全不一样,子类可以选择重写父类的方法。
# 2. 子类在父类的基础上又有更多的实现

多态的使用

多态是基于继承,通过子类重写父类的方法,达到不同的子类对象调用相同的父类方法,得到不同的结果,提高代码的灵活度

不使用多态使用多态

class PoliceDog(object):
  def attack_enemy(self):
    print(‘警犬正在攻击坏人’)

class BlindDog(object):
   def lead_road(self):
    print(‘导盲犬正在领路’)

class DrugDog(object):
  def search_drug(self):
    print(‘缉毒犬正在搜毒’)

class Person(object):
  def __init__(self, name):
    self.name = name
    self.dog = None
  def work_with_pd(self):
     if self.dog is not None:
      self.dog.attack_enemy()
  def work_with_bd(self):
    if self.dog is not None:
      self.dog.lead_road()
  def work_with_dd(self):
     if self.dog is not None:
      self.dog.search_drug()

p = Person(‘张三’)

pd = PoliceDog()
p.dog = pd
p.work_with_pd()

bd = BlindDog()
p.dog = bd
p.work_with_bd()

dd = DrugDog()
p.dog = dd
p.work_with_dd()
class Dog(object):
  def work(self):
    print('狗正在工作')

class PoliceDog(Dog):
   def work(self):
    print(‘警犬正在攻击敌人’)

class BlindDog(Dog):
  def work(self):
     print(‘导盲犬正在领路’)

class DrugDog(Dog):
  def work(self):
     print(‘缉毒犬正在搜毒’)

class Person(object):
  def __init__(self, name):
    self.name = name
    self.dog = None
   def work_with_dog(self):
     if self.dog is not None and isinstance(self.dog, Dog):
     self.dog.work()



p = Person(‘张三’)

pd = PoliceDog()
p.dog = pd
p.work_with_dog()

bd = BlindDog()
p.dog = bd
p.work_with_dog()

dd = DrugDog()
p.dog = dd
p.work_with_dog()

名片管理系统(练习)

user_list = [
    {'name': '张三', 'tel': '15112345678', 'qq': '911021120'},
    {'name': 'lisi', 'tel': '18112345678', 'qq': '1010212015'},
    {'name': 'jack', 'tel': '19903214568', 'qq': '320156201'}
]

# 添加用户
def add_user():
    name = input('请输入用户名:')
    for u in user_list:
        if u['name'] == name:
            print('用户名已经被占用')
            return # 如果用户名存在,直接结束这个给函数
    else:
        tel = input('请输入手机号:')
        qq = input('请输入QQ号:')
        user = {'name': name, 'tel': tel, 'qq': qq}
        user_list.append(user)
    print(user_list)


def check_number(n):
    if n.isdigit():
        n = int(n)
        if 0 <= n < len(user_list):
            return True
    return False

# 删除用户
def del_user():
    number = input('请输入要删除的序号(序号从0开始):')
    is_valid = check_number(number)
    if is_valid:
        answer = input('你确定要删除么?yes or no:  ')
        if answer.lower() == 'yes':
            user_list.pop(int(number))
    else:
        print('输入的序号不合法')

    print(user_list)

# 修改用户
def modify_user():
    number = input('请输入要修改的序号(序号从0开始):')
    if check_number(number):
        user = user_list[int(number)]
        # print('您要修改的信息是:\n姓名:{},手机号:{},QQ号:{}'.format(user['name'], user['tel'], user['qq']))
        print('您要修改的信息是:\n姓名:{name},手机号:{tel},QQ号:{qq}'.format(**user))
        new_name = input('请输入新的姓名:')
        for u in user_list:
            if u['name'] == new_name:
                print('新用户名已经存在')
                modify_user()
                return
        else:
            new_tel = input('请输入新的手机号:')
            new_qq = input('请输入新的QQ号:')
            if new_name == user['name'] and new_tel == user['tel'] and new_qq == user['qq']:
                print('信息未修改')
            else:
                user['name'] = new_name
                user['tel'] = new_tel
                user['qq'] = new_qq

# 查询用户
def search_user():
    search_name = input('请输入要查询的姓名:')
    for u in user_list:
        if u['name'] == search_name:
            print('查询到的信息如下:\n姓名:{name},手机:{tel},QQ:{qq}'.format(**u))
            break
    else:
        print('没有您要找的信息....')

# 显示所有名片
def show_all():
    print('序号    姓名            手机号          QQ')
    for i, u in enumerate(user_list):
        print(i, u['name'].center(15), u['tel'].center(15), u['qq'].center(10))

# 退出系统
def exit_system():
    answer = input('亲, 你确定要退出么?~~~~(> _ <)~~~~(yes or no)')
    
    # 方法一
    return answer.lower() == 'yes'

	# 方法二(与下面方法二同时用,注掉方法一)
	# if answer.lower() == 'yes':
	# 	exit()  # 内置函数exit 可以用来结束整个程序  异常退出场景下可以赋值自定义数字exit(100) 
 

def start():
    while True:
        print(
            "------------菜单---------------\n名片管理系统 V1.0\n\t1:添加名片\n\t2:删除名片\n\t3:修改名片\n\t4:查询名片\n\t5:显示所有名片\n\t6:退出系统\n---------------------------"))
        operator = input('请输入要进行的操作(数字):')
        if operator == '1':
            add_user()
        elif operator == '2':
            del_user()
        elif operator == '3':
            modify_user()
        elif operator == '4':
            search_user()
        elif operator == '5':
            show_all()
        elif operator == '6':
        	# 方法一
            is_sure = exit_system()
            if is_sure:
                break

			# 方法二(与上面方法二同时用,注掉方法一)
			# exit_system()
        else:
            print('您输入的不合法,请重新输入')


if __name__ == '__main__':
    start()

在这里插入图片描述

文件的打开与关闭

打开文件

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

  • xxx.txt 写入时,使用的utf8编码格式
    在windows操作系统里,默认使用gbk编码格式打开文件
    解决方案: 写入和读取使用相同的编码格式。
    file = open(‘xxx.txt’, encoding=‘utf8’)
  • windows系统里,文件夹之间使用 \ 分隔.
    在非windows系统里,文件夹之间使用 / 分隔.
    在Python的字符串里, \ 表示转义字符
    路径书写的三种方式: 1.\\ 2. r'\' 3. '/'(推荐)
  • 绝对路径: 从电脑盘符开始的路径。
  • 相对路径:当前文件所在的文件夹开始的路径。
    ../ 表示返回到上一级文件夹
    ./可以省略不写,表示当前文件夹
    / 不能随便用

open(file, mode='r', encoding=None, errors=None)

open 参数介绍
   file:用来指定打开的文件(不是文件的名字,而是文件的路径)(用于表示要打开的文件,可以是字符串或整数。如果file是字符串,则表示文件名,文件名既可以是当前目录的相对路径,也可以是绝对路径;如果file是整数,则表示一个已经打开的文件。)
   mode:打开文件时的模式,默认是 r 表示只读。(用于设置文件打开模式,用字符串表示,例如rb表示以只读模式打开二进制文件。用于设置文件打开模式的字符串中的每一个字符都表示不同的含义,)
   encoding:打开文件时的编码方式。(来指定打开文件时的文件编码,默认是UTF-8编码,主要用于打开文本文件。)
   errors: errors参数用来指定在文本文件发生编码错误时如何处理。推荐errors参数的取值为'ignore',表示在遇到编码错误时忽略该错误,程序会继续执行,不会退出。

open函数会有一个返回值,打开的文件对象

访问模式说明
r以只读方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,则报错。这是默认模式。
w打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
r打开一个文件用于读写。文件指针将会放在文件的开头。
w+打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a+     打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab+以二进制格式打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

关闭文件

close( )

写数据

write()

f = open('文件.txt', 'w')
f.write('hello world, i am here!\n' * 5)
f.close()

读数据

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

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

f = open('文件.txt', 'r')# 拿到写数据的内容为例子
content = f.read(5)  # 最多读取5个数据
print(content)

print("-"*30)  # 分割线,用来测试

content = f.read()  # 从上次读取的位置继续读取剩下的所有的数据
print(content)

f.close()  # 关闭文件,这个可是个好习惯哦

# hello
#------------------------------
# world, i am here!
# 优化:没有绝对的优化,除非提升硬件
# 读取大的文件
# 分步读取 内存占比会小很多
file = open('../大的文件.mp4', 'rb')
# print(file.read())
while True:
    content = file.read(1024)
    if not content:
        break
    print(content)

file.close()

指针定位

  • tell() 方法用来显示当前指针的位置

    f = open('test.txt')
    print(f.read(10))  # read 指定读取的字节数
    print(f.tell())    # tell()方法显示当前文件指针所在的文字
    f.close()
    
  • seek(offset,whence) 方法用来重新设定指针的位置。

    • offset:表示偏移量
    • whence:只能传入012中的一个数字。
      • 0表示从文件头开始
      • 1表示从当前位置开始
      • 2 表示从文件的末尾开始
    f = open('test.txt','rb')  # 需要指定打开模式为rb,只读二进制模式
    
    print(f.read(3))
    print(f.tell())
    
    f.seek(2,0)   # 从文件的开头开始,跳过两个字节
    print(f.read())
    
    f.seek(1,1) # 从当前位置开始,跳过一个字节
    print(f.read())
    
    f.seek(-4,2) # 从文件末尾开始,往前跳过四个字节
    print(f.read())
    
    f.close()
    

文件的拷贝

# 提示输入文件
file_name = input("请输入要拷贝的文件名字:")

# 以读的方式打开文件
old_file = open(file_name, 'rb')

# 分割文件名和后缀名
file_names =file_name.rsplit('.', maxsplit=1)

# 组织新的文件名字
new_file_name = file_names[0] + '.bak.'+file_names[1]

# 创建新文件
newFile = open(new_file_name, 'wb')

# 把旧文件中的数据,一行一行的进行复制到新文件中
for lineContent in old_file.readlines():
    newFile.write(lineContent)

# 关闭文件
old_file.close()
newFile.close()

CSV文件 (了解)

CSV文件:Comma-Separated Values,中文叫逗号分隔值或者字符分割值,其文件以纯文本的形式存储表格数据。可以把它理解为一个表格,只不过这个表格是以纯文本的形式显示的,单元格与单元格之间,默认使用逗号进行分隔;每行数据之间,使用换行进行分隔。
文件后缀名 为.csv,用记事本打开 会正常显示不会乱码,用wps打开就是表格,或excel打开会部分乱码

name,age,score
zhangsan,18,98
lisi,20,99
wangwu,17,90
jerry,19,95

Python中的csv模块,提供了相应的函数,可以让我们很方便的读写csv文件。

CSV文件的写入

import csv  #内置模块

# 以写入方式打开一个csv文件
file = open('test.csv','w')

# 调用writer方法,传入csv文件对象,得到的结果是一个CSVWriter对象
writer = csv.writer(file)

# 调用CSVWriter对象的writerow方法,一行行的写入数据
writer.writerow(['name', 'age', 'score'])

# 还可以调用writerows方法,一次性写入多行数据
writer.writerows([['zhangsan', '18', '98'],['lisi', '20', '99'], ['wangwu', '17', '90'], ['jerry', '19', '95']])
file.close()

CSV文件的读取

import csv

# 以读取方式打开一个csv文件
file = open('test.csv', 'r')

# 调用csv模块的reader方法,得到的结果是一个可迭代对象
reader = csv.reader(file)

# 对结果进行遍历,获取到结果里的每一行数据
for row in reader:
    print(row)

file.close()

内存中写入数据 (了解)

除了将数据写入到一个文件以外,我们还可以使用代码,将数据暂时写入到内存里,可以理解为数据缓冲区。Python中提供了StringIO和BytesIO这两个类将字符串数据和二进制数据写入到内存里。

StringIO
StringIO可以将字符串写入到内存中,像操作文件一下操作字符串。

from io import StringIO

# 创建一个StringIO对象
f = StringIO()
# 可以像操作文件一下,将字符串写入到内存中
f.write('hello\r\n')
f.write('good')

# 使用文件的 readline和readlines方法,无法读取到数据
# print(f.readline())
# print(f.readlines())

# 需要调用getvalue()方法才能获取到写入到内存中的数据
print(f.getvalue())

f.close()

BytesIO
如果想要以二进制的形式写入数据,可以使用BytesIO类,它的用法和StringIO相似,只不过在调用write方法写入时,需要传入二进制数据。

from io import BytesIO

f = BytesIO()
f.write('你好\r\n'.encode('utf-8'))
f.write('中国'.encode('utf-8'))

print(f.getvalue())
f.close()

sys模块的使用 【标准输入&标准输出&错误输出】

sys.stdin 接收用户的输入,说白了就是读取键盘里输入的数据
      stdout和stdin默认都是控制台
sys.stdout 标准输出
sys.stderr 错误输出

import sys

# sys.stdin  接收用户的输入,说白了就是读取键盘里输入的数据
#
# stdout和stdin默认都是控制台
# sys.stdout 标准输出
# sys.stderr 错误输出

s_in = sys.stdin  # input就是读取 sys.stdin 里的数据


while True:
    # 输入一行回车打印刚才输入的一行 没有输入直接回车就结束了
    content = s_in.readline().rstrip('\n')  # hello\n  ==>hello   \n==> ''
    if content == '':
        break
    print(content)
import sys
# 将内容直接打印到  stdout.txt 文件里面 ,不会在控制台打印
m = open('stdout.txt', 'w', encoding='utf8')
sys.stdout = m
print('hello')
print('yes')
print('good')
m.close()

在这里插入图片描述

import sys
# 错误输出,将报错打印到 stderr.txt 文件里面,不会在控制台打印
# err == > error 错误
x = open('stderr.txt', 'w', encoding='utf8')
sys.stderr = x
print(1 / 0)
x.close()

在这里插入图片描述

序列化和反序列化

通过文件操作,我们可以将字符串写入到一个本地文件。但是,如果是一个对象(例如列表、字典、元组等),就无法直接写入到一个文件里,需要对这个对象进行序列化,然后才能写入到文件里。

设计一套协议,按照某种规则,把内存中的数据转换为字节序列,保存到文件,这就是序列化,反之,从文件的字节序列恢复到内存中,就是反序列化。

Python中提供了JSON和pickle两个模块用来实现数据的序列化和反序列化。

序列化:将数据从内存持久化保存到硬盘的过程
反序列化:将数据从硬盘加载到内存的过程

JSON模块

JSON(JavaScriptObjectNotation, JS对象简谱)是一种轻量级的数据交换格式,它基于 ECMAScript 的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。JSON的本质是字符串!

** 使用JSON实现序列化**

json里将数据持久有两个方法:>

dumps:将数据转换成为json字符串,不会将数据保存到文件里。
dump: 将数据转换成为json字符串的同时写入到指定文件。

json 反序列化也有两个方法:

loads: 将json字符串加载成为Python里的数据
load: 读取文件,把读取的内容加载成为Python里的数据

JSON提供了dump和dumps方法,将一个对象进行序列化。

dumps方法的作用是把对象转换成为字符串,它本身不具备将数据写入到文件的功能。

import json
file = open('names.txt', 'w')
names = ['zhangsan', 'lisi', 'wangwu', 'jerry', 'henry', 'merry', 'chris']
# file.write(names)  出错,不能直接将列表写入到文件里

# 可以调用 json的dumps方法,传入一个对象参数
result = json.dumps(names)

# dumps 方法得到的结果是一个字符串
print(type(result))  # <class 'str'>

# 可以将字符串写入到文件里
file.write(result)

file.close()

dump方法可以在将对象转换成为字符串的同时,指定一个文件对象,把转换后的字符串写入到这个文件里。

import json

file = open('names.txt', 'w')
names = ['zhangsan', 'lisi', 'wangwu', 'jerry', 'henry', 'merry', 'chris']

# dump方法可以接收一个文件参数,在将对象转换成为字符串的同时写入到文件里
json.dump(names, file)
file.close()

注意:如果是一个空对象,调用dumps方法转换成为一个JSON对象,得到的结果是null(JS里的空对象)

json.dumps(None)  # null

** 使用JSON实现反序列化**
使用loads和load方法,可以将一个JSON字符串反序列化成为一个Python对象。

loads方法需要一个字符串参数,用来将一个字符串加载成为Python对象。

import json

# 调用loads方法,传入一个字符串,可以将这个字符串加载成为Python对象
result = json.loads('["zhangsan", "lisi", "wangwu", "jerry", "henry", "merry", "chris"]')
print(type(result))  # <class 'list'>

load方法可以传入一个文件对象,用来将一个文件对象里的数据加载成为Python对象。

import json

# 以可读方式打开一个文件
file = open('names.txt', 'r')

# 调用load方法,将文件里的内容加载成为一个Python对象
result = json.load(file)

print(result)
file.close()

pickle模块

序列化

dumps:将Python数据转换成为二进制
dump:将Python数据转换成为二进制,同时保存到指定文件

反序列化 loads:

将二进制加载成为Python数据
load:读取文件,并将文件的二进制内容加载成为Python数据

和json模块类似,pickle模块也有dump和dumps方法可以对数据进行序列化,同时也有load和loads方法进行反序列化。区别在于,json模块是将对象转换成为字符串,而pickle模块是将对象转换成为二进制。

pickle模块里方法的使用和json里方法的使用大致相同,需要注意的是,pickle是将对象转换成为二进制,所以,如果想要把内容写入到文件里,这个文件必须要以二进制的形式打开。

区别(了解)

  • json模块:

    • 将对象转换成为字符串,不管是在哪种操作系统,哪种编程语言里,字符串都是可识别的。

    • json就是用来在不同平台间传递数据的。

    • 并不是所有的对象都可以直接转换成为一个字符串,下标列出了Python对象与json字符串的对应关系。

      PythonJSON
      dictobject
      list,tuplearray
      strstring
      int,floatnumber
      Truetrue
      Falsefalse
      Nonenull
  • 如果是一个自定义对象,默认无法装换成为json字符串,需要手动指定JSONEncoder.

  • 如果是将一个json串重新转换成为对象,这个对象里的方法就无法使用了。

    import json
    class MyEncode(json.JSONEncoder):
        def default(self, o):
            # return {"name":o.name,"age":o.age}
            return o.__dict__
    
    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
          def eat(self):
              print(self.name+'正在吃东西')
    
    p1 = Person('zhangsan', 18)
    
    # 自定义对象想要转换成为json字符串,需要给这个自定义对象指定JSONEncoder
    result = json.dumps(p1, cls=MyEncode)
    print(result)  # {"name": "zhangsan", "age": 18}
    
    # 调用loads方法将对象加载成为一个对象以后,得到的结果是一个字典
    p = json.loads(result)
    print(type(p))
    
  • pickle模块:

    • pickle序列化是将对象按照一定的规则转换成为二进制保存,它不能跨平台传递数据。
    • pickle的序列化会将对象的所有数据都保存。

在这里插入图片描述

异常的处理

程序在运行过程中,由于我们的编码不规范,或者其他原因一些客观原因,导致我们的程序无法继续运行,此时,程序就会出现异常。如果我们不对异常进行处理,程序可能会由于异常直接中断掉。为了保证程序的健壮性,我们在程序设计里提出了异常处理这个概念。

try…except语句

try…except语句可以对代码运行过程中可能出现的异常进行处理。

语法结构:
try:
   可能会出现异常的代码块
except 异常的类型:
  出现异常以后的处理语句


except Exception as e: # 给异常起了一个变量名 e

try:
    f = open('test.txt', 'r')
    print(f.read())
except FileNotFoundError:
    print('文件没有找到,请检查文件名称是否正确')

try…else语句

咱们应该对else并不陌生,在if中,它的作用是当条件不满足时执行的实行;同样在try…except…中也是如此,即如果没有捕获到异常,那么就执行else中的事情

age = input('请输入您的年龄:')  # input接收到的用户输入是一个字符串

# if age.isdigit():
try:
    age = float(age)
except ValueError as e:
    print('输入的不是数字')
else:
    if age > 18:
        print('欢迎来到我的网站')
    else:
        print('未满18岁,请自动离开')

try…finally语句 finally关键字 最终都会被执行的代码

try…finally…语句用来表达这样的情况:

在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等。

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

我们可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。

with关键字的使用

对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。

比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 “Too many open files” 的错误,因为系统允许你打开的最大文件数量是有限的。

同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 “Can not connect to MySQL server Too many connections”,因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。

来看看如何正确关闭一个文件。

  • 普通版:

    def m1():
        f = open("output.txt", "w")
        f.write("python之禅")
        f.close()
    

    这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。那么该如何改进代码呢?

  • 进阶版:

    def m2():
        f = open("output.txt", "w")
        try:
            f.write("python之禅")
        except IOError:
            print("oops error")
        finally:
            f.close()
    

    改良版本的程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭。

  • 高级版:

    def m3():
        with open("output.txt", "r") as f:
            f.write("Python之禅")
    

    一种更加简洁、优雅的方式就是用 with 关键字。open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。

上下文管理器
with语句实质上是一个上下文管理器,with语句后的对象都会有__enter__()__exit__()方法。在进入到上下文时,会自动调用__enter__()方法,程序正常执行完成,或者出现异常中断的时候,都会调用__exit__()方法。
比如说 文件连接,socket连接,数据库的连接 都能使用 with关键自动关闭连接

class MyContext(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __enter__(self):
        print('调用了enter方法')
        return self

    def test(self):
        # 1 / 0
        print(self.name + '调用了test方法')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('调用了exit方法')
        print(exc_type, exc_val, exc_tb)

with MyContext('zhangsan', 18) as context:
    context.test()

#调用了enter方法
#zhangsan调用了test方法
#调用了exit方法
#None None None

自定义异常【raise 引发一个自定义的异常】

系统内置的异常:
ZeroDivisionError:除以0的异常 1 / 0
FileNotFoundError:文件不存在异常 open(‘xxx.txt’)
FileExistsError: 多次创建同名的文件夹 os.mkdir(‘test’)
ValueError: int(‘hello’)
KeyError: person = {‘name’:‘zhangsan’} person[‘age’]
SyntaxError: print(‘hello’,‘good’)
IndexError names = [‘zhangsan’,‘lisi’] names[5]

# 要求:让用户输入用户名和密码,如果用户名和密码的长度在 6~12 位正确,否则不正确
from exceptions import LengthError

password = input('请输入您的密码:')
m = 6
n = 12
if m <= len(password) <= n:
    print('密码正确')
else:
    # print('密码格式不正确')
    # 使用 raise 关键字可以抛出一个异常
    raise LengthError(m, n)

# 把密码保存到数据库里
print('将密码保存到数据库')


# 请输入您的密码:1
# Traceback (most recent call last):
#   File "D:\????????.py", line 12, in <module>
#     raise LengthError(m, n)
# exceptions.LengthError: 长度必须要在6至12之间
class ShortInputException(Exception):
    '''自定义的异常类'''
    def __init__(self, length, atleast):
        #super().__init__()
        self.length = length
        self.atleast = atleast

    def __str__(self):
        return '输入的长度是{},长度至少应是{}'.format(self.length, self.atleast)


# def main():
def main():
    try:
        s = input('请输入 --> ')
        if len(s) < 3:
            # raise 引发一个自定义的异常
            raise ShortInputException(len(s), 3)
    except ShortInputException as result:  # 这个变量被绑定到了错误的实例
        print('ShortInputException:' % result)
    else:
        print('没有异常发生.')


main()


# 请输入 --> 44
# Traceback (most recent call last):
#   File "D:\???????????.py", line 18, in main
#     raise ShortInputException(len(s), 3)
# ShortInputException: 输入的长度是2,长度至少应是3
# 
# During handling of the above exception, another exception occurred:
# 
# Traceback (most recent call last):
#   File "D:\???????.py", line 25, in <module>
#     main()
#   File "D:\???????.py", line 20, in main
#     print('ShortInputException:' % result)
#           ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
# TypeError: not all arguments converted during string formatting

练习

#定义一个点类 Pointer
#属性是横向坐标 x与 纵向坐标 y
#定义一个圆类 Circle
#属性有圆心点 cp 与 半径 radius
#方法有:
#1.求圆的面积
#2.求圆的周长
# 3.求指定点与圆的关系
#提示:关系有三种【圆内 圆外 圆上】
#设计到的数学公式:指定点与圆心点之间的距离 与 圆的半径进行比较

import math


class Pointer(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y


class Circle(object):
    def __init__(self, cp, radius):  # cp = p,radius = 3
        self.cp = cp
        self.radius = radius

    def get_area(self):
        return self.radius ** 2 * math.pi

    def get_length(self):
        return self.radius * 2 * math.pi

    def relationship(self, point):
        """
        求一个点和圆的关系。有三种关系:在圆内,在圆外,在圆上
        :param point: 需要判断的点
        """
        # 计算圆心到point的距离
        distance = (point.x - self.cp.x) ** 2 + (point.y - self.cp.y) ** 2
        if distance > self.radius ** 2:
            print('在圆外')
        elif distance < self.radius ** 2:
            print('在圆内')
        else:  # 等于的情况
            print('在圆上')


p = Pointer(3, 4)  # 创建了一个Pointer对象
c = Circle(p, 5)  # 创建好的Pointer对象传递给了Circle对象c
# print(hex(id(p)), hex(id(c)))

print(c.get_area()) # 78.53981633974483
print(c.get_length()) # 31.41592653589793

p1 = Pointer(10, 10)
c.relationship(p1) # 在圆外

p2 = Pointer(2, 2)
c.relationship(p2) #  在圆内

p3 = Pointer(0, 0)
c.relationship(p3) # 在圆上

# 学生类Student:
# 属性:学号,姓名,年龄,性别,成绩
# 班级类 Grade:
# 属性:班级名称,班级中的学生【使用列表存储学生】
# 方法:
# 1.查看该班级中的所有学生的信息
# 2.查看指定学号的学生信息
# 3.查看班级中成绩不及格的学生信息
#4.将班级中的学生按照成绩降序排序


class Student(object):
    def __init__(self, number, name, age, gender, score):
        self.number = number
        self.name = name
        self.age = age
        self.gender = gender
        self.score = score

    def __str__(self):
        return '编号:{},姓名:{},年龄:{},性别:{},分数{}'.format(self.number, self.name, self.age, self.gender,
                                                     self.score)


class Grade(object):
    def __init__(self, name, students):
        self.name = name
        self.students = students

    def show_all(self):
        for student in self.students:
            print(student)

    def get_student_by_number(self, n):
        for s in self.students:
            if s.number == n:
                return s
        else:
            return '用户未找到'

    def failed_students(self):
        result = filter(lambda student: student.score < 60, self.students)
        for x in result:
            print(x)

    def order_students(self):
        # self.students.sort(key=lambda s: s.score, reverse=True)  # 直接修改self.students
        return sorted(self.students, key=lambda s: s.score, reverse=True)


# 如果数字以 0 开头,在Python2里表示八进制
s1 = Student(1, 'zhangsan', 18, 'male', 80)
s2 = Student(5, '李四', 19, 'male', 50)
s3 = Student(10, 'tony', 20, 'male', 70)
s4 = Student(7, 'jack', 18, 'female', 90)
s5 = Student(4, 'henry', 19, 'female', 56)

g = Grade('中二班', [s1, s2, s3, s4, s5])
g.show_all()
print("^^^^^^^^^^^")
g.failed_students()
print("^^^^^^^^^^^")
x = g.order_students()
for student in x:
    print(student)

print("^^^^^^^^^^^")

print(g.get_student_by_number(7))


# 编号:1,姓名:zhangsan,年龄:18,性别:male,分数80
# 编号:5,姓名:李四,年龄:19,性别:male,分数50
# 编号:10,姓名:tony,年龄:20,性别:male,分数70
# 编号:7,姓名:jack,年龄:18,性别:female,分数90
# 编号:4,姓名:henry,年龄:19,性别:female,分数56
# ^^^^^^^^^^^
# 编号:5,姓名:李四,年龄:19,性别:male,分数50
# 编号:4,姓名:henry,年龄:19,性别:female,分数56
# ^^^^^^^^^^^
# 编号:7,姓名:jack,年龄:18,性别:female,分数90
# 编号:1,姓名:zhangsan,年龄:18,性别:male,分数80
# 编号:10,姓名:tony,年龄:20,性别:male,分数70
# 编号:4,姓名:henry,年龄:19,性别:female,分数56
# 编号:5,姓名:李四,年龄:19,性别:male,分数50
# ^^^^^^^^^^^
# 编号:7,姓名:jack,年龄:18,性别:female,分数90

迭代器

我们已经知道可以对list、tuple、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。

  • 如何判断一个对象是否是迭代器
    • 调用一个对象的__iter__方法,或者调用iter()内置函数,可以获取到一个可迭代对象的迭代器。

      names = ['hello', 'good', 'yes']
      print(names.__iter__())  # 调用对象的__iter__()方法
      print(iter(names))  # 调用iter()内置函数
      
    • 可以使用 isinstance() 判断一个对象是否是 Iterable 对象:

      from collections.abc import Iterator
      names = ['hello', 'good', 'yes']
      print(isinstance(iter(names), Iterator))
      
from collections.abc import Iterable
mylist = []

print(isinstance([], Iterable)) # True

print(isinstance({}, Iterable)) # True

print(isinstance('abc', Iterable)) # True

print(isinstance(mylist, Iterable)) # True

print(isinstance(100, Iterable)) # False

可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.

那么也就是说,一个具备了__iter__方法的对象,就是一个可迭代对象。

class Demo(object):
    def __init__(self, x):
        self.x = x
        self.count = 0

    def __iter__(self):  # 只要重写了 __iter__ 方法就是一个可迭代对象
        return self

    def __next__(self):
        # 每一次for...in都会调用一次__next__方法,获取返回值
        self.count += 1
        if self.count <= self.x:
            return self.count - 1
        else:
            raise StopIteration  # 让迭代器停止


d = Demo(10)
# isinstance:用来判断一个实例对象是否是有指定的类创建出来的
print(isinstance(d, Iterable))  # True

# for...in循环的本质就调用可迭代对象的 __iter__ 方法,获取到这个方法的返回值
# 这个返回值需要是一个对象,然后再调用这个对象 __next__ 方法
for i in Demo(10):
    print(i)
for…in…循环的本质

for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

class Demo(object):
    def __init__(self, x):
        self.x = x
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.count += 1
        if self.count <= self.x:
            return self.count - 1
        else:
            raise StopIteration


d = Demo(10)

# i = d.__iter__()
# i.__next__()
print(d.__iter__().__next__())  # 0
 # d.__iter__().__next__()  与 next(iter(d)) 一样
print(next(iter(d))) # 1

i = iter(d)  # 内置函数 iter 可以获取到一个可迭代对象的迭代器
print(next(i)) # 2
print(next(i)) # 3
print(next(i)) # 4
# ……

自定义 range (练习)

class Range(object):
    def __init__(self, end, start=0, step=1): # 如果给一个值,表示结束值
        if start != 0:
            start,end = end,start   # 假如传入例如开始值,且不为0时,互换 开始 结束 位置
        self.start = start
        self.end = end
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        self.start +=  self.step
        if self.start > self.end:
            raise StopIteration
        return self.start - 1



for i in Range(10,21, 3):
    print(i)

迭代器与列表区别

列表 会申请一块内存空间,用来保存1-1000000000的数字
   nums = [i for i in range(1000000000)]
   print(nums)


迭代器不睡开辟内存空间存储数据
  for i in range(1000000000):
     print(nums)




硬件不升级的情况下    时间换空间      空间换时间
数据过大的话 列表容易报错(比如执行1000000000时)
数据较小时  列表效率要高于迭代器(比如执行100时),数据过大时就不一定了

生成器

生成器是一类特殊的迭代器

创建生成器方法1 —— ( )

只要把一个列表生成式的 [ ] 改成 ( )

L = [x*2 for x in range(5)]
print(L) #[0, 2, 4, 6, 8]

G = (x*2 for x in range(5))
print(G) # <generator object <genexpr> at 0x000001E375B4F6B0>


# 创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。

print(next(G)) # 0
print(next(G)) # 2
print(next(G)) # 4
print( "……………………")
# 一步一打印  下面for循环接着上面打印的

for x in G:
   print(x)     # 6
                #8
创建生成器方法2 —— generator

如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字的 就称为 生成器通过使用生成器,可以生成一个值序列用于迭代,并且这个值序列不是一次生成的,而是使用一个再生成一个,最大的好处是可以使程序节约大量内存。

在Python程序中,生成器是记住上一次返回时在函数体中的具体位置的函数。对生成器的第二次(或n次)调用跳转至该函数中间,而上次所有得局部变量都保持不变。生成器不仅记住了他的数据状态,还记住了它在流控制构造中的位置。

  • 生成器的特点:
    • 生成器是一个函数,而且函数参数会保留

    • 当迭代到下一次调用的时候,所使用的参数都是第一次所保留的。也就是说在整个函数调用中的参数都是第一次所调用时保留的,而不是型创建的。

    • 生成器内部实现了迭代协议:意味者就实现了__next__和__iter__方法

    • 所以生成器是可迭代的

    • 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)

    • yield关键字有两点作用:

      • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
      • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
    • 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

    • Python3中的生成器可以使用return返回最终运行的返回值,而Python2中的生成器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但return后不能有任何表达式)。

# 迭代器是一个对象,定义class
# 生成器写法上像一个函数
def my_gen(n):
    i = 0
    while i < n:
        # return i  # 函数里的return表示函数的执行结束
        yield i  # yield关键字,将函数变成生成器
        i += 1


G = my_gen(10)
# print(next(iter(G)))
for i in G:
    print(i)

# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
使用send唤醒

我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

例子:执行到yield时,gen函数作用暂时保存,返回i的值; temp接收下次c.send(“python”),send发送过来的值,c.next()等价c.send(None)

send() 传参不能为空

综合练习 —— 学生管理系统

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

files → data.json —— 账号数据 可以为空 首次录入时会创建

jack 123456
tony 987654

{
	"jack": "fb5e792828620327ee791fb2d29b2858173803fd00f3483b7464a58b9d945e1f",
	"tony": "cd44195e0b4c2b7ac0e273db53af8505947923638e71686bdacd999659bb11ef"
}
files → jack.json —— Jack老师名下学生数据 可以为空 首次录入时会创建
{
	"all_student": [
		{
			"s_id": "stu_0001", 
			"name": "mery", 
			"age": "17", 
			"gender": "female", 
			"tel": "112"
		}, {
			"s_id": "stu_0002", 
			"name": "张三", 
			"age": "18", 
			"gender": "male", 
			"tel": "113"
		}, {
			"s_id": "stu_0003", 
			"name": "pink",
			"age": "19", 
			"gender": "female", 
			"tel": "114"
		}, {
			"s_id": "stu_0004", 
			"name": "李四", 
			"age": "16", 
			"gender": "male", 
			"tel": "116"
		}
	], 
	"num": 4
}
files → students_page.txt —— 老师进入到学生管理系统目录
====================================
🌺🌺欢迎%s老师进入到学生管理系统:1.  添加学生
    ♥ 2.  查看学生
    ♥ 3.  修改学生信息
    ♥ 4.  删除学生
    ♥ 5.  返回
======================================
files → tony.json —— tony老师名下学生 数据 可以为空 首次录入时会创建
{
  "all_student": [
    {
      "s_id": "stu_0001",
      "name": "zhangsan",
      "age": "12",
      "gender": "male",
      "tel": "213"
    }
  ],
  "num": 1
}
files → welcome.txt 学生管理系统登录目录
===================================
     ** 欢迎来到学生管理系统 **

      ♦️ 1. 登      录
      ♦️ 2. 注      册
      ♦️ 3. 退      出

===================================
file_manager.py —— 文件的读写 文件操作 封装方法 文件
# 文件的读写 文件操作 封装方法 文件
import json

# 定义变量 引入文件路径  方便修改 可以通过外部修改(参考 index,py 文件 注释的  修改操作)
base_dir = './files/'


# 读取文件
def read_file(file_name):
    try:
        with open(base_dir + file_name, 'r', encoding='utf8') as file:
            content = file.read()
            return content
    except FileNotFoundError:
        print('文件未找到')

# 写入 json文件
def write_json(file_name, data):
    with open(base_dir + file_name, 'w', encoding='utf8') as file:
        json.dump(data, file)

# 读取 json文件
def read_json(file_name, default_data):
    try:
        with open(base_dir + file_name, 'r', encoding='utf8') as file:
            return json.load(file)
    except FileNotFoundError:
        # print('文件未找到')
        return default_data
index.py —— 登录 注册 退出 功能文件
# 登录 注册 退出 功能文件
import sys

import file_manager
import model
import student_manager
import tools


# 注册
def register():
    # 读取文件,查看文件里是否有数据。如果文件不存在,默认是一个空字典
    data = file_manager.read_json('data.json', {})
    while True:
        teacher_name = input('请输入账号(3~6位):')
        if not 3 <= len(teacher_name) <= 6:
            print('账号不符合要求,请重新输入!')
        else:
            break

    if teacher_name in data:
        print('注册失败!该账号已经注册过!')
        return

    while True:
        password = input('请输入密码(6~12位):')
        if not 6 <= len(password) <= 12:
            print('密码不符合要求,请重新输入!')
        else:
            break

    # 用户名密码都已经输入正确以后,创建一个teacher对象
    t = model.Teacher(teacher_name, password)
    # 拼接字典   后面学到sql  就可以直接传数据了   sql.write(t)
    data[t.name] = t.password

    file_manager.write_json('data.json', data)

# 登录
def login():
    # 读取文件,查看文件里是否有数据。如果文件不存在,默认是一个字典

    data = file_manager.read_json('data.json', {})
    teacher_name = input('请输入老师账号:')

    if teacher_name not in data:
        print('登录失败!该账号没有注册!')
        return
    password = input('请输入密码:')
    #                             密码加密
    if data[teacher_name] == tools.encrypt_password(password):
        # 把用户名传给 student_manager.py 文件
        student_manager.name = teacher_name
        # 调用 展示 🌺🌺欢迎%s老师进入到学生管理系统: 目录 的方法
        student_manager.show_manager()
    else:
        print('密码错误,登录失败!')

# 登录 注册 退出 目录
def start():
    # 可以通过外部修改 file_manager.py 里面 变量base_dir的值
    # 不修改参数 就会读取file_manager.py 里面 默认值
    # file_manager.base_dir = "文件地址"
    #           模块名.方法名
    content = file_manager.read_file('welcome.txt')
    while True:
        operator = input(content + '\n请选择(1-3):')
        if operator == '1':
            login()
        elif operator == '2':
            register()
        elif operator == '3':
            # break  # 把死循环停止
            # exit(0)  # 退出整个程序
            sys.exit(0)
        else:
            print('输入有误!')


if __name__ == '__main__':
    start()
model.py —— 实例类 对象 封装 文件
#实例类 对象   封装 文件
class Teacher(object):
    def __init__(self, name, password):
        import tools
        self.name = name
        self.password = tools.encrypt_password(password)


class Student(object):
    def __init__(self, s_id, name, age, gender, tel):
        self.s_id = s_id
        self.name = name
        self.age = age
        self.gender = gender
        self.tel = tel
student_manager.py —— 老师进入到学生管理系统目录操作界面
# 🌺🌺欢迎%s老师进入到学生管理系统:


"""
jack   123456
学生管理系统的数据结构设置:
一个学生信息对应一个字典
整个系统的所有学生:所有学生对应一个列表,列表中的元素全是字典
整个系统:一个字典, 字典中有一个key(all_student)
对应的值是所有学生;
这个字典需要做数据持久化,将数据存储到json文件中, 文件名就是当前登录的账号名
jack.json
{
    'all_student': [
        {'name': '张三', 'age': 89, 'tel': '237837293'},
        {'name': '张三', 'age': 89, 'tel': '237837293'},
        {'name': '张三', 'age': 89, 'tel': '237837293'},
        {'name': '张三', 'age': 89, 'tel': '237837293'}
    ],
    'num': 4
}
"""

import file_manager
import model

name = ''


# 添加学生
def add_student():
    x = file_manager.read_json(name + '.json', {})
    if not x:
        students = []
        num = 0
    else:
        students = x['all_student']
        num = int(x['num'])
    while True:
        s_name = input('请输入学生姓名:')
        s_age = input('请输入年龄:')
        s_gender = input('请输入性别:')
        s_tel = input('请输入电话号码:')

        num += 1
        # 字符串的zfill方法,在字符串的前面补 0
        s_id = 'stu_' + str(num).zfill(4)

        # 创建一个Student对象
        s = model.Student(s_id, s_name, s_age, s_gender, s_tel)

        # {
        # 'all_student': [
        #       {'name':'zhangsan','age':18,'gender':'男','tel':'110'},
        #       {'name':'zhangsan','age':18,'gender':'男','tel':'110'},
        #   ],
        # 'num': 2
        # }
        students.append(s.__dict__)

        # 拼接字典
        data = {'all_student': students, 'num': len(students)}
        # print(data)
        # 把数据写入到文件里
        file_manager.write_json(name + '.json', data)
        choice = input('添加成功!\n1.继续\n2.返回\n请选择(1-2):')
        if choice == '2':
            break

# 查询学生
def show_student():
    x = input('1.查看所有学生\n2.根据姓名查找\n3.根据学号查找\n其他: 返回\n请选择:')
    y = file_manager.read_json(name + '.json', {})

    students = y.get('all_student', [])
    if not students:
        print('该老师还没有学员,请添加学员')
        return

    key = value = '' # 定义两个变量为空
    if x == '1':  # 查询所有
        pass # 不可以写 return 否者直接结束了
    elif x == '2':
        value = input('请输入学员姓名:')
        key = 'name'
    elif x == '3':
        value = input('请输入学员的id:')
        key = 's_id'
    else:
        return

    # 查找学员 名单 包含 重复
    # x == 2 || 3 时 可以拿到S的值 # s = {"s_id": "stu_0001", "name": "mery", "age": "17", "gender": "female", "tel": "112" }
    # x==1 时 s = [""] 所以 要用到 s.get(key, '') == value  # s.get('', '') 拿不到就使用第二个参数为默认值
    students = filter(lambda s: s.get(key, '') == value, students)

    if not students:
        print('未找到学员')
        return

    for student in students:
        print('学号:{s_id}, 姓名:{name},性别:{gender},年龄:{age}, ☎:{tel}'.format(**student))

#
def modify_student():
    y = file_manager.read_json(name + '.json', {})
    all_students = y.get('all_student', [])
    key = value = ''

    if not all_students:
        print('该老师还没有学员,请添加学员')
        return

    num = input('1.按姓名修改\n2.按学号修改\n其他:返回\n请选择:')
    if num == '1':
        key = 'name'
        value = input('请输入要修改的学生名字:')
    elif num == '2':
        key = 's_id'
        value = input('请输入要修改的学生id:')
    else:
        return

    students = list(filter(lambda s: s.get(key, '') == value, all_students))
    if not students:
        print('没有找到对应的学生')
        return

    for i, s in enumerate(students):
        print('学生的标号:{x} 学号:{s_id}, 姓名:{name},性别:{gender},年龄:{age}, ☎:{tel}'.format(x=i, **s))
    n = input('请输入需要修改的学生的标号(0~{}),q-返回:'.format(i))  # 使用变量 i 有潜在风险


    if not n.isdigit() or not 0 <= int(n) <= i:
        print('输入的内容不合法')
        return

    y['all_student'] = all_students
    while True:
        s_name = input('请输入学生姓名:')
        s_age = input('请输入年龄:')
        s_gender = input('请输入性别:')
        s_tel = input('请输入电话号码:')

        # 拿到当前学生的学号唯一的 不可修改的 ,精准替换信息
        # 获取当前学生的学号
        s_index = int(list(students[0].values())[0][4:])
        # 当前学生的学号
        oldS_id = 'stu_' + str(s_index).zfill(4)
        y['all_student'][s_index-1] = {"s_id": oldS_id, "name": s_name, "age": s_age, "gender": s_gender, "tel": s_tel}

        file_manager.write_json(name + '.json', y)

        choice = input('修改成功!\n1.继续\n2.返回\n请选择(1-2):')
        if choice == '2':
            break

#  删除学生
def delte_student():
    y = file_manager.read_json(name + '.json', {})
    all_students = y.get('all_student', [])
    key = value = ''

    if not all_students:
        print('该老师还没有学员,请添加学员')
        return

    num = input('1.按姓名删\n2.按学号删\n其他:返回\n请选择:')
    if num == '1':
        key = 'name'
        value = input('请输入要删除的学生名字:')
    elif num == '2':
        key = 's_id'
        value = input('请输入要删除的学生id:')
    else:
        return

    students = list(filter(lambda s: s.get(key, '') == value, all_students))
    if not students:
        print('没有找到对应的学生')
        return

    for i, s in enumerate(students):
        print('学生的标号:{x} 学号:{s_id}, 姓名:{name},性别:{gender},年龄:{age}, ☎:{tel}'.format(x=i, **s))
    n = input('请输入需要删除的学生的标号(0~{}),q-返回:'.format(i))  # 使用变量 i 有潜在风险

    if not n.isdigit() or not 0 <= int(n) <= i:
        print('输入的内容不合法')
        return

    # 将学生从all_students里删除
    all_students.remove(students[int(n)])

    y['all_student'] = all_students
    file_manager.write_json(name + '.json', y)


# 展示 🌺🌺欢迎%s老师进入到学生管理系统: 目录
def show_manager():
    content = file_manager.read_file('students_page.txt') % name
    while True:
        print(content)
        operator = input('请选择(1-5):')
        if operator == '1':
            add_student()
        elif operator == '2':
            show_student()
        elif operator == '3':
            modify_student()
        elif operator == '4':
            delte_student()
        elif operator == '5':
            break
        else:
            print('输入有误!')

tools.py —— 工具方法的封装 文件(密码加密)
# 工具方法的封装  文件
import hashlib

# 密码加密
# x 是任意编写数据 拼接到实际密码的后面 进行加密,可以通过传参或者设置默认值
def encrypt_password(passwd, x='afsoijweogj4wiu'):
    h = hashlib.sha256()
    h.update(passwd.encode('utf8'))
    h.update(x.encode('utf8'))
    return h.hexdigest()

正则表达式

正则表达式是一个特殊的字符序列,计算机科学的一个概念。通常被用来检索、替换那些符合某个模式(规则)的文本。

许多程序设计语言都支持利用正则表达式进行字符串操作。在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用re模块。re 模块使 Python 语言拥有全部的正则表达式功能。

  • Python中的正则表达式
    • 与大多数编程语言相同,正则表达式里也使用\作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符\,那么使用编程语言表示的正则表达式里将需要4个反斜杠\:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。

      print(re.match('\\\\', '\hello'))  # 需要使用四个反斜杠来匹配一个 \
      
    • Python里的原生字符串很好地解决了这个问题,有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。在Python 字符串前面添加r即可将字符串转换成为原生字符串。

      print(re.match(r'\\', '\hello')) # 使用两个反斜杠即可匹配一个 \
      

查找方法的使用

在Python中的查找匹配方法,常见的有下面几种,他们的用法大致相同,但是匹配出的结果却不同。

match方法(只匹配字符串开头)
search方法(扫描整个字符串,找到第一个匹配)
findall方法(扫描整个字符串,找到所有的匹配)
finditer方法(扫描整个字符串,找到所有的匹配,并返回一个可迭代对象)
fullmatch方法完整匹配,字符串需要完全满足正则规则才会有结果,否则就是None
  • match方法的使用
    re.match尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

    函数语法:
    re.match(pattern,string,flags=0)

    参数描述
    pattern匹配的正则表达式
    string要匹配的字符串。
    flags标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
    # 我们可以使用group(num)函数来获取匹配表达式。
    
    import re
    result1 = re.match(r'H','Hello')
    result2 = re.match(r'e','Hello')
    print(result1.group(0)) # 'H' 匹配到的元素
    print(result1.span()) # (0,1) 匹配到的元素所在位置
    print(result2)  # None
    
  • search方法的使用
    re.search 扫描整个字符串并返回第一个成功的匹配。

    函数语法:

    re.search(pattern, string, flags=0)

    import re
    result1 = re.search(r'He','Hello')
    result2 = re.search(r'lo','Hello')
    
    print(result1.group(0))  # He
    print(result1.span()) # (0,2)
    print(result2.group(0)) # lo
    print(result2.span()) # (3,5)
    
  • findall 方法的使用
    在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

    语法格式:

    re.findall(pattern,string,flags=0)

    import re
    ret = re.findall(r'\d+','he23ll34')
    print(ret)  # ['23', '34']
    ret = re.match(r'\d+','he23ll34') 
    print(ret) # None match只匹配开头,所以匹配到
    ret = re.search(r'\d+','he23ll34')
    print(ret) # <re.Match object; span=(2, 4), match='23'> search 只能匹配到一个数字
    
    • 注意事项:

      1. findall方法匹配时,如果匹配规则里有分组,则只匹配分组数据。

        import re
        ret = re.findall(r'\w+@(qq|126|163)\.com','123@qq.com;aa@163.com;bb@126.com')
        print(ret)  # ['qq', '163', '126']  只匹配到了分组里的内容
        
      2. 如果正则表达式里存在多个分组,则会把多个分组匹配成元组。

        import re
        ret = re.findall(r'\w+@(qq|126|163)(\.com)','123@qq.com;aa@163.com;bb@126.com')
        print(ret) #[('qq', '.com'), ('163', '.com'), ('126', '.com')]
        
      3. 如果想要让findall匹配所有的内容,而不仅仅只是匹配正则表达式里的分组,可以使用 ?:来将分组标记为非捕获分组。

        import re
        ret = re.findall(r'\w+@(?:qq|126|163)\.com','123@qq.com;aa@163.com;bb@126.com')
        print(ret) # ['123@qq.com', 'aa@163.com', 'bb@126.com']
        
  • finditer方法的使用
    和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

    import re
    ret = re.finditer(r'\d+','he23ll34')  # 得到的结果是一个可迭代对象
    for x in ret: # 遍历 ret 取出里面的每一项匹配
        print(x.group(), x.span()) # 匹配对象里的group保存了匹配的结果
    
  • re.match与re.search的区别

    共同点:

    1. 只对字符串查询一次
    2. 返回值类型都是 re.Match类型的对象

    不同点:

    1. match是从头开始匹配,一旦匹配失败,就返回None;
    2. search是在整个字符串里匹配
      ```python
      import re
      result1 = re.search(r'天气','今天天气不错哟')
      result2 = re.match(r'天气','今天天气不错哟')
      print(result1)  # <re.Match object; span=(2, 4), match='天气'>
      print(result2) # None
      ```
    
  • re.findall 、 re.finditer与re.match、re.search的区别

    match 和 search 是匹配第一次
    findall 和 finditer 匹配所有内容。

re.Match类介绍

当我们调用re.match方法、re.search方法,或者对re.finditer方法的结果进行迭代时,拿到的数据类型都是re.Match对象。

import re
x = re.match(r'h','hello')
y = re.search(r'e','hello')
z = re.finditer(r'l','hello')
print(type(x))  # <class 're.Match'>
print(type(y)) # <class 're.Match'>
for a in z:
    print(type(a)) # <class 're.Match'>

这个类里定义了相关的属性,可以直接让我们来使用。

属性和方法说 明
pos搜索的开始位置
endpos搜索的结束位置
string搜索的字符串
re当前使用的正则表达式的对象
lastindex最后匹配的组索引
lastgroup最后匹配的组名
group(index=0)某个分组的匹配结果。如果index等于0,便是匹配整个正则表达式
groups()所有分组的匹配结果,每个分组的结果组成一个列表返回
groupdict()返回组名作为key,每个分组的匹配结果座位value的字典
start([group])获取组的开始位置
end([group])获取组的结束位置
span([group])获取组的开始和结束位置
expand(template)使用组的匹配结果来替换模板template中的内容,并把替换后的字符串返回
import re
ret = re.search(r'(abc)+', 'xxxabcabcabcdef')

# 搜索开始的位置,默认是0
print(ret.pos)  # 0

# 搜索结束的位置,默认是字符串的长度
print(ret.endpos)  # 15

# abcabcabc 匹配整个表达式
print(ret.group(0)) # abcabcabc
print(ret.group()) # abcabcabc

# abc 第一次匹配到的结果
print(ret.group(1))  # abc

# 开始和结束位置
print(ret.span()) # (3, 12) 

# 表示当正则表达式里有多个分组时,多个分组的匹配结果
print(ret.groups())  # ('abc',)


# . 任意字符  * 出现任意次数  贪婪模式
m = re.search(r'm.*a', 'o3rjomjadas')  # <re.Match object; span=(5, 6), match='m'>
print(dir(m)) #['__class__', '__class_getitem__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']

print(m.pos, m.endpos) # 0 11
#匹配到的结果字符串的开始和结束下标
print(m.span())  # (5, 10)

# 使用group方法可以获取匹配的字符串结果
# group 可以传参,表示第 n 个分组
print(m.group())  # mjada
print(m.group(0))  # mjada
# print(m.group(1))  # IndexError: no such group

# group方法表示正则表达式的分组
# 1. 在正则表达式里使用 () 表示一个分组
# 2. 如果没有分组,默认只有一组
# 3. 分组的下标从 0 开始

# 正则表达式有 四个 分组
m1 = re.search(r'(9.*)(0.*)(5.*7)', 'da9fi0riel5kfsda7ifsdaiferit')
# 第 0 组就是把整个正则表达式当做一个整体
print(m1.group(0))  # 9fi0riel5kfsda7
# 默认就是拿第0组
print(m1.group())  # 9fi0riel5kfsda7
print(m1.group(1))  # 9fi
print(m1.group(2))  # 0riel
print(m1.group(3))  # 5kfsda7

print(m1.groups())  # ('9fi', '0riel', '5kfsda7')

# groupdict 作用是获取到分组组成的字典
print(m1.groupdict())  # {}


# r'(9.*)(0.*)(5.*7)'   r'9.*0.*5.*7'   加括号只是为了分组
m1_1 = re.search(r'9.*0.*5.*7', 'da9fi0riel5kfsda7ifsdaiferit')
print(m1_1.group())  # 9fi0riel5kfsda7
print(m1_1.groups())  # ()




# (?P<name>表达式)  可以给分组起一个名字
m2 = re.search(r'(9.*)(?P<xxx>0.*)(5.*7)', 'da9fi0riel5kfsda7ifsdaiferit')
print(m2.groupdict())  # {'xxx': '0riel'}

# print(m2.groupdict('xxx22'))  # {'xxx': '0riel'}

# 可以通过分组名或者分组的下标获取到分组里匹配到的字符串
print(m2.group('xxx'))  # 0riel
print(m2.group(2))  # 0riel

print(m2.span(2)) # (5, 10)

re.compile方法的使用

我们在使用正则表达式时,可以直接调用re 模块的 match,search,findall等方法,传入指定的正则表达式。同时,也可以调用re.compile方法,生成一个正则表达式对象,再调用这个正则表达式对象的相关方法实现匹配。

import re
# 可以使用re.match方法直接匹配
print(re.match(r'h', 'hello'))

# 也可以调用re模块的compile方法,生成一个 Pattern 对象,再调用 Pattern 对象的 match方法
regex = re.compile(r'h')
print(regex.match('hello'))

print(re.search(r'l', 'hello'))
regex = re.compile(r'l')
print(regex.match('hello'))

regex = re.compile(r'l')
print(regex.findall('hello'))


regex1 = re.compile(r'l')
print(regex.finditer('hello'))

# <re.Match object; span=(0, 1), match='h'>
# <re.Match object; span=(0, 1), match='h'>
# <re.Match object; span=(2, 3), match='l'>
# None
# ['l', 'l']
# <callable_iterator object at 0x000001D201635840>

正则表达式修饰符

修饰符 (常用标志·描述
re.I使匹配对大小写不敏感
re.M多行匹配,影响 ^ 和 $
re.S使 . 匹配包括换行在内的所有字符
re.L做本地化识别(locale-aware)匹配
re.U与 re.L 模式类似,根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。详细模式,可以在正则表达式中加注解,可以识别正则表达式中的注释信息并且自动忽略
import re
print(re.search(r'L','hello'))  # None
# 不区分大小写
print(re.search(r'L', 'hello', re.I))  # <re.Match object; span=(2, 3), match='l'>

y = re.findall(r'x', 'good Xyz', re.I)
print(y)  # ['X']
import re
# .  表示除了换行以外的任意字符
x = re.findall(r'm.*a', 'sdfmo\nej.oasdl', re.S)
print(x)  # ['mo\nej.oa']


a = '''
        asdfhellopass:
        worldaf
    '''
b = re.findall('hello(.*?)world',a)
c = re.findall('hello(.*?)world',a,re.S)
print ('b 值: ' , b)
print ('c 值: ' , c)
# b 值:  []
# c 值:  ['pass:\n        ']
import re
# \w+$ 表示匹配以一个或者多个字母结尾
# re.M 可以进行多行匹配,每个换行都认为是一个结尾
print(re.findall(r'\w+$','i am boy\n you are girl\n he is man',re.M)) # ['boy', 'girl', 'man']
# 不实用re.M修饰符,只会匹配到最后的 man
print(re.findall(r'\w+$','i am boy\n you are girl\n he is man')) # ['man']
print(re.search(r'.','\n')) # None . 匹配除了 \n 以外的所有字符
print(re.search(r'.','\n',re.S)) # '\n'  匹配到了 \n
re.S单行匹配 re.M多行匹配
import re
 
string = '''
hate is a beautiful feel
love you very much
love she
love her
'''
 
pattern = re.compile(r'^love',re.M) # re.M 多行模式
ret = pattern.findall(string)
 
print(ret) # ['love', 'love', 'love']
import re
 
string = '''<div>沁园春-雪
北国风光
千里冰封
万里雪飘
望长城内外
惟余莽莽
大河上下
顿失滔滔
山舞银蛇
原驰蜡象
欲与天公试比高
</div>'''
 
# .  可以匹配除换行符之外的所有字符,当设置成re.S之后,可以简单理解为:【.】可以匹配换行符,所以【.】可以匹配所有字符
pattern = re.compile(r'^<div>(.*?)</div>',re.S) # re.S 单行模式
ret = pattern.findall(string)
 
print(ret) # ['沁园春-雪\n北国风光\n千里冰封\n万里雪飘\n望长城内外\n惟余莽莽\n大河上下\n顿失滔滔\n山舞银蛇\n原驰蜡象\n欲与天公试比高\n']


# \w+不能匹配换行符
pattern1 = re.compile(r'^<div>(\w+)</div>', re.S)  # re.S 单行模式
ret = pattern1.findall(string)

print(ret)  # []
基本语法
  1. 单字符:
    [ ]: [aoe][a-w] 匹配集合中任意一个字符
    \s: 所有的空白字符 ,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。
    \S: 非空白字符

  2. 数量修饰:
    *:任意次数 >=0
    +: 至少1次 >=1
    ?: 可有可无 0次或者1次
    {m}: 固定m次
    {m,}: 至少m次
    {m,n}: m-n次

  3. 边界:
    ^: 以…开头
    $: 以…结尾

  4. 分组:
    (): 视为一个整体
    (ab){4}:视为一个整体,匹配次数

  5. 取消贪婪模式
    .*?
    .+?

  6. 查找
    match: 只从开头开始找
    search: 从任意位置开始找
    findall: 找所有

  7. 非打印字符
    \cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
    \f 匹配一个换页符。等价于 \x0c 和 \cL。
    \n 匹配一个换行符。等价于 \x0a 和 \cJ。
    \r 匹配一个回车符。等价于 \x0d 和 \cM。
    \t 匹配一个制表符。等价于 \x09 和 \cI。
    \v 匹配一个垂直制表符。等价于 \x0b 和 \cK。

  8. 特殊字符 描述
    ( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
    . 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 . 。
    [ 标记一个中括号表达式的开始。要匹配 [,请使用 [。
    \ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。‘\n’ 匹配换行符, \ 匹配 \,而 ( 则匹配 ( 。
    { 标记限定符表达式的开始。要匹配 {,请使用 {。
    | 指明两项之间的一个选择。要匹配 |,请使用 |。
    \d 匹配一个数字字符。等价于 [0-9]。
    [0-9] 匹配任何数字。等价于 \d
    \D 匹配一个非数字字符。等价于 [^0-9]。
    [a-z] 匹配任何小写字母
    [A-Z] 匹配任何大写字母
    [a-zA-Z0-9] 匹配任何字母及数字。等价于\w
    \w 匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_]。
    \W 匹配任何非单词字符。等价于 [^A-Za-z0-9_]。
    [\u4e00-\u9fa5] 匹配纯中文

  9. 限定符

import re

# \s  表示任意的空白字符
print(re.search(r'\s', 'hello world'))  # 空格     <re.Match object; span=(5, 6), match=' '>
print(re.findall(r'\n', 'hello\nworld'))  # 换行   ['\n']
print(re.search(r'\t', 'hello\tworld'))  # 制表符  <re.Match object; span=(5, 6), match='\t'>
print(re.findall(r'\t', 'hello\tworld'))  # 制表符  ['\t']

# \S  表示非空白字符
print(re.search(r'\S', '\t\n   x'))   # <re.Match object; span=(5, 6), match='x'>
print(re.findall(r'\S', '\t\n   x'))  # ['x']

# 标点符号的使用:

# ():用来表示一个分组
m0 = re.search(r'h(\d+)x', 'sh829xkflsa')
print(m0.group(1))  # 829


# 如果要表示括号,需要使用 \
m1 = re.search(r'\(.*\)', '(1+1)*3+5')
print(m1.group())   # (1+1)

# /d:表示数字
# . 表示匹配除了换行以外的任意字符。如果想要匹配 . 需要使用 \.
# [] 用来表示可选项范围  [x-y]从x到y区间,包含x和y
m2 = re.search(r'f[0-5a-dx]m', 'pdsfxm')  # 0<=value<=5 或者 a<=value<=d或者value==x
m2_2 = re.findall(r'f[0-5a-dx]m', 'pdsfxm')
print(m2)  # <re.Match object; span=(3, 6), match='fxm'>
print(m2_2)  # ['fxm']

# | 用来表示或者  和  [] 有一定的相似,但是有区别
# [] 里的值表示的是区间,而且是单个字符
# | 就是可选值,可以出现多个值
print(re.search(r'f(x|prz|t)m', 'pdsfprzm')) # <re.Match object; span=(3, 8), match='fprzm'>
print(re.findall(r'f(x|prz|t)m', 'pdsfprzm'))# ['prz']

# {} 用来限定前面元素出现的次数
# {n}:表示前面的元素出现 n 次
print(re.search(r'go{2}d', 'good')) # <re.Match object; span=(0, 4), match='good'>
print(re.findall(r'go{2}d', 'good')) # ['good']

# {n,}:表示前面的元素出现 n 次以上
print(re.search(r'go{2,}d', 'gooooood')) # <re.Match object; span=(0, 8), match='gooooood'>
print(re.findall(r'go{2,}d', 'gooooood')) # ['gooooood']

# {,n}:表示前面的元素出现 n 次以下
print(re.search(r'go{,2}d', 'gd')) # <re.Match object; span=(0, 2), match='gd'>
print(re.findall(r'go{,2}d', 'gd')) # ['gd']

# {m,n}:表示前面的元素出现m到n次
print(re.search(r'go{3,5}d', 'gooood')) # <re.Match object; span=(0, 6), match='gooood'>
print(re.findall(r'go{3,5}d', 'gooood')) # ['gooood']

# *:表示前面的元素出现任意次数(0次及以上) 等价于  {0,}
x = re.search(r'go*d', 'goooooooooooooooooooooooooooooooooooooooooooooooooooooooooood')
print(x.group()) # goooooooooooooooooooooooooooooooooooooooooooooooooooooooooood


# +:表示前面的元素至少出现一次,等价于 {1,}
print(re.search(r'go+d', 'goood')) # <re.Match object; span=(0, 5), match='goood'>
print(re.findall(r'go+d', 'goood')) # ['goood']


# ?:两种用法:
# 1.规定前面的元素最多只能出现一次,等价于 {,1}
# 2.将贪婪模式转换成为非贪婪模式
print(re.search(r'go?d', 'god'))  # <re.Match object; span=(0, 3), match='god'>
print(re.findall(r'go?d', 'god')) # ['god']


# ^:以指定的内容开头   $:指定内容结尾
print(re.search(r'^a.*i$', 'aofi')) # <re.Match object; span=(0, 4), match='aofi'>
print(re.findall(r'^a.*i$', 'aofi')) # ['aofi']

# 1. 数字和字母都表示它本身
# 2. 很多字母前面添加 \ 会有特殊含义(重点)
# 3. 绝大多数标点符号都有特殊含义(重点)
# 4. 如果想要使用标点符号,需要在 \
import re

# 字母x表示它本身
re.search(r'x', 'hello xyz')
re.search(r'5', '23r49534')

# 字母d是普通的字符
print(re.search(r'd', 'good')) # <re.Match object; span=(3, 4), match='d'>

# \d 有特殊含义,不再表示字母 d
print(re.search(r'\d', 'good'))   # None
print(re.search(r'\d', 'wsdfk4sdfjl')) # <re.Match object; span=(5, 6), match='4'>

# re.search('+', '1+2')   # 不能直接使用,+ 有特殊含义 悬空限定符 ‘+’ 只能用于相同类型的对象之间的连接操作。如果尝试将字符串与数字或其他类型的对象连接,将会引发类型错误。
print(re.search(r'\+', '1+2')) # <re.Match object; span=(1, 2), match='+'>
import re

# 字母表示它本身,很多字母前面  \ 会有特殊含义

# \n:表示换行   \t:表示一个制表符  \s:空白字符  \S:非空白字符
# \d:表示数字,等价于 [0-9]
print(re.search(r'x\d+p', 'x243p')) # <re.Match object; span=(0, 5), match='x243p'>
print(re.search(r'x[0-9]+p', 'x243p'))  # <re.Match object; span=(0, 5), match='x243p'>

# ^ 除了表示以指定的内容开始以外,在 [] 里还可以表示取反
# \D:表示非数字,等价于 [^0-9]
print(re.search(r'\D+', 'he110'))  # <re.Match object; span=(0, 2), match='he'>
print(re.search(r'[^0-9]+', 'he110'))  # <re.Match object; span=(0, 2), match='he'>

# \w:表示数字、字母、 _ 以及中文等   非标点符号
print(re.findall(r'\w+', 'h+E-11.0_X*'))  # ['h', 'E', '11', '0_X']
print(re.findall(r'\w+', '大,家+好!'))  # ['大', '家', '好']

# \W: \w 取反
print(re.findall(r'\W+', 'h+E-11.0_X*'))  # ['+', '-', '.', '*']
常用正则表达式

参考:验证码~正则表达式【整理汇总】
使用时r'正则表达式'

正则替换

Python中的re模块提供了re.sub用户替换字符串中的匹配项。

语法:

re.sub(pattern,repl,string,count=0)

参数:

  pattern : 正则中的模式字符串。
  repl : 替换的字符串,也可为一个函数。
  string : 要被查找替换的原始字符串。
  count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

import re
phone = "2004-959-559 # 这是一个电话号码"

# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)  # 电话号码 :  2004-959-559 


# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num) # 电话号码 :  2004959559

repl可以使用一个字符串用来表示替换后的结果以外,还可以传入一个函数。

import re
def double(matched):
    test = int(matched.group('test'))
    print(test)
    #23
    #34
    return str(test * 2)


print(re.sub(r'(?P<test>\d+)', double, 'hello23hi34'))  # hello46hi68
贪婪和非贪婪模式

Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;

非贪婪则相反,总是尝试匹配尽可能少的字符。

在*,?,+,{m,n}后面加上 ?使贪婪变成非贪婪。

import re

# 在Python的正则表达式里,默认是贪婪模式,尽可能多的匹配
# 在贪婪模式后面添加  ?  可以将贪婪模式转换成为非贪婪模式
m = re.search(r'm.*a', 'o3rjomjadas')
print(m.group())  # mjada

# 尽可能少的匹配
n = re.search(r'm.*?a', 'o3rjomjadas')
print(n.group())  # mja

x1 = re.match(r"aa(\d+)", "aa2343ddd")
print(x1.group(0))  # aa2343
print(x1.group(1))  # 2343

x2 = re.match(r"aa(\d{2,}?)", "aa2343ddd")
print(x2.group(0))  # aa23
print(x2.group(1))  # 23

x3 = re.match(r"aa(\d+)ddd", "aa2343ddd")
print(x3.group(0))  # aa2343ddd
print(x3.group(1))  # 2343

x4 = re.match(r"aa(\d+?)ddd", "aa2343ddd")
print(x4.group(0))  # aa2343ddd
print(x4.group(1))  # 2343

x5 = re.match(r"aa(\d??)(.*)", "aa2343ddd")
print(x5.group(0))  # aa2343ddd
print(x5.group(1))  # 空
print(x5.group(2))  # 2343ddd

src = '<img data-original="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" src="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" style="display: inline;">'
x6 = re.search(r'https://.*?\.jpg', src)
print(x6.group())


s="This is a number 234-235-22-423"
r=re.match(r".+(\d+-\d+-\d+-\d+)",s)
print(r.group(1))  # 4-235-22-423

r=re.match(r".+?(\d+-\d+-\d+-\d+)",s)
print(r.group(1)) # 234-235-22-423

练习
import re

# 用户名匹配
# 1. 用户名只能包含数字、字母和下划线
# 2. 不能以数字开头   3. 长度在6到16位范围
username = input('请输入用户名:')
# x = re.match(r'^[a-zA-Z_][a-zA-Z0-9_]{5,15}$', username)
x = re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]{5,15}', username)
if x is None:
    print('输入的用户名不符合规范')
else:
    print(x)



# 密码匹配
# 1. 不能包含 !@%^&*字符  2.必须以字母开头  3.长度在6到12位
password = input('请输入密码:')
y = re.fullmatch(r'[a-zA-Z][^!@#$%^&*]{5,11}', password)
if y is None:
    print('密码不符合规范')
else:
    print(y)





z = []
try:
    with open('demo.txt', 'r', encoding='utf8') as file:
        content = file.read()  # 读出所有的内容
        z = re.findall(r'1000phone.*', content)
        print(re.findall(r'^1000phone.*', content, re.M))

        while True:
            content = file.readline().strip('\n')
            if not content:
                break
            if re.match(r'^1000phone', content):
                z.append(content)
except FileNotFoundError:
    print('文件打开失败')

print(z)

# ip地址检测 0.0.0.0 ~ 255.255.255.255
num = input('请输入ip地址:')
# #    0~9      10~99            100 ~ 199  200~209 210~219 220~229 230~239 240~249  250~255
# # \d:一位数   [1-9]\d:两位数   1\d{2}:1xx  2:00~255
x = re.fullmatch(r'((\d|[1-9]\d|1\d{2}|2([0-4]\d|5[0-5]))\.){3}(\d|[1-9]\d|1\d{2}|2([0-4]\d|5[0-5]))', num)
print(x)





# 非捕获分组
x = re.findall(r'(?:-)?\d+(?:\.\d+)?', '-90good87ni0ce19bye')
print(x)  # ['-90', '87', '0', '19']

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半生过往

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值