互联网的伟大之处
在于每个人都在分享自己的知识

阿里云 Python教程

阶段一

第一部分 1-3 章 环境搭建语法入门

第一阶段总结
# 唐僧大战白骨精:

# 显示欢迎信息
print('-' * 20, '欢迎光临<唐僧大战白骨精>', '-' * 20)
# 显示选择你的身份
print('选择你的身份:')
print('\t1.唐僧')
print('\t2.白骨精')
player_choose = input("请选择[1-2]:")

# 打印分割线:
print('-' * 64)

if player_choose == '1':
    # 选择1
    print('你已经选择了1,你将以->唐僧<-的身份来进行游戏')
elif player_choose == '2':
    # 选择2
    print('你竟然选择了白骨精,系统将自动分配身份:你将以->唐僧<-的身份来进行游戏')
else:
    # 选择3
    print('你输入了错误的参数,系统将自动分配身份:你将以->唐僧<-的身份来进行游戏')

# 进入游戏
# 创建变量 来保存玩家的生命值和攻击里
player_life = 5.0  # 生命值
player_attack = 3.0  # 攻击力
# 创建 boss的生命值和攻击力
boss_life = 100.0
boss_attack = 5.0
# 打印分割线:
print('-' * 64)
print(f"唐僧,你的生命值是:{player_life},你的攻击力是:{player_attack}")
# 打印分割线:
print('-' * 64)

# 定义boss或玩家死亡的die
# 由于游戏的选项是需要反复显示的,所以必须将其编写到一个循环里
die = True
while die:
    print('您可以进行以下操作:')
    print('\t1.练级')
    print('\t2.打boss')
    print('\t3.逃跑')
    game_choose = input('请选择要做的操作[1-3]:\n')
    # 打印分割线:
    print('-' * 64)
    if game_choose == '1':
        # 选择1
        print('你已经选择了1,你将进行练级,练级会使你增长生命值和攻击力')
        player_life += 5.0
        player_attack += 3.0
        print(f'恭喜你升级了, 你现在的生命值是:{player_life}, 你现在的攻击力是: {player_attack}')
        # 打印分割线:
        print('-' * 64)
        continue
    elif game_choose == '2':

        # 选择2 打boss
        # 玩家攻击boss
        # boss反击玩家
        # 减去的生命值,应该等于玩家的攻击力

        print('你已经选择了2,你将开始大战白骨精')
        while die:
            print('唐僧攻击了白骨精')
            boss_life -= (player_attack * 0.4)
            real = player_attack * 0.4
            # 控制
            print('因为白骨精是boss,减免40%的伤害,唐僧打掉了白骨精', format(real, '.1f'), '点血')
            # 打印分割线:
            print('-' * 64)
            if boss_life <= 0:
                print(f'白骨精受到了{player_attack}点伤害,重伤不治狗带了')
                print('恭喜您击杀了boss!!!游戏结束了')
                # 直接击杀,退出游戏
                die = False
                break
            print('白骨精攻击了唐僧')
            player_life -= boss_attack
            print(f'白骨精打掉了唐僧{boss_attack}点血')
            # 打印分割线:
            print('-' * 64)
            if player_life <= 0:
                print('您被打败了,游戏结束了')
                die = False
                break

    elif game_choose == '3':
        # 选择3
        print('唐僧一听说要打白骨精,吓得扭头就走,游戏结束')
        break
    else:
        print('您输入了错误的数字,请重新输入')
input("按任意键退出")

第二部分 4章 列表,元组,字典,集合

第四章 序列

# 第四章 序列
## 列表(list)
	- 列表是python中的一个对象
	- 对象就是内存中存储数据的一块数据
	- 之前我们学习的对象,像数值,他只能保存一个单一的数据
	- 列表中可以保存多个有序的数据
	- 列表时存储对象的对象
	- 列表的使用:
		1.列表的创建
		2.操作列表中的数据

## 序列(sequence)
	- 序列时python中最基本的数据结构
	- 数据结构指计算机中数据存储的方式
	- 序列用于保存一组有序的数据,所有的数据在序列但终都有一个唯一的位置(索引)
		并且序列中的数据会按照添加的顺序来分配索引
	- 序列的分类:
		可变序列(序列中的元素可以改变):
			> 列表 (list)
		不可变序列(序列中的元素不可以改变):
			> 字符串 (str)
			> 元组 (tuple)
		- 刚刚将的所有操作都时序列的通用操作 也就是切片 in,not in 等

## EMS( Employee Manager System 员工管理系统) 练习
	- 做命令行版本的命令行管理系统
	- 功能:
		四个:
			1. 查询
				- 显示当前系统的所有员工
			2. 添加
				- 将员工添加到当前系统中
			3. 删除
				- 将员工从系统中删除
			4. 退出
				- 退出系统
	- 员工信息要保存到哪里? 列表,在系统中应该有一个列表,专门用来保存所有员工信息的

## 可变对象
	- 每个对象中都保存了三个数据:
		id(标识)
		type(类型)
		value(值) 可变
	列表就是换一个可变对象
		a=[1,2,3]

	- a[0]=10 (改对象)
		- 这个操作是在同故宫变量去修改对象的值
		- 这种操作不会改变变量所指向的独享
		- 当我们去修改对象时,如果有其他对象也指向了该对象,则修改也会在其他变量中体现
		- 会影响所有指向该对象的变量

	a=[4,5,6] (改变量)
		- 这个操作是在给变量重新赋值
		- 这种操作会改变变量所指向的对象
		- 为一个变量重新赋值时,不会影响其他变量
	- 一般只有在为变量赋值时,才是赋值变量,其余都是修改对象

## 字典(dict)
	- 字典属于一种新的数据结构,成为映射,(mapping)
	- 字典的作用和列表类似,都是用来存储对象的容器
	- 列表存储数据的性能很好,查询性能的性能很差
	- 在字典中每一个元素都有一个唯一的名字,通过这个唯一的名字可以快速查找到指定的元素
	- 在查询元素时,字典的小路是非常快的
	- 在字典中可以保存多个对象,每个对象都会有一个唯一的名字
		这个唯一的名字我们叫做键(key)
		这个对象我们称之为值(value)
		所以字典,我们亦可以称为键值对(key-value)结构
		所以每个字典中都可以有多个键值对,而每一个键值对我们称其为一项(item)
		
## 集合(set)
	- 集合和列表非常相似
	- 不同点:
		1.集合中只能存储不可变对象
		2.集合中存储的对象是无序的(不是按照元素的插入顺序保存的)
		3.集合中不能出现重复的元素

01.列表的简介

01.列表的简介.py
# 创建列表,通过[]创建列表
my_list = []  # 创建了一个空列表
# print(my_list, type(my_list)) #类型是list

# 列表中存储的数据,我们称为元素
# 一个列表中可以存储多个元素,也可以在创建列表时,来指定列表中的元素
my_list = [10]  # 创建一个只有一个包含一个元素的列表

# 当向列表中添加元素时,多个元素之间使用,隔开
my_list = [10, 20, 30, 40, 50]  # 创建一个包含五个元素的列表

# 列表中可以保存任意对象
my_list = [10, 'hello', True, None, [1, 2, 3], print]

# 列表中的对象都会按照插入的顺序存储到列表中,
#   第一个插入的对象保存到第一个位置,第二个插入的保存到第二个位置
# 我们可以通过索引(index)来获取列表中的元素
#   索引是元素在列表中的位置,列表中的每一个元素都有一个索引
#   索引时从0开始的证书,第一个位置为0,第二个1,以此类推
my_list = [10, 20, 30, 40, 50]


# 通过索引获取列表总的元素
# 语法:my_list[索引] my_list[]
# print(my_list[4])
# 如果使用的索引超过最大范围会抛出异常
# print(my_list[5]) IndexError: list index out of range

# 获取列表的长度,列表中元素的个数
# len()函数,通过该函数可以获取列表的长度
# 获取到的长度,时列表的最大索引+1
print(len(my_list)) # 5

02.切片


02.切片.py
# 切片
# 切片指从现有列表中,获取一个子列表
# 创建一个列表,一般创建列表时,变量的名字会使用复数
stus = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精']

# 列表的索引可以时负数
# 如果索引时负数,则从后向前获取元素,-1代表倒数第一个,-2代表第二个
print(stus[-1])

# 通过切片获取指定的元素
# 列表 : 列表[起始:结束]
#  通过切片获取元素时,会包括起始位置的元素,不会包括结束位置的元素
#  做切片操作时,总会返回一个新的列表
#  起始和结束位置的索引都可以省略不写
#  如果省略结束位置,则会一直截取到最后
#  如果省略开始位置,则会从第一个元素开始截取
# 也可以传负值,也就是从后往前数
# 开始位置和结束位置则相当于创建了一个列表的副本
print(stus[-3:])
print(stus[1:3])
# 这俩不是一个 先当与复制了一个
print(stus[:])
print(stus)

# 语法: 列表[起始:结束:步长]
# 步长表示,每次获取元素的间隔,默认是1
# 步长可以是负数,但是不能是0
print(stus[0:5:2])
# 如果是负数,则从列表后部向前面取元素
print(stus[::-1])

03.通用操作

03.通用操作.py
# + 和 *

# + 可以把两个列表拼接为一个列表
my_list = [1, 2, 3] + [4, 5, 6]

# * 可以把列表重复指定的次数
my_list = [1, 2, 3] * 2


# print(my_list)
stus = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精', '沙和尚']

# in 和 not in
# in用来检查指定元素是否存在于列表中
#  如果存在返回 true,否则返回false
# not in 来检查指定元素是否不在列表中
#  如果不在返回 false,否则返回true
print("沙和尚" in stus)
print("牛魔王" in stus)

# len() 获取列表中元素的个数
# min() 获取列表中的最小值
# max() 获取列表中的最大值
arr = [10, 1, 2, 5, 100, 77]
print(min(arr), max(arr))


# 两个方法(method) , 方法和函数基本是一样的,方法必须通过对象.方法()的形式调用
# xxx.print() 方法实际上就是和对象关系紧密的函数
# s.index() 获取指定元素在列表中第一次出现时的位置
print(stus.index('沙和尚'))
# index的第二个参数传从第几个位置开始查找,第三个参数表时查找的结束位置
print(stus.index('沙和尚', 3, 7))
# 如果没有 则会报错ValueError: '牛魔王' is not in list
# print(stus.index('牛魔王'))

# s.count()
# 查看指定的元素在列表出现的次数
print(stus.count('沙和尚'))

04.修改元素


04.修改元素.py
stus = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精']

# 修改列表中的元素
# 直接通过索引来修改元素
print('修改前', stus)
stus[0] = 'sunwukong'
print('修改后', stus)

# 通过del删除元素
del stus[0]  # 删除索引为0的元素


stus = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精']
print('修改前', stus)
# 通过切片来修改列表
# 再给切片赋值时,只能使用序列
# stus[0:2]=123 # 错
# stus[0:2] = ['红孩儿', '牛魔王','二郎神']
stus[0:0] = ['牛魔王']  # 向索引为零的位置插入元素
# 当我们设置了步长时,序列中的个数必须和切片中元素的个数一致
stus[::2] = ['牛魔王', '红孩儿', '二郎神', '黑熊精']
print('修改前', stus)
# 通过切片来删除元素
del stus[0:2]
print('修改后', stus)
del stus[::2]
print('修改后', stus)


stus = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精']
stus[0:3] = []
print('修改后', stus)

# 以上操作仅适用于可变序列
s = 'hello'
print(s[::2])
# s[1]='a' # 错 # 不可变序列,无法通过索引来修改
# 可以通过list()函数将其他序列转换为list
s = list(s)
print(s)

05.列表的方法

05.列表的方法.py
# 列表的方法
stus = ['孙悟空', '猪八戒', '沙和尚']
print('源列表', stus)


# append() 向列表最后添加一个元素
stus.append('唐僧')
print('修改后', stus)

# insert() 向列表指定的位置传入一个元素
# 参数:
#  1.要插入的位置
#  2.要插入的元素
stus.insert(2, '唐僧')
print('修改后', stus)

# extend()
# 使用新的序列来扩展当前序列
# 需要一个序列作为参数,它会将该序列中的元素添加到当前序列
stus.extend(['tangseng', '白骨精'])
# 等同于 stus+=['tangseng', '白骨精']
print('修改后', stus)

# clear()
# 情况序列
stus.clear()
print('修改后', stus)


stus = ['孙悟空', '猪八戒', '沙和尚']

# pop()
# 根据索引删除并返回指定元素,并返回删除的元素

result = stus.pop(2)  # 删除索引为2的元素,并返回删除的元素 不写参数就删最后一个
print('修改后', stus, '\n', result)


# remove()
# 删除指定值的元素,如果相同值只会删除第一个
stus.remove('猪八戒')
print('修改后', stus)


stus = ['孙悟空', '猪八戒', '沙和尚']
# reverse()
# 反转列表
stus.reverse()
print('修改后', stus)


# sort()
# 用来对列表中的元素进行排序
# 默认升序排序
my_list = list('asdfasdfasdf')
print('修改前', my_list)
my_list.sort()
print('修改后', my_list)
# 降序排列
my_list.sort(reverse=True)
print('修改后', my_list)

06.遍历列表


06.遍历列表.py
# 遍历列表,指的就是将列表中的所有元素取出来
# 创建列表
stus = ['孙悟空', '猪八戒', '沙和尚', '唐僧', '白骨精', '蜘蛛精']

# 遍历列表

# 渣渣
print(stus[0])
print(stus[1])
print(stus[2])

print('-' * 20 + '分割线' + '-' * 20)
# 通过while遍历循环
# 也不用
i = 0
while i < len(stus):
    print(stus[i])
    i += 1

print('-' * 20 + '分割线' + '-' * 20)
# 通过for循环来遍历列表
# 常用
# 语法:
#   for 变量 in 序列:
#	代码块
# for循环的代码块会执行多次,序列中有几个元素就会执行几次
#  每执行一次就会将序列中的一个元素赋值给变量
for s in stus:
    print(s)

07.EMS 练习

07.EMS练习.py
(Employee Manager System 员工管理系统)  
# 显示系统欢迎信息
print('=' * 20 + '欢迎使用员工管理系统' + '=' * 20)

# 创建一个列表,来保存员工的信息,员工的信息以字符串的形式同一保存到列表中
emps = ['孙悟空\t18\t男\t花果山', '猪八戒\t28\t男\t高老庄']
# 创建一个死循环
while True:
    # 显示用户选项
    print('请选择要做的操作:')
    print('\t 1.显示员工')
    print('\t 2.添加员工')
    print('\t 3.删除员工')
    print('\t 4.退出系统')
    user_choose = input('请选择要做的操作[1-4]:')
    print('-' * 58)
    # 根据用户的选择做相关的操作
    if user_choose == '1':
        # 查询员工
        # 打印表头
        print('\t序号\t姓名\t年龄\t性别\t住址')
        # 创建一个变量,来表示员工的序号
        n = 1
        # 显示员工信息
        for emp in emps:
            print(f'\t{n}\t{emp}')
            n += 1

    elif user_choose == '2':
        # 添加员工
        # 获取要添加员工的信息,姓名,年龄,性别,住址
        emp_name = input('请输入员工姓名:')
        emp_age = input('请输入员工年龄:')
        emp_gender = input('请输入员工性别:')
        emp_address = input('请输入员工住址:')

        # 创建一个员工信息
        emp = f'{emp_name}\t{emp_age}\t{emp_gender}\t{emp_address}'
        # 显示一个提示信息
        # input('员工:' + emp + '将会被添加到系统中,是否确认该操作[Y/N]:')
        print('以下员工将会被添加到系统中')
        print('-' * 58)
        print('姓名\t年龄\t性别\t住址')
        print(emp)
        print('-' * 58)
        user_confirm = input('是否确认该操作[Y/N]:')

        # 判断
        if user_confirm == 'y' or user_confirm == 'Y' or user_confirm == 'yes':
            # 确认
            emps.append(emp)
            # 显示提示信息
            # 插入成功

        else:
            # 取消操作
            print('添加以取消')
            pass

    elif user_choose == '3':
        # 删除员工 根据员工的序号来删除员工
        # 要获取要删除员工的序号
        del_num = int(input('请输入要删除员工的序号:'))

        # 判断序号是否有效
        if 0 < del_num <= len(emps):
            # 输入合法,根据序号来获取索引
            del_i = del_num - 1
            # 显示提示
            print('以下员工将会被删除')
            print('-' * 58)
            print('\t序号\t姓名\t年龄\t性别\t住址')
            print(f'\t{del_num}\t{emps[del_i]}')
            print('-' * 58)
            user_confirm = input('该操作不可恢复,是否确认该操作[Y/N]:')
            # 判断
            if user_confirm == 'y' or user_confirm == 'Y':
                # 删除元素
                emps.pop(del_i)
                # 显示提示
                print('员工已被删除')
            else:
                # 操作取消
                print
        else:
            # 输入有误
            print('输入有误,请重新输入')

    elif user_choose == '4':
        # 退出系统
        print('-' * 58)
        print('欢迎使用!再见!')
        input('点击回车键退出')
        break
    else:
        print('您的输入有误,请重新选择!')
    # 打印分割线
    print('-' * 58)

08.range

# range() 是一个函数,可以用来生成一个自然数的序列
r = range(10)  # 生成了这样的序列[0,1,2,3,4]
print(r)
print(list(r))
# 该函数需要三个参数
#	1.起始位置(可以省略,默认是0)
#	2.结束位置
#	3.步长(可以省略,默认是1)

# 通过range()可以创建一个执行指定次数的for循环
# for()循环除了创建方式意外,
# 其余都和while一样,包括else,break,continue都可以在for循环中使用
# 并且for循环使用也更加简单
for i in range(20):
    print(i)


for s in 'hello':
    print(s)

09.元组

09.元组.py
# 元组就是不可变序列
# tuple
# 他的操作方式基本上和列表是一样的
# 索引在操作元组时,就把元组当成一个不可变的列表就ok了
# 一般当我们希望数据不改变时,就是用元组,其余情况都使用列表

# 创建元组
# 使用()来创建元组
my_tuple = ()  # 创建了一个空元组
print(my_tuple, type(my_tuple))


my_tuple = (1, 2, 3, 4, 5)  # 创建了一个五个元素的元组
# 元组是不可变对象,不能为尝试为元组中的元素重新赋值
# my_tuple[3] = 10 # TypeError: 'tuple' object does not support item assignment

# 当我们的元组不是空元组时,括号可以省略
# 如果元组不是空元组,它里面至少要有一个',' 不管加不加()
my_tuple = 10, 20, 30, 40
print(my_tuple, type(my_tuple))

# 元组的解包
# 解包就是将元组的每一个元素赋值给一个变量
my_tuple = 10, 20, 30, 40
print(my_tuple)
a, b, c, d = my_tuple
print(' a=', a, '\n', 'b=', b, '\n', 'c=', c, '\n', 'd=', d, '\n')
# a= 10
# b= 20
# c= 30
# d= 40

a = 100
b = 300
print(a, b)

# 交换 a和b的值,这是就可以利用元组的解包
a, b = b, a

print(a, b)


# 在对一个元组进行解包时,变量的数量必须和元组中的元素的数量是一致的
# 也可以在变量前边添加一个'*',这样变量将会获取元组中的所有剩余的元素生成列表
# 最后
my_tuple = 10, 20, 30, 40
a, b, *c = my_tuple
print(a, b, c)

# 中间
a, *b, c = my_tuple
print(a, b, c)

# 开头
*a, b, c = my_tuple
print(a, b, c)

# 不能同时出现两个及两个以上'*'
# *a, *b, c = my_tuple #SyntaxError: two starred expressions in assignment
# print(a, b, c)

a, b, *c = "hello,word"
print(a, b, c)

10.可变对象

10.可变对象.py
# 可变对象
a = [1, 2, 3]
print('修改前:', a, id(a), id(a[0]))

# 通过索引修改列表
a[0] = 9
print('修改后:', a, id(a), id(a[0]))

# 为变量重新赋值
a = [4, 5, 6]
print('修改后:', a, id(a), id(a[0]))

a = [1, 2, 3]
b = a
b[0] = 10
print('a', a, id(a))
print('b', b, id(b))

a = [1, 2, 3]
b = a
b = 10, 20, 30
print('a', a, id(a))
print('b', b, id(b))

# == != is is not
# == != 比较的时对象的值是否相等
# is is not 比较的是对象的id是否相等(比较两个对象是否是一个对象)
a = [1, 2, 3]
b = [1, 2, 3]
print(a, b)
print(id(a), id(b))
print(a == b)  # a和b的值相等,使用== 会返回true
print(a is b)  # a和b不是一个对象,内存地址不同,使用is会返回false

11.字典

# 字典
# 使用{}来创建字典
d = {}  # c创建了一个空字典

# 创建一个保存有数据的字典
# 语法:
# {key:value,key:value,key:value}
# 字典的值可以是任意对象
# 字典的键可以是任意的不可变对象(int,str,bool,tuplu ...)
d = {'name': '孙悟空', 'age': '18', 'gender': '男'}
print(d, type(d))
# 	字典的键是不能重复的,如果出现重复,后面会替换前面的
d = {
    'name': '孙悟空',
    'age': '18',
    'gender': '男',
    'name': 'sunwukong'
}
print(d, type(d))

# 需要根须键来获取值
print(d['name'], d['age'], d['gender'])

# 如果使用字典中不存在的键,会报错
print(d['hello'])

12.字典的使用

# 字典
# 使用{}来创建字典
# 语法:{key:value,key:value,key:value}

# 使用dict()函数来创建字典
# 每一个参数都是一个键值对,参数名就是键,参数名就是值(这种方式创建的字典,key都是字符串)
# 这种函数会给键自动加''
d = dict(name='孙悟空', age=18, gender='男')
print(d, type(d))

# 也可以将一个包含有双值子序列的序列转换为字典
# 双值序列,序列中只有两个值 : [1,2](a,3) 'ab'
# 子序列,如果序列中的元素也是序列那么我们就称这个元素为子序列
# (1,2),(3,5)]
# 特殊情况会用
d = dict([('name', '孙悟饭'), ('age', 18)])

print(d, type(d))


# len() 获取字典中键值对的个数
d = dict(name='孙悟空', age=18, gender='男')
print(len(d))

# in 检查字典中是否包含指定的键
# not in 检查字典中是否不包含指定的键
print('name' in d)
print('hello' in d)


# 获取字典中的值,根据键来获取值
# 语法: d[key]
print(d['name'])
# print(d[name])  # 不加引号当变量使用报错 NameError: name 'name' is not defined

# 使用变量时不要加''
n = 'name'
print(d[n])


# 通过[]来获取值时,如果键不存在,会抛出异常 KeyError
# get(key[,default])
# 没有的话会返回None
print(d.get('name'))
# 也可以指定一个默认值,来作为第二个参数,这样获取不到值时会返回默认值
print(d.get('hello', '默认值'))


# 修改字典
# d[key]=value 如果key存在则覆盖,不存在则添加
d['name'] = 'sunwukong'
# 添加字典 默认在最后
d['address'] = '花果山'
print(d)


# setdefault(key[,default]) 可以向字典添加key-value
# 如果字典存在键 key ,返回它的值。
# 如果不存在,插入值为 default 的键 key ,并返回 default 。 default 默认为 None。
result = d.setdefault('name', '猪八戒')
print('result:', result)
result = d.setdefault('hello', '猪八戒')
print('result:', result)
print(d)


# update([other])
# 将其他字典的key-value添加到当前字典中
# 如果有重复,后面会替换当前的
d = {'a': 1, 'b': 2, 'c': 3}
d2 = {'d': 4, 'e': 5, 'f': 6, 'a': 7}
d.update(d2)
print(d)

# 删除字典
# del来删除字典中的key-value
# 删除不存在的key-value也会报错
print(d)
del d['a']
print(d)
del d['b']
print(d)

# popitem()
# 随机删除字典中的一个键值对,一般都会删除最后一个键值对
# 	删除之后,他会将删除的键值对作为返回值放回
# 	返回的是一个元组,元组有两个元素,一个是字典删除的key一个是删除的value
# 删除一个空字典也会报错
print(d)
result = d.popitem()
print(d, result)


# pop(key[, default])
# 根据key删除字典中的key-value
# 会将被删除的value返回
result = d.pop('d')
# 删除不存在的key也会抛出异常
# 如果指定默认值,在删除不存在的key则不会报错,会直接返回默认值
result = d.pop('z', '这是默认值')
print(d, result)

# clear()
# 删除所有的项
d = {'a': 1, 'b': 2, 'c': 3}
print(d)
d.clear()
print(d)


# copy()
# 该方法用于对字典[浅复制]
# 复制以后的对象,和源对象是独立,修改一个不会影响其他的
# 此方法也用于序列
d = {'a': 1, 'b': 2, 'c': 3}
# 直接 d=d2 指向的都是一个对象
# id一致
d2 = d

# id不一致
d2 = d.copy()
print(id(d), id(d2))

# 注意,浅复制指挥简单的复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制
# 深复制性能较差,常用浅赋值
d = {'a': {'name': '孙悟空', 'age': 18}, 'b': 2, 'c': 3}
print(d)
d2 = d.copy()
d2['a']['name'] = '猪八戒'
print(d)
print(d2)

13.遍历字典

# 遍历字典
# keys() 该方法会返回所有的key
# 该方法会返回一个序列,序列中保存有字典的所有的键 key
d = {'name': '孙悟空', 'age': 18, 'gender': '男'}
print(d.keys())
# 通过遍历keys()来获取所有的键
for k in d.keys():
    print(d[k])

#  分割线
print('-' * 90)


# values()
# 该方法会返回一个序列,序列中保存有字典中所有的value 值
for v in d.values():
    print(v)


#  分割线
print('-' * 90)

# items()
# 该方法会返回字典中所有的项
# 他会返回一个序列,序列中包含有双值子序列
# 双值分别是字典中的key和value
print(d.items())
for k, v in d.items():
    print(k, '=', v)

14.集合

# 集合
# 使用{}来黄健集合
s = {10, 3, 5, 1, 2, 1}  # 只有一个1
# s = {[1, 2, 3], [4, 5, 6]} TypeError: unhashable type: 'list'
print(s, type(s))


# 使用set()来使用集合
# 不能直接s={}这样是字典
s = set()  # 这是空集合
# 可以通过set()来将序列和字典转换为集合
s = set([1, 2, 3, 4, 5, 1, 2, 1, 3, 4, 5])
# {1, 2, 3, 5, 10} <class 'set'>
s = set('hello')
# {'e', 'l', 'h', 'o'} <class 'set'>
s = set({'a': 1, 'b': 2, 'c': 3})  # 使用set()将字典转换为集合是,指挥包含字典中的键
# {'c', 'b', 'a'} <class 'set'>
print(s, type(s))


# 创建集合
s = {'a', 'b', 1, 2, 3}
# print(s[0]) TypeError: 'set' object does not support indexing
# 不能使用索引若想使用需要转换为list
print(list(s)[0])  # b

# 也可以使用in和not in来检查
print('a' in s)

# 使用len()获取集合中元素的数量
print(len(s))

# add()向集合中添加元素
s.add(10)
s.add(30)
print(s)

# update()将集合中的元素添加到当前集合中
s2 = set('hello')
s.update(s2)
s.update((100, 200, 300))
# update()可以传递序列活字典作为参数,字典纸绘使用键 key
s.update({1000: 'ab', 10000: 'cd'})
print(s)


# pop()随机删除集合中的元素
result = s.pop()
print(s, result)

# remove()删除集合中的指定元素
s.remove(100)
print(s)

# clear()清空集合
s.clear()
print(s)

# copy()对集合进行浅复制
s = set('hello')
s1 = s.copy()
print(s1, id(s1), s, id(s))

15.集合的运算

# 创建两个集合
# 对集合做运算时,不会影响原来的集合,而是返回一个运算的结果
s = {1, 2, 3, 4, 5}
s2 = {3, 4, 5, 6, 7}

# & 交集运算
print(s, s2)
result = s & s2
print(result)  # {3, 4, 5}

# | 并集运算
result = s | s2
print(result)  # {1, 2, 3, 4, 5, 6, 7}

# - 差集运算
result = s - s2
print(result)  # {1, 2}  只在a集合有的b集合没有的

# ^ 异或集运算
result = s ^ s2
print(result)  # {1, 2, 6, 7} 两个集合中不相交的值

# <= 检查一个集合是否是另一个集合的子集
# 如果a集合中的元素全部都在b集合中出现,那么a集合就是b集合的子集,b集合就是a集合的超集
a = {1, 2, 3}
b = {1, 2, 3, 4, 5}
result = a <= b  # true
result = {1, 2, 3} <= {1, 2, 3}  # true
result = {1, 2, 3, 4} <= {1, 2, 3}  # false
print(result)
# 如果两个集合一样也是子集

# < 检查一个集合是否是另一个集合的真子集
# 如果超集b中含有子集a中的所有元素,并且b中还有a中没有的元素,则b就是a的真超集,a是b的真子集
result = {1, 2, 3} < {1, 2, 3}  # false  # 子集但不是真子集
result = {1, 2, 3} < {1, 2, 3,4,5} # true  # 真子集
print(result)

# >= 检查a集合是否是b集合的超集
# > 检查a集合是否是b集合的真超集

第三部分 5- 章 函数,面向对象,异常处理

第五章 函数

# 第五章 函数

## 函数简介(function)
	- 函数也是一个对象
	- 对象是内存中专门用来存储数据的一块区域
	- 函数可以用来保存一些可执行的代码,且可以在需要时,对这些语句进行多次的调用
	- 创建函数:
		def 函数名([形参1],[形参2],.....,[形参n]):
			代码块
		- 函数名必须要符合标识符的规范
			(可以包含数字字母下划线,数字不能开头)
	- 函数中保存的代码不会立即执行,调用后才会执行
	- 调用函数
		函数对象()
	- 实现函数都是要实现某种功能

## 函数的参数
	- 在定义函数时,可以在函数名后()中定义数量不等的形参
		多个形参中用','隔开
	- 形参(形式参数),定义形参就相当于在函数定义了变量但没赋值
	- 实参(实际参数)
		- 如果函数定义了形参,那么在调用时也必须传递实参,
			实参将会赋值给对应的形参,有几个形参,就得传几个实参

## 函数式编程
	- 在python中,函数是一等对象
	- 一等对象一般会具有以下条件:
		① 对象是在运行时创建的
		② 能赋值给变量或者作为数据结构中的元素
		③ 能作为参数传递
		④ 能作为返回值返回
	- 高阶函数
		- 高阶函数至少要符合以下两个特点中的一个
			① 接收一个或多个函数作为参数
			② 将函数作为返回值返回
	- 装饰器

01.函数的简介

# 比如有如下三行代码,这三行代码是一个完整的功能
# print('hello')
# print('你好')
# print('再见')


# 定义一个函数
def fn():
    print('这是我的第一个函数!')
    print('hello')
    print('今天天气真不错')
    print('再见')


print(fn, type(fn))
# <function fn at 0x0000026293BA2E18> <class 'function'>

# fn时函数对象,fn()时调用函数
# print时函数对象,print()调用对象
fn()

# 定义一个函数,可以用来求任意两个数的和

# 定义函数时定义形参


def sum(a, b):
    print(a, '+', b, '=', a + b)


# 调用函数时,来传递实参
sum(123, 456)

02.函数的参数

# 求任意三个数的乘积
def mul(a, b, c):
    print(a * b * c)

# 根须不同的用户名显示不同的欢迎信息


def welome(username):
    print('欢迎', username, '光临')


mul(1, 2, 3)
welome('孙悟空')

# 定义一个函数
# 定义形参是,可以为形参指定默认值
# 指定了默认值以后,如果用户传递了参数则不会生效
# 如果用户没有传递,则默认值生效


def fn(a=1, b=2, c=20):
    print('a=', a)
    print('b=', b)
    print('c=', c)


fn(1, 2)

# 实参的传递方式
# 位置参数
# 位置参数就是将对应位置的实参赋值给对应位置的形参
# 第一个实参赋值给第一个形参,第二个赋值给第二个形参 ...
fn(1, 2, 3)

# 关键字参数
# 关键字参数可以不按照形参定义的顺序去传递,而直接根据参数名去传递参数
fn(b=1, a=3, c=2)
print('hello', end='' + '\n')
# 位置参数和关键字可以混合使用
# 混合使用关键字和位置参数时,必须将位置参数写道前面
fn(1, c=20)
# fn(1, a=10)  # erro


def fn2(a):
    print('a=', a)


# 函数在调用时,解析器不会检查实参的类型
# 实参可以传递任意类型的对象
b = 123
b = True
b = [1, 2, 3]
fn2(b)

fn2(fn)  # a= <function fn at 0x000002666CF89B70>


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

# fn3(123, '456')
# TypeError: unsupported operand type(s) for +: 'int' and 'str'


def fn4(a):
    # 在函数中对形参进行重新赋值,不会影响其他变量
    # a = 20
    # a是一个列表,尝试修改列表中的元素
    # 如果形参指向的是一个对象,当我们通过形参去修改对象是
    # 	会修改所有指向的该对象的变量
    a[0] = 30
    print('a=', a, id(a))


c = 10
c = [1, 2, 3]
fn4(c)
print(c, id(c))


# 传副本
def fn4(a):
    a[0] = 30
    print('a=', a, id(a))


c = [1, 2, 3]
fn4(c.copy())
# or fn(c[:])
print('c=', c, id(c))

03.不定长参数

# 不定长参数
# 定义一个函数,可以求任意数的和
def sum(*nums):
    # 定义一个变量来保存结果
    result = 0
    # 遍历元组,并将元组中的数进行累加
    for n in nums:
        result += n
    print(result)


sum(1, 2, 12, 345, 3245, 3245)

print('*' * 60)  # 分割线

# 在定义函数时,可以在形参前面加上一个*,这样这个形参将会获取到所有的实参
# 他将会将所有的实参保存到一个元组中
# a,b,*c=(1,2,3,4,5,6)
# *a会接受所有的位置实参,并且会将这些实参同一保存到一个元组中(装包)


def fn(*a):
    print('a=', a, type(a))


fn(1, 2, 3, 4, 5)
# 带* 的参数只能有一个
# 带* 的参数可以和其他参数配合使用

print('*' * 60)  # 分割线


def fn2(a, b, *c):
    print('a=', a)
    print('b=', b)
    print('c=', c)


fn2(1, 2, 3, 4, 5)
# 第一个参数给a,第二个给b,其他给c

# 可变参数不是必须写到最后,带*的参数后的所有参数,必须以关键字形式传递
print('*' * 60)  # 分割线


def fn2(a, *b, c):
    print('a=', a)
    print('b=', b)
    print('c=', c)


fn2(1, 2, 3, 4, c=5)
# 第一个参数给a,其他给b ,c必须用关键字传递

print('*' * 60)  # 分割线


def fn2(*a, b, c):
    print('a=', a)
    print('b=', b)
    print('c=', c)


fn2(1, 2, 3, b=4, c=5)
# 所有位置参数给a,b和c必须用关键字传递

print('*' * 60)  # 分割线


def fn2(*, a, b, c):
    print('a=', a)
    print('b=', b)
    print('c=', c)


fn2(a=3, b=4, c=5)
# 如果在形参开头直接写 *, 则要求我们所有的参数必须以关键字来传递

print('*' * 60)  # 分割线

# *形参只能接受位置参数,不能接收关键字参数


# def fn3(*a):
#     print('a=', a)


# fn3(b=1, d=2, c=3)


def fn3(b, c, **a):
    print('b=', b)
    print('c=', c)
    print('a=', a, type(a))


fn3(b=1, d=2, c=3, e=10, f=20)
# **形参可以接收其他的没有定义的关键字参数,会将这些参数同意保存到一个字典中
#	字典的key就是参数的名字,value就是参数的值
# **形参只能有一个,并且必须写在所有参数的最后


# 参数的解包(拆包)
def fn4(a, b, c):
    print('a=', a)
    print('b=', b)
    print('c=', c)


print('*' * 60)  # 分割线

# 创建一个元组
t = (10, 20, 30)
# 传递实参时,也可以在序列类型的参数前添加* ,这样他会自动将序列中的元素依次作为参数传递
# 要求序列中元素的个数必须和形参的个数一致
fn4(*t)

print('*' * 60)  # 分割线

# 创建一个字典
d = {'a': 100, 'b': 200, 'c': 300}
# **对字典解包
fn4(**d)

04.返回值

# 返回值就是函数执行以后返回的结构
# 使用return来指定函数的返回值
# 可以直接使用函数的返回值,也可以通过一个变量来接收函数的返回值

def sum(*nums):
    # 定义一个变量来保存结果
    result = 0
    # 遍历元组,并将元组中的数进行累加
    for n in nums:
        result += n
    return result


result = sum(123, 356, 789)
print(result)

print('*' * 60)  # 分割线


def fn():
    # return [1, 3, 4, 5, 5]
    # return{'name': 123}
    def fn2():
        print('hello')
    return fn2  # 返回值也可以是一个函数


r = fn()  # 这个函数的执行结果就是他的返回值
print(r)
r()
# return后面跟什么值,函数就会返回什么值
# return可以跟任意对象,返回值甚至可以时一个函数

print('*' * 60)  # 分割线


def fn2():
    a = 10


r = fn2()
print(r)

# 如果仅仅写一个return 或者 不写return 则相当于return None

print('*' * 60)  # 分割线


def fn3():
    print('hello')
    return
    print('abc')


r = fn3()
print(r)

# return 在函数中return后的代码都不会执行,return一旦执行,函数自动结束

print('*' * 60)  # 分割线


def fn4():
    for i in range(5):
        if i == 3:
            # break  # 用来退出当前循环
            # continue # 用来跳出当次循环
            return  # 用来结束当前函数
        print(i)
    print('循环执行完毕')


fn4()

print('*' * 60)  # 分割线


def fn5():
    return 10


# fn5和fn5()的区别
print(fn5)  # fn5是函数对象,打印fn5实际是在打印函数对象 <function fn5 at 0x000001A491CA9D08>
print(fn5())  # fn5()是在调用函数,打印fn5()实际上是在打印fn5()函数的返回值 10

05.文档字符串

# 文档字符串
# help()是Python中的内置函数
# 通过help()函数可以查询python中的函数用法
# 语法:help(函数对象)
# help(print)  # 查看print函数的使用说明

# 文档字符串(doc str)
# 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明
#	当我们编写了文档字符串是,就可以通过help()来查看函数的说明
# 	文档字符串非常简单,启示直接在函数的地一样写一个字符串就是文档字符串


def fn(a: int, b: bool, c: str) -> int:
    '''
    这是一个文档字符传的示例
    尽可能使用英文,但是why?对外项目就用英文吧,国内还是中文

    函数的作用
    函数的参数:
        a,作用:类型....
        b,作用:类型....
        c,作用:类型....
    '''
    return 10


help(fn)

06.作用域于命名空间

# 作用域(scope)
# 作用域指的是变量生效的区域
b = 20


def fn():
    a = 10  # a定义在了函数内部,所以他的作用域就是海曙内部,函数外部无法访问
    print('函数内部:', 'a=', a)
    print('函数内部:', 'b=', b)


fn()

# print('函数外部:', 'a=', a)  # NameError: name 'a' is not defined
print('函数外部:', 'b=', b)

print('-' * 60)  # 分割线

# 在python中一共有两种作用域,
# 全局作用域
#  	- 全局作用域在程序执行时创建,在程序执行结束时销毁
#	- 所有函数以外的都是全局作用域
#	- 在全局作用域中定义的变量都属于全局变量,全局变量可以在程序的任意位置被访问
#
# 函数作用域
# 	- 函数作用域在函数调用时创建,在调用结束时销毁
# 	- 函数每调用一次就会产生一个新的函数作用域
# 	- 在函数作用域中定义的变量,都是局部变量,他只能在函数内部被访问
#
# 变量的查找
#	- 当我们使用一个变量时,会优先在当前作用域中寻找变量,如果有则使用
# 		如果没有,则继续去上一级作用域寻找
# 		如果依然没有则则继续去上一级作用域寻找.......以此类推
#		只到找到全局作用域依然没有找到,则会抛出异常


def fn2():
    a = 30
    print('fn2中', 'a=', a)

    def fn3():
        a = 40
        print('fn3中', 'a=', a)
    fn3()


fn2()  # fn3中 a= 30 可以从内往外,不能从外往内

print('-' * 60)  # 分割线

a = 20


def fn3():
    # a = 10  # a= 10 在函数中为变量赋值时,默认都是为局部变量赋值
    # 如果需要函数内部修改全局变量,则需要使用global关键字,来声明变量
    global a  # 声明在函数内部中使用的a时全局变量,此时再去修改a时,就是在修改全局变量
    a = 10
    print('函数内部:', 'a=', a)


fn3()
print('函数外部:', 'a=', a)

print('-' * 60)  # 分割线

# 命名空间(namespace)
# 命名空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间中
# 每一个作用于都会有一个它对应的命名空间
# 全局命名空间用来保存全局变量,函数命名空间用来保存函数中的变量
# 命名空间实际上就是一个字典,是一个专门用来存放变量的字典

# locals()用来获取当前作用域的命名空间
# 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
scope = locals()
print(scope, type(scope))
print(a)
print(scope['a'])

print('-' * 60)  # 分割线

# print(c)  # NameError: name 'c' is not defined

# 向scope中添加一个key-value
scope['c'] = 1000  # 向字典中田间key-value就相当于在全局中创建了一个变量,但是不建议这么做
print(c)

print('-' * 60)  # 分割线


def fn4():
    a = 10
    # scope = locals()  # 在函数内部调用locals()会获取到函数的命名空间
    # scope['b'] = 20 # 可以通过scope来操作函数的命名空间,但是不建议这么做

    # global()函数可以用来在任意位置获取全局命名空间
    global_scope = globals()
    print(scope)
    global_scope['a'] = 30
    print(b)


fn4()
print('函数外部:', 'a=', a)

07.递归

# 尝试求10的阶乘(10!)
# 创建一个函数,求任意数的阶乘
# 1!=1
# 2!=1*2=2
# 3!=1*2*3=6
# 4!=1*2*3*4=24

# 创建一个变量保存结果
n = 10
for i in range(1, 10):
    n *= i
    print(n)

print('-' * 60)  # 分割线

# 创建一个函数,用来求任意数的阶乘


def factorial(n: int):
    '''
        该函数求任意数的阶乘

        参数:
            n 要求阶乘的数字
    '''
    # 创建一个变量来保存结果
    result = n
    for i in range(1, 10):
        result *= i
    return result


print(factorial(10))

print('-' * 60)  # 分割线

# 递归式的函数
# 从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?
#   从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?....
# 递归简单理解就是自己去引用自己
# 递归式函数,在函数中自己调用自己

# 无穷递归,如果这个函数被调用,程序的内存会溢出,效果类似于死循环
# def fn():
#     fn()

# fn()

# 递归是我们解决问题的一种方式,他和循环很像
#   他的整体思想是,将一个大问题,分解为一个一个小问题,直到问题无法分解时,再去解决问题

# 递归式函数的两个要件
#   1.基线条件
#       - 问题可以分解为的最小的问题,当满足基线条件时,递归就不执行了
#   2.递归条件
#       - 将问题继续分解的条件
# 递归和循环类似,基本上可以相互代替的
#   循环编写起来比较容易,但是阅读稍难
#   递归编写起来难,但是方便阅读
# 10! = 10*9!
# 9!=9*8!
# 8!=8*7!
# ...
# 1!=1


def factorial(n):
    '''
        该函数求任意数的阶乘

        参数:
            n 要求阶乘的数字
    '''
    # 创建一个变量来保存结果

    # 基线条件 判断n是否为1,如果为1,则不能继续递归
    if n == 1:
        # 1的阶乘就是1,直接返回1
        return 1
    # 递归条件
    return n * factorial(n - 1)


print(factorial(10))

print('-' * 60)  # 分割线

# 练习
#   创建一个函数power来求任意数字的幂运算 n**i
# 10 ** 5 =10*10**4
# 10 ** 4 =10*10**3


def power(n, i):
    '''
    用来为任意的数字做幂运算
    参数:
        n 要做幂运算的数字
        i 要做幂运算的次数
    '''
    # 基线条件
    if i == 1:
        # 求1次幂
        return n
    # 递归条件
    return n * power(n, i - 1)


print(power(8, 6))
print(8**6)


print('-' * 60)  # 分割线
#   创建一个函数,用来检查一个任意的字符串是否时回文字符串,如果是返回true,否则返回false
#   abcba
#   abcdefgfedcba
#   先检查第一个和最后一个字符是否一致,如果不一致,则不是回文
#   如果一致,则看甚于的部分是否是回文
#   先检查abcdefgfedcba 是不是回文
#   检查bcdefgfedcb 是不是回文
#   检查cdefgfedc 是不是回文
#   检查defgfed 是不是回文
#   检查efgfe 是不是回文
#   检查fgf 是不是回文
#   检查g 是不是回文


def palindrome(s: str):
    '''
        该函数用来检查指定的字符串是否是回文字符串,如果是返回true,否则返回false

        参数: 
            s: 就是要检查的字符串

    '''
    # 基线条件
    if len(s) < 2:
        # 字符串的长度小于2,则字符串一定是回文
        return True
    elif s[0] != s[-1]:
        # 第一个和最后一个不相等,不相等,不是回文字符串
        return False
    # 递归条件
    return palindrome(s[1:-1])


print(palindrome('abcdefgfedcba'))

08.高阶函数

# 高阶函数
# 接收函数作为参数,或者将函数作为返回值的函数是高阶函数
# 当我们使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数
# 创建一个列表
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 定义一个函数
# 可以将指定列表中的所有的偶数,保存到一个新的列表中返回

# 定义一个函数,用来检测一个任意的数字是否是偶数


def fn2(i):
    if i % 2 == 0:
        return True

    return False

# 定义一个函数大于五的数


def fn3(i):
    if i > 5:
        return True
    return False

# 三的倍数函数


def fn4(i):
    if i % 3 == 0:
        return True
    return False


def fn(func, lst):
    '''
        fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回
        参数:
            lst:要进行筛选的列表
    '''

    # 创建一个新列表
    new_list = []

    # 对列表进行筛选
    for n in lst:
        # 判断n的奇偶
        if func(n):
            new_list.append(n)
        # if n > 5:
        #     new_list.append(n)

        # 返回新列表
    return new_list


print(fn(fn4, l))
print(l)

print('-' * 60)

# filter()  # 过滤器
# 可以从序列中过滤出符合条件的元素,保存到一个新的序列中
# 参数:
#   1. 函数,根据该函数过滤序列(可迭代)
#   2. 需要过滤的序列(可迭代)
# 返回值:
#   过滤后的新序列(可迭代的结构)

print(list(filter(fn4, l)))

print('-' * 60)

# fn4时作为参数传递进filter()函数中 不要加()
#   而fn4实际上只有一个作用,就是作为filter()的参数
#   filter()调用完以后,fn4就已经没用
r = filter(fn4, l)
print(list(l))

print('-' * 60)

# 匿名函数 lambda 函数表达式 (语法糖)
#   lambda函数表达式专门用来创建一些简单的函数,他是函数创建的又一种方式
# 语法 lamdba 参数列表:返回值
# 匿名函数一般都是作为参数使用,其他地方不会使用


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


print(fn5(5, 5))

print('-' * 60)

print(lambda a, b: a + b)

(lambda a, b: a + b)(10, 20)

# 一般不会这么用:
print((lambda a, b: a + b)(10, 20))

print('-' * 60)

# 也可以将匿名函数赋值给变量
# fn6=lambda a, b: a + b  # 这个编译器直接给我转成了函数
def fn6(a, b): return a + b


lambda i: i % 3 == 0

r = filter(lambda i: i > 5, l)
print(list(r))

print('-' * 60)

# map()
# map()函数可以对可迭代对象中的所有元素做指定的操作,然后将其添加到一个新的对象返回

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

r = map(lambda i: i ** 2, l)

print(list(r))

print('-' * 60)

# sort()
# 该方法用来对列表中的元素进行排序
# sort()方法默认时直接比较列表中的元素的大小
# 在我们的sort()中可以接收一个关键字参数,key
#   key需要一个函数作为参数,当我们设置了函数作为参数
#   每次都会以列表中的一个元素,作为参数来调用函数,并且使用函数的返回值来比较元素的大小
l = ['bb', 'aaaa', 'c', 'dddddd', 'fff']
l.sort(key=len)
print(l)


l = [2, 5, '1', 3, '6', '4']
l.sort(key=int)
print(l)

print('-' * 60)

# sorted()
# 这个函数和sort()用法基本一致,但是sorted()可以对任意的序列进行排序
#   兵器使用sorted()排序不会影响原来的对象,而是返回一个新的对象
l = [2, 5, '1', 3, '6', '4']
print('排序前', l)
print(sorted(l, key=int))
print('排序后', l)

print('-' * 60)

l = '12312313432532467'
print('排序前', l)
print(sorted(l, key=int))
print('排序后', l)

09.闭包

# 闭包
# 将函数作为返回值返回,也是一种高阶函数
# 这种高阶函数我们就叫做闭包,通过闭包可以创建一些只有当前函数能访问的变量
#   可以将一个私有的数据藏到闭包里

def fn():
    a = 10
    # 函数内部在定义一个函数

    def inner():
        print('我是fn2', a)
    return inner


print(fn())

# r 是一个函数,是调用fn()后返回的函数
# 这个函数是在fn()内部定义的,并不是全局函数
# 所以这个函数总是能访问到fn()内部的变量

r = fn()

r()  # 我是fn2 10

print('-' * 60)

# 求多个数的平均值  最简单方法
nums = [50, 30, 20, 10, 77]

# sum()用来求一个列表中的所有的和
print(sum(nums) / len(nums))

# 创建一个列表,来保存数值
nums = []

print('-' * 60)

# 创建一个函数,用来计算平均值 全局函数方法


def averager(n):
    # 将n添加到列表中
    nums.append(n)

    # 求平均值
    return sum(nums) / len(nums)


print(averager(10))
print(averager(20))
print(averager(30))
print(nums)

print('-' * 60)


# 闭包方法
# 形成闭包的要件
#   ① 函数嵌套
#   ② 将内部函数作为返回值返回
#   ③ 内部函数必须要用外部函数的变量
def make_averager():
    # in_nums = []

    def averager(n):
        in_nums = []
        # 将n添加到列表中
        in_nums.append(n)

        # 求平均值
        return sum(in_nums) / len(in_nums)
    return(averager)


averager = make_averager()
print(averager(10))
print(averager(20))
print(averager(30))
# print(in_nums) NameError: name 'in_nums' is not defined

10.装饰器

# 创建几个函数

def add(a, b):
    '''
        求任意两个数的和
    '''
    r = a + b
    return r


def mul(a, b):
    '''
        求任意两个数的和
    '''
    r = a * b
    return r
# 希望函数可以在计算前,打印开始计算,计算结束后打印计算完毕
#   我们可以直接通过修改函数中的代码来完成这个需求,但是会产生一下问题:
#       ① 如果要修改的函数过多,修改起来会比较麻烦
#       ② 并且不方便后期维护
#       ③ 这样会违反开闭原则()
#           程序的设计,要求开发对程序的扩展,要关闭对程序的修改
#


r = add(123, 456)

print(r)

print('-' * 60)  # 分割线

# 我们希望在不修改原函数的的情况下,来对函数进行扩展


def fn():
    print('我是fn函数.........')

# 只需要根据现有的函数,来创建一个新的函数


def fn2():
    print('函数执行开始...')
    fn()
    print('函数执行结束')


fn2()

print('-' * 60)  # 分割线

# 需要参数的情况  但是这种不能通用


def new_add(a, b):
    print('函数执行开始...')
    r = add(a, b)
    print('函数执行结束')
    return r


r = new_add(123, 456)
print(r)

print('-' * 60)  # 分割线

# 上面的方式,已经可以在不修改源代码的情况下,对函数进行扩展了
#   但是,这种方式要求我们每扩展一个函数就要手动创建一个新的函数,实在是太麻烦了
#   为了解决这个问题,我们创建一个函数,让这个函数可以自动地帮助我们生产函数


def begin_end(old):
    '''
        用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结果\

        参数:
        old 要扩展的函数对象
    '''
    # 创建一个新函数
    def new_function(*args, **kwargs):  # 装包
        print('函数执行开始...')
        # 调用被扩展的函数
        result = old(*args, **kwargs)  # 拆包
        print('函数执行结束...')
        # 返回函数的执行结果
        return result
    # 返回新函数
    return new_function


f = begin_end(add)
result = f(1, 2)
print('result=', result)

print('-' * 60)  # 分割线

f2 = begin_end(mul)
result = f2(3, 2)
print('result=', result)

print('-' * 60)  # 分割线

f3 = begin_end(fn)
f3()

print('-' * 60)  # 分割线

# 像begin_end()这种函数我们就称他为装饰器
#   通过装饰器, 可以在不修改原来函数的情况下对函数进行扩展
#   在开发中,我们都是通过装饰器来扩展函数的功能的
# 在定义函数时,可以通过@装饰器,来使用指定的装饰器,来装饰当前函数
#   可以同时为一个函数指定多个装饰器,这样函数将会按照由内向外的装饰


def fn3(old):
    '''
        用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结果\

        参数:
        old 要扩展的函数对象
    '''
    # 创建一个新函数
    def new_function(*args, **kwargs):  # 装包
        print('fn3--函数执行开始...')
        # 调用被扩展的函数
        result = old(*args, **kwargs)  # 拆包
        print('fn3-函数执行结束...')
        # 返回函数的执行结果
        return result
    # 返回新函数
    return new_function


@begin_end
@fn3
def say_hello():
    print('大家好')


say_hello()

第六章 对象

# 第六章 对象 (Object)

## 什么是对象?
	- 对象是内存中专门用来存储数据的一块区域
	- 对象中可以存放各种数据(比如:数字,布尔值,代码)
	- 对象中保存由三部分组成:
		1. 对象的标识(id)
		2. 对象的类型(type)
		3. 对象的值(value)

## 面向对象(oop)
	- Python是一门面向对象的编程语言
	- 所谓的面向对象的语言,简单来说就是语言中的所有操作都是通过对象来进行的
	- 面向过程的编程语言
		- 面向过程指的是将我们的程序的逻辑分解为一个一个的步骤,
			通过对每个步骤的抽象,来完成程序
		- 例子:
			- 孩子上学 
				1. 妈妈起床
				2. 妈妈上厕所
				3. 妈妈洗漱
				4. 妈妈做早饭
				5. 叫孩子起床
				6. 孩子上厕所
				7. 孩子要洗漱
				8. 孩子吃饭
				9. 孩子背着书包上学校
		
		- 面向过程的编程思想将一个功能分解为一个一个小的步骤
			我们通过完成一个一个的小的步骤来完成一个程序
		- 这种编程方式,符合我们人类的思维,编写起来相对比较简单
		- 但是这种方式编写代码的王婉只适用于一个功能,
			如果要实现别的功能,即使功能相差极小,也往往要重新编写代码
			所以可复用行比较低,并且难以维护

	- 面向对象的编程语言
		- 面向对象的编程语言,关注的是对象,而不关注过程
		- 对于面向对象的语言来说,一切都是对象
		- 例子:
			1. 孩他妈起床叫孩子上学
		- 面向对象的编程思想,将所有的功能同一保存到对应的对象中
			比如,妈妈功能保存到妈妈的对象中,孩子的功能保存在孩子的对象中
			要使用某个功能,直接找到对应的对象即可
		- 这种方式白那些的代码,比较容易越多,容易维护,容易复用
		- 但是这种方式编写, 不太符合常规的思维,所以编写起来稍微麻烦一点

	- 简单归纳一下,面向对象的思想
		1. 找对象
		2. 搞对象

## 类(class)
	- 我们目前所学习的对象,都是python所内置的对象
	- 但是内置对象比那个不能满足所有的需求,所以我们在开发中经常需要自定义一些对象
	- 类,简单理解它就相当于一个图纸,在程序中我们需要根据类来创建对象
	- 类就是对象的图纸!
	- 我们也称对象是类的实例(instance)
	- 如果多个对象是通过一个类创建的, 我们称这些对象是一类对象
	- 像 int(),float(),bool(),str(),list(),dict()...这些都是类
	- a= int(10) #创建一个int类的实例,等价于a=10
	- 我们自定义的类都需要使用大写字母开头,使用大驼峰命名法(帕斯卡命名法)来对类命名

	- 类也是一个对象!
	- 类就是一个用来创建对象的对象!
	- 类是type类型的对象,定义类实际上就是定义了一个type类型的对象

## 使用类创建对象的流程
	- 创建一个变量mc
	- 在内存中创建一个新对象
	- 将对象的id赋值给变量

## 类的定义
	- 类和对象都是对现实生活中的事务,或者程序中内容的抽象
	- 实际上所有的事务都由两部分构成:
		1. 数据(属性)
		2. 行为(方法)
	- 在类的代码块中,我们可以定义变量和函数
		变量会成为该类实例的公共属性,所有该类实例都可以通过 对象.属性名 的形式访问
		函数会成为该类实例的公共方法,所有该类实力都可以同通过 对象.方法名() 的形式调用方法

	- 注意: 
		方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参!!!

	- 实例为什么能访问到类中的属性和方法
		类中定义的属性和方法都是公共的,任何该类实例都可以访问

		- 属性和方法查找的流程
			当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,
				如果有,则直接返回当前的对象的属性值,
				如果没有,则去当前对象的类对象中去寻找,
					如果有则返回类对象的属性值
					如果没有则报错
		- 类对象和实例对象都可以保存属性(方法)
			- 如果这个属性(方法)是所有实例共享的,则应该将其保存到类对象中
			- 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中

		- 一般情况下,属性保存到实例对象中,
			方法保存到类对象中

## 创建对象的流程
    最先执行执行类的代码块中的代码(只在类定义的时候执行一次)
    p1= Person()的运行流程
    	1. 创建一个变量mc
    	2. 在内存中创建一个新对象
    	3. __init__(self)方法执行
    	4. 将对象的id赋值给变量

## 类的基本结构
	class 类名([父类]) :
		[公共属性.....]

		# 对象的初始化方法
		def __init__(self,....):
			....
		# 其他方法
		def method_1(self,....):
			....
		def method_2(self,....):
			....
		....

	- 练习:
		尝试自定义一个表示狗的类(Dog)
			属性:
				name
				age
				gender
				height
				...			
			方法:
				jiao()
				yao()
				run()
				...

01.类的简介

a = int(10)  # 创建一个int类的实例
b = str('hello')  # 创建一个str类的实例
print(a, type(a))  # 10 <class 'int'>
print(b, type(b))  # hello <class 'str'>

print('-' * 60)  # 分割线

# 定义一个简单的类
# 使用class关键字来定义类,语法和函数很像!
# class 类名([父类]):
#   代码块


class MyClass():
    pass


print(MyClass)  # <class '__main__.MyClass'>

print('-' * 60)  # 分割线

# 使用MyClass创建一个对象
# 使用类来创建对象,就像调用一个函数一样
mc = MyClass()  # mc就是通过MyClass创建的对象,mc是MyClass的实例
mc_2 = MyClass()
mc_3 = MyClass()
mc_4 = MyClass()
# mc* 都是MyClass的实例,他们收拾一类对象

print(mc, type(mc))  # <__main__.MyClass object at 0x0000019D6D0286A0> <class '__main__.MyClass'>

print('-' * 60)  # 分割线

# isinstance()用来检查是一个对象是否是一个类的实例
result = isinstance(mc, MyClass)
print(result)

print('-' * 60)  # 分割线

print(id(MyClass), type(MyClass))  # 2512224225576 <class 'type'>

print('-' * 60)  # 分割线

# 现在我们通过MyClass这个类创建的对象都是一个空对象
# 也就是对象中实际什么都没有,就相当于是一个空的盒子
# 可以向对象中添加变量,对象中的变量称为属性
# 语法: 对象.属性名=属性值

mc.name = '孙悟空'
print(mc.name)

mc_2.name = '猪八戒'
print(mc_2.name)

02.定义类

# 尝试定义一个表示人的类
class Person:
    # 再类的代码块中,我们可以定义变量和函数
    # 在类中我们所定义的变量,将会称为所有的实例的公共属性
    # 所有实例都可以访问这些变量

    name = 'swk'  # 公共属性,所有实例都可以访问

    # 在类中也可以定义函数,类中定义的函数,我们称之为方法
    # 这些方法可以通过该类的所有实例来访问

    def say_hello(self):
        # 方法每次被调用,我们的解析器都会自动传递第一个实参
        # 第一个参数,就是调用方法对象本身
        # 如果是p1调用的,则第一个参数就是p1本事,以此类推
        # 一般我们都会将这个参数命名为self

        # sqy_hello()这个方法,可以显示如下格式的数据:
        #   你好! 我是 xxx
        # 在方法中不能直接访问类中的属性
        print("你好!! 我是%s" % self.name)

# 创建Person的实例


p1 = Person()
p2 = Person()

print(p1.name, p2.name)

print('-' * 60)  # 分割线

# 调用方法: 对象.方法名()
# 方法调用和函数调用的区别
# 如果是函数调用,则调用是传几个参数,就会有几个实参
# 但是如果是方法调用,默认传递一个参数,所以方法中至少要定义一个形参
p1.say_hello()
p2.say_hello()
# 你好!!
# 你好!!

print('-' * 60)  # 分割线

# 修改p1的name属性
p1.name = '猪八戒'
p2.name = '沙和尚'
# 删除p2的name属性
# del p2.name

print(p1.name, p2.name)  # 猪八戒 沙和尚 # 猪八戒 swk

print('-' * 60)  # 分割线

p1.say_hello()  # 你好!! 我是猪八戒
p2.say_hello()  # 你好!! 我是沙和尚

03.对象的初始化

class Person:
    # print('Person代码块中的代码')
    # 在类中可以定义一些特殊方法(魔术方法)
    # 特殊方法都是以__开头,__结尾的方法
    # 特殊方法不需要我们自己调用,不要尝试去调用特殊方法
    # 特殊方法将会在特殊的时刻自动调用
    # 学习特殊方法:
    #   1. 特殊方法什么时候调用
    #   2. 特殊方法有什么作用
    # 创建对象的流程
    # 最先执行执行类的代码块中的代码(只在类定义的时候执行一次)
    # p1= Person()的运行流程
    # 1. 创建一个变量mc
    # 2. 在内存中创建一个新对象
    # 3. __init__(self)方法执行
    # 4. 将对象的id赋值给变量

    # init会在对象创建以后立即执行  init(初始化)
    # init可以用来向新创建的对象中初始化属性
    # 调用类创建对象时,类后边的所有参数都会依次传递到init()中
    def __init__(self, name: str):
        # print(self)
        # 可以通过self向新建的对象中初始化属性
        self.name = name

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


# 目前来讲,对于Person类来说name是碧血的,并且每一个对象中的name属性基本上都是不同的
# 而我们现在是将name属性在定义为对象以后,手动添加到对象中,这种方式很容易出现错误
# 我们希望,在创建对象时,必须设置name属性,如果不设置对象将无法创建
#   并且属性的创建应该时自动完成的,而不是在创建对象有以后手动完成


# # 麻烦方法
# p1 = Person()
# # 手动向对象添加name属性
# p1.name = '孙悟空'

# p2 = Person()
# p2.name = '猪八戒'

# p3 = Person()
# p3.name = '沙和尚'

# p3.say_hello()  # AttributeError: 'Person' object has no attribute 'name'

# print('-' * 60)

# 新方法
# p1.__init__()  # 不要这么做
p1 = Person('孙悟空')
p2 = Person('猪八戒')
p3 = Person('沙和尚')
p4 = Person('唐僧')
p1.say_hello()
p2.say_hello()
p3.say_hello()
p4.say_hello()

04.练习

class Dog:
    '''
        表示狗的类
    '''

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

    def jiao(self):
        '''狗叫的方法'''
        print('汪汪汪')

    def yao(self):
        '''狗咬的方法'''
        print('咬你')

    def run(self):
        '''狗跑的方法'''
        print('%s拜拜了您嘞' % self.name)


d = Dog('旺财', 8, 'male', 30)
print(d.name, d.age, d.gender, d.height)

d.jiao()
d.yao()
d.run()

# 目前我们可以直接通过 对象.属性的方式来修改属性的值,这种方式导致对象中的属性可以随意修改
# 非常不安全,值可以任意修改,不论对错
# 需要一种方式来增强数据的安全性
#   1. 数据不能随便修改(我让你改才能改,不让你改你就不能改)
#   2. 属性不能修改为任意的值(年龄不能修改为负数)
d.name = '阿黄'
d.age = -10
d.run()

05.修改属性名封装

# 封装是面向对象的三大特性之一
# 封装指的是隐藏对象中不希望被外部所访问的属性或方法
# 如何隐藏一个对象中的属性
# 最基本的封装:
#  - 将对象的属性名,修改为一个外部不知道的名字
# 如何获取(修改)对象中的属性?
#   - 需要提供一个getter和setter方法使外部可以访问到属性
#   - getter 获取对象中的指定属性(get_属性名)
#   - setter 用来设置对象的指定属性(set_属性名)
# 使用封装确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
#   1.隐藏了属性名,使调用者无法随意的修改对象中的属性
#   2.增加了getter和setter方法,很好的控制了属性是否是只读的
#       如果希望属性是只读的,则可以直接去掉setter方法
#       如果希望属性不能被外部访问,则可以直接去掉getter方法
#   3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
#   4.使用getter方法获取属性,使用setter方法设置属性
#       可以在读取属性和修改属性的同时做一些其他的处理
class Dog:
    '''
        表示狗的类
    '''

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

    def say_hello(self):
        print('大家好,我是%s' % self.hidden_name)

    def get_name(self):
        '''
            get_name()用来获取对象的name属性
        '''
        print('用户读取了属性')
        return self.hidden_name

    def set_name(self, name):
        print('用户修改了属性')
        self.hidden_name = name

    def get_age(self):
        '''
            get_name()用来获取对象的name属性
        '''
        return self.hidden_age

    def set_age(self, age):
        if age > 0:
            self.hidden_age = age


d = Dog('小黑', 10)

d.say_hello()


print(d.get_name())

# 调用setter方法来修改name属性
d.set_name('旺财')
print(d.get_name())
d.say_hello()

06.python自带封装

class Rectangle:
    '''
        表示矩形的类
    '''

    def __init__(self, width, height):
        self.hidden_width = width
        self.hidden_height = height

    def get_width(self):
        return self.hidden_width

    def get_height(self):
        return self.hidden_height

    def set_width(self, width):
        self.hidden_width = width

    def set_height(self, height):
        self.hidden_height = height

    def get_area(self):
        return self.hidden_height * self.hidden_width


# r = Rectangle(5, 2)
# r.set_height(10)
# r.set_width(3)
# print(r.get_height())
# print(r.get_area())


# 可以为对象的属性使用双__开头,__xxx
# 双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
# 其实隐藏属性只不过是python自动为属性改了一个名字
#   实际上是将名字改为了 _类名__属性名 比如 __name -> _Person__name
# 防君子不防小人

# class Person:
#     def __init__(self, name):
#         self.__name = name

#     def get_name(self):
#         return self.__name

#     def set_name(self, name):
#         self.__name = name


# p = Person('孙悟空')
# # __开头的属性是隐藏属性,无法通过对象访问
# # print(p.__name)  # AttributeError: 'Person' object has no attribute '__name'
# # p.__name = '猪八戒'  # AttributeError: 'Person' object has no attribute '__name'
# print(p.get_name())
# print('查看隐藏', p._Person__name)
# p._Person__name = '猪八戒'  # 修改隐藏
# print('查看隐藏 且修改为猪八戒', p._Person__name)

# 使用__开头的属性,实际上依然可以发在外部访问,所以这种方式我们一般不用
#   一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
#   一般情况下,使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性
class Person:
    def __init__(self, name):
        self._name = name

    def get_name(self):
        return self._name

    def set_name(self, name):
        self._name = name


p = Person('孙悟空')
print(p._name)

07.封装中的’装饰器’

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

    # property装饰器,用来将一个get方法转换为属性
    # 添加为property装饰器后,我们就可以像调用属性一样使用get方法
    # 使用property装饰的方法,必须和属性名是一样的

    @property
    def name(self):
        print('get方法执行了')
        return self._name

    # setter方法的装饰器:@属性名.setter
    # 且使用setter装饰器是必须有property装饰器

    @name.setter
    def name(self, name):
        print('setter方法调用了')
        self._name = name

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age


p = Person('猪八戒', 10)

p.name = '孙悟空'
p.age = '18'
print(p.name, p.age)

08.继承

# 继承

# 定义一个类 Animal(动物)
#   这个类中需要两个方法:run() sleep()


class Animal:
    def run(self):
        print('动物会跑')

    def sleep(self):
        print('动物睡觉')


# 定义一个列 Dog (狗)
# 这个类中需要三个方法:run() sleep() bark()
# 有一个类,能够实现我们需要大部分功能,但是不能实现全部功能
# 如何能让这个类,来实现全部的功能呢 ?
#   ① 直接修改这个类,在这个类中添加我们需要的功能
#       - 修改起来麻烦,并且会违反OCP原则
#   ② 直接创建一个新的类
#       - 创建一个新的类比较麻烦,并且需要大量的进行复制粘贴,会出现大量的重复代码
#   ③ 直接从Animal类中来继承它的属性和方法
#       - 继承是面向对象的三大特征之一
#       - 通过继承我们可以使一个类获取到其他类中的属性和方法
#       - 在定义类时,可以在类名后的括号中指定当前类的父类(超类,基类,super)
#           子类(衍生类)可以直接继承父类中的所有的属性和方法
# 通过继承可以直接让子类获取到父类的方法或属性,避免白那些重复性代码,并且也符合OCP原则
#   所以我们经常需要通过继承来对一个类进行扩展

class Dog(Animal):
    def bark(self):
        print('动物嚎叫')
    # 重写
    def run(self):
        print('狗在跑')


class HaShiQi(Dog):
    def fansha(self):
        print('我是一只傻傻的哈士奇')


d = Dog()
d.run()
d.sleep()
d.bark()

# isinstance() 用来检查一个对象是否是一个类的实例
# 如果这个类是这个对象的父类,也会返回True
# 所有的对象都是object的实例
r = isinstance(d, Dog)
r = isinstance(d, Animal)

print(r)


# 在创建类时,如果省略了父类,则默认父类为object
#   object是所有类的父类,所有类都继承自object
# 检查一个类是否是另一个类的子类 issubclass()

print(issubclass(Dog, Animal))

09.重写

# 重写
# 如果在子类中有和父类同名的方法,则通过子类实例去调用方法时,
#   会调用子类的方法而不是父类的方法,这个特点我们成为叫做方法的重写(覆盖,override)


class Animal:
    def run(self):
        print('动物会跑')

    def sleep(self):
        print('动物睡觉')


class Dog(Animal):
    def bark(self):
        print('动物嚎叫')
    # 重写

    def run(self):
        print('狗在跑')


#
d = Dog()
d.run()
d.sleep()
d.bark()

print('-' * 60)

# 当我们调用一个对象的方法时,
#   会优先取当前对象中寻找是否具有该方法,如果有则直接调用
#   如果没有,则取当前对象的父类中勋章,如果父类中有则直接调用父类中的方法,
#   如果没有,则去父类的父类寻找,依次类推,只到找到object ,如果依然没有,则报错 


class A:
    def test(self):
        print('AAAAA')


class B(A):
    def test(self):
        print('BBBBB')


class C(B):
    def test(self):
        print('CCCCC')


c = C()
c.test()

10.特殊方法继承

class Animal:
    def __init__(self, name):
        self._name = name

    def run(self):
        print('动物会跑')

    def sleep(self):
        print('动物睡觉')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

# 父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法


class Dog(Animal):
    def __init__(self, name, age):
        # 希望直接调用父类的__init__来初始化父类中定义的属性
        # super()可以用来获取当前类的父类,并且通过super()返回对象调用父类方法时,不用传递self
        super().__init__(name)
        # self._name = name
        self._age = age

    def bark(self):
        print('汪汪汪')
    # 重写

    def run(self):
        print('狗在跑')

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age


# d = Dog() #SyntaxError: invalid syntax

d = Dog('旺财', 18)
d.name = '小黑'
print(d.name)
print(d.age)

11.多重继承

class A(object):
    def test(self):
        print('AAAAA')


class B(object):
    def test(self):
        print('b中的test方法~~~')

    def test2(self):
        print('BBBBB')

# 在python中式支持多重继承的,也就是我们可以为一个类同时指定多个父类
#   可以在类名的()后面添加多个类,来实现多重继承
#   多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
# 在开发中没有特殊情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
# 如果多个父类中有同名方法,则会在第一个父类中寻找,然后找第二个,然后找第三个....
# 前面的父类方法会覆盖后边的父类方法


class C(A, B):  # B,A时调用的时b中的test方法 b中的test方法~~~
    pass
    # __bases__ 这个属性可以用来获取当前类的所有父类


print(A.__bases__)  # (<class 'object'>,)
print(B.__bases__)  # (<class 'object'>,)
print(C.__bases__)  # (<class '__main__.B'>,)

c = C()
c.test()
c.test2()

12.多态

# 多态时面向对象的三大特征之一
# 堕胎从字面上理解是多种形态
# 狗(狼狗,藏獒,哈士奇,古牧....)
# 一个对象以不同的形态去呈现

# 定义两个类
class A:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name


class B:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

    def __len__(self):
        return 10


class C:
    pass


a = A('孙悟空')
b = B('猪八戒')
c = C()
# 定义一个函数
# 对于say_hello()这个函数来说,只要对象中含有name属性,他就可以作为参数传递
#   这个函数并不会考虑对象的类型,只要有name属性即可


def say_hello(obj):
    print('你好 %s' % obj.name)


# say_hello(c)  # AttributeError: 'C' object has no attribute 'name'

# 在say_hello2中我们做了一个类型检查,也就是只有obj时a类型的对象时,才可以正常使用
#   其他类型的对象都无法使用该函数,这个函数就违反了多态
# 违反了多态的对象,只适用于一种类型的对象,无法处理其他类型对象,这样导致函数的适应性非常的差
# 注意 像ininstance()这种函数,在开发中一般是不会使用的!!!


def say_hello2(obj):
    if isinstance(obj, A):
        print('你好 %s' % obj.name)


say_hello2(a)


# 鸭子类型
# 如果一个东西走路像鸭子,叫声像鸭子,那么他就是鸭子

# len()
# 之所以一个对象能通过len()来获取,是因为对象中具有一个特殊方法 __len__
# 换句话说,只要对象中具有__len__特殊方法,就可以通过len()来获取它的长度
l = [1, 2, 3]
s = 'hello'
print(len(s))
print(len(a))  # TypeError: object of type 'A' has no len()
print(len(b))
print(len(c))  # TypeError: object of type 'C' has no len()


# 面向对象的三大特征
#   封装
#       - 确保对象中的数据安全
#   继承
#       - 保证了对象的可扩展性
#   多态
#       - 保证了程序的灵活性

13.类中的属性和方法

# 定义一个类
class A(object):
    # 类属性
    # 实例属性
    # 类方法
    # 实例方法
    # 静态方法



    # 类属性,直接在类中定义的属性时类属性
    # 类属性可以通过类或类的实例访问到
    # 但是类属性之恶能通过类对象来修改,无法通过实例对象修改
    count = 0

    def __int__(self):
        # 实例属性,通过实例对象添加的属性属于实例属性
        #   实例属性只能通过实例对象来访问和修改,类对象无法访问修改
        self.name = '孙悟空'

    # 实例方法
    # 在类中定义,以self为第一参数的方法都是实例方法
    # 实例方法在调用时,python会将调用对象作为self传入
    # 实例方法可以通过实例和类去调用
    #   当通过实例调用是,会自动将当前调用对象作为self传入
    #   当通过类调用时,不会自动传递self,此时我们必须手动传递self
    def test(self):
        print('这是test方法', self)


    # 类方法
    # 在类内部使用 @classmethod 来修饰的方法属于类方法
    # 类方法的第一个参数时cls,也会被自动传递,cls就是当前的类对象
    #   类方法和实例方法的区别,实例方法的第一个参数时self,而类方法的第一个参数是cls
    #   类方法可以通过类去调用,也可以通过实例调用,没有区别
    @classmethod
    def test_2(cls):
        print('这是test_2方法,他是一个类方法~~~~~', cls)


    # 静态方法
    # 在类中使用 @staticmethod 来修饰的方法属于静态方法
    # 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用
    # 静态方法,基本上是一个和当前类无关的方法,他只是一个保存到当前类中的函数
    # 静态方法一般都是一些工具方法,和当前类无关
    @staticmethod
    def test_3():
        print('test_3执行了!!!')



# 实例
a = A()

# 实例属性,通过实例对象添加的属性属于实例属性
a.count = 10

# 类属性
A.count = 100

print('A', A.count)
print('a', a.count)
# A 100
# a 10

# 实例方法
a.test()
# a.test() 等价于 A.test(a)
A.test(a)  # TypeError: test() missing 1 required positional argument: 'self'

# 类方法
# A.test2() 等价于 a.test2()
A.test_2()


# 静态方法
# 在类中使用 @staticmethod 来修饰的方法属于静态方法
def test_3():
    print('test_3执行了!!!')

14.垃圾回收

# 垃圾回收
# 就像生活中会产生垃圾一样,程序在运行过程当中,也会产生垃圾
# 程序运行过程中产生的垃圾,会影响到程序的运行性能,所以这些垃圾必须被及时清理
# 没用的东西就是垃圾
# 在程序中没有被引用的对象就是垃圾,这种垃圾过多之后会影响到程序的运行性能
#   所以我们必须进行及时垃圾回收,所谓的垃圾回收就是将垃圾对象从内存中删除
# 在python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除
#   所以我们不用手动处理垃圾回收


class A:
    def __init__(self):
        self.name = 'A类'
    # del是一个特殊方法,他会在对象被垃圾回收前调用
    # 一般不会用,通过它知道对象被删除
    def __del__(self):
        print('A()对象被删除了~~~~', self)


a = A()
print(a.name)
b = a  # 又使用一个变量b,来引用a对应的对象
a = None  # 将a设置为了None,此时没有任何的变量对A()对象进行引用,他就变成了垃圾

# del a, b # A()对象被删除了~~~~ <__main__.A object at 0x000001AB3FF584A8>
input('按回车键退出.......')

15.特殊方法

# 特殊方法也成为魔术方法
# 特殊方法都是使用__开头和结尾的
# 特殊方法不许要手动调用,需要在一些特殊情况下自动执行
# 特殊方法用来配合多态
# 定义一个Person类
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 这个特殊方法会在禅师将对象转换为字符串的时候调用
    # 它的作用可以用来指定对象转换为字符串的结果 (print()函数)
    def __str__(self):
        return 'hello Person name=%s,age=%d' % (self.name, self.age)
    # hello Person name=孙悟空,age=18
    # hello Person name=猪八戒,age=28

    # 这个特殊方法会在对当前对象使用repr()函数时调用
    # 它的作用是指定对象在 '交互模式' 中直接输出的结果

    def __repr__(self):
        return 'hello'

    # 运算方法
    # object.__add__(self, other) +
    # object.__sub__(self, other) -
    # object.__mul__(self, other) *
    # object.__matmul__(self, other)
    # object.__truediv__(self, other)
    # object.__floordiv__(self, other)
    # object.__mod__(self, other)
    # object.__divmod__(self, other)
    # object.__pow__(self, other[, modulo])
    # object.__lshift__(self, other)
    # object.__rshift__(self, other)
    # object.__and__(self, other)
    # object.__xor__(self, other)
    # object.__or__(self, other)

    # object.__lt__(self, other) 小于 <
    # object.__le__(self, other) 小于等于 <=
    # object.__eq__(self, other) 等于 ==
    # object.__ne__(self, other) 不能于 !=
    # object.__gt__(self, other) 大于 >
    # object.__ge__(self, other) 大于等于 >=

    # __gt__()会在对象在做大于比较的时候调用,该方法的返回值将会作为比较结果
    # 需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
    def __gt__(self, other):
        return self.age > other.age

    # __len__() 获取对象的长度

    # __bool__(self)
    # 可以通过bool来指定对象转换为布尔值的情况
    def __bool__(self):
        return self.age > 17


# 创建两个Person类的实例
p1 = Person('孙悟空', 8)
p2 = Person('猪八戒', 28)

# 打印p1
# 当我们打印一个对象时,实际上打印的是对象中的特殊方法 __str__() 的返回值
# print(p1)  # <__main__.Person object at 0x000002295F746C88>
print(p1)  # hello Person
print(p2)  # hello Person

# 添加__repr__()特殊方法后
print(repr(p1))  # hello


# print(p1 > p2)  # TypeError: '>' not supported between instances of 'Person' and 'Person'
# 添加__gt__()特殊方法后
print(p1 > p2)  # False

# __bool__()特殊方法
# 改前
# print(bool(p1)) # True 只要p1不为空就是True
# 改后 实例的年龄属性是否大于17
print(bool(p1))  # False
# 不建议这么做:
if p1:
    print(p1.name, '成年了')
else:
    print(p1.name, '未成年')

16.模块->test_module

# 模块 (module)
# 模块化,模块化指将以一个完整的程序分解为一个一个小的模块
#   通过将模块组合,来搭建出一个完整的程序
# 不采用模块化,统一将所有的代码编写到一个代码中
# 采用模块化,将程序分别编写到多个文件中
#   模块化的优点:
#       ① 方便开发
#       ② 方便维护
#       ③ 模块可以复用
#       ④

# 在pyton中一个py文件就是一个模块,要想创建模块,实际上就是创建python文件
# 注意:模块名要符合标识符的规范

# 在一个模块中引入外部模块
# ① import 模块名 (模块名,就是python文件的名字,注意不要py)
# ② import 模块名 as 模块别名 (起别名)
#   - 可以引入同一个模块多次,但是模块的实例只会创建一个
#   - import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序开头
#   - 在每一个模块内部都有一个__name__ , 通过这个属性可以获取到模块的名字
#   - __name__属性值为 __main__的模块是主模块,一个程序中只会有一个主模块


# ①
# import test_module  # 我是一个test_module模块
# # import test_module
# # import test_module
# print(test_module)
# # <module 'test_module' from 'D:\\python练习\\lesson_06\\code\\test_module.py'>

# ②
import test_module as test
print(test)
# <module 'test_module' from 'D:\\python练习\\lesson_06\\code\\test_module.py'>

# __name__
print(test.__name__)


test_module.py
print('我是一个test_module模块')

17.模块->m.py

# import xxx
# import xxx as yyy
# from xxx import yyy,zzz,fff
# from xxx import *
# from xxx import yyy as zz


# # 直接import xxx 打印需要 xxx.变量(函数/类)
# import m
# print(m.a, m.b)  # 10 20
# print(m.test(), m.test2())  # m.test m.test2
# p = m.Person()
# print(p.name)


# # 选择引入函数,变量,类
# from m import test  # 引入单个
# from m import Person, test2  # 引入多个,也可以添加变量 ,a,b
# from m import *  # 引入到模块中的所有内容,一般不会使用

# test()  # m.test
# p1 = Person()
# print(p1.name)  # 孙悟空
# test2()  # m.test2

# print(a, b)   # 引入所有后可以使用 模块中的变量 # NameError: name 'c' is not defined
# # print(_c)  # NameError: name '_c' is not defined # 因为在模块中加入了 _c 是隐藏的


# # 也可以为引入的变量使用别名
# # 语法: form 模块名 import 变量 as 别名
# from m import test2 as new_test2
# # test2()  # NameError: name 'test2' is not defined
# new_test2() # m.test2

# from m import *
# print(_c) # NameError: name '_c' is not defined





m.py
# 可以在模块中定义变量,在模块中定义的变量,在引入模块后,就可以直接使用了
a = 10
b = 20

# 添加了_的变量,只能在模块内部访问,在同故宫import * 引入式,不会引入_开头的变量
_c = 0

# 可以在模块中定义函数,同样可以通过模块访问到


def test():
    print('m.test')


def test2():
    print('m.test2')

# 也可以定义类


class Person:
    def __init__(self):
        self.name = '孙悟空'

# 编写测试代码,这部分代码,只要当 当前文件作为主模块的时候才需要执行
#   而当模块被其它模块引用时,不需要执行,此时我们就必须要检查当前模块是否是主模块

18.包

# 包 Package
# 包也是一个模块
# 当我们模块中的代码过多时,或者一个模块需要被分解为多个模块时,这时就需要使用到包
# 普通的模块就是一个py文件,而包是一个文件夹
# 包中必须要有一个__init__.py这个文件,这个文件中可以包含有包中的主要内容


# import hello
# print(hello)  # <module 'hello' (namespace)>
# print(hello.init_a, hello.init_b)  # 10 20 # 实际是引用的 hello/__init__中的变量
# hello.test()  # hello.init.test


# from hello import a, b
# print(a.c, b.d)


# __pycache__ 是模块的缓存文件
# py代码在执行前,需要被解析器先转换为机器码,然后再执行
#   所以在使用模块(包)时,也需要将模块的代码先转换为机器码然后再交给计算机执行
#   而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中
#   这样在下次加载这个模块(包)时,就可以不再重新编译而是直接加载韩村中编译好的代码

19.python标准库

# 开箱即用
# 为了实现开箱即用思想,python中为我们提供了一个模块的标准库
# 在这个标准库中,有很多很强大的模块我们可以直接使用
#   并且标准库会随python的安装一同安装
# sys模块,它里面提供了一些变量和函数,使我们可以获取到Python解析器的信息
#   或者通过函数来操作Python解析器

# pprint模块 它给我们提供了一个方法pprint()该方法可以用来对打印的数据做简单的格式化
import pprint


# 引入sys模块
import sys

# print(sys)  # <module 'sys' (built-in)>

# sys.argv
# 获取执行代码时,命令行中所包含的参数
# 该属性是一个列表,列表中保存了当前命令的列表
# print(sys.argv)

# sys.modules
# 获取当前程序中引入的所有模块
# modules 是一个字典, key是模块的名字,value是模块对象
# print(sys.modules)
# pprint.pprint(sys.modules)

# sys.path
# 他是一个列表,列表中保存的是模块的搜索路径
# pprint.pprint(sys.path)
# ['D:\\python练习\\lesson_06\\code',
#  'C:\\Program Files\\python\\python36.zip',
#  'C:\\Program Files\\python\\DLLs',
#  'C:\\Program Files\\python\\lib',
#  'C:\\Program Files\\python',
#  'C:\\Program Files\\python\\lib\\site-packages',
#  'C:\\Program Files\\python\\lib\\site-packages\\win32',
#  'C:\\Program Files\\python\\lib\\site-packages\\win32\\lib',
#  'C:\\Program Files\\python\\lib\\site-packages\\Pythonwin']

# sys.platfrom
# 当前python运行的平台
# print(sys.platform) # win32


# sys.exit()
# 函数用来退出程序
# sys.exit('程序出现异常,结束')
# print(sys.platform) # 出不来了

# os 模块让我们可以对操作系统进行访问
import os
print(os)
# <module 'os' from 'C:\\Program Files\\python\\lib\\os.py'>


# os.environ
# 通过这个属性可以获取到系统的环境变量
pprint.pprint(os.environ['path'])


# os.system()
# 可以用来执行操作系统的命令
os.system('dir')
os.system('notepad')# 记事本

第七章 异常和文件

# 第七章 异常和文件
	
## 异常
	- 程序在运行过程当中,不可避免的会出现一些错误,比如
		使用了没有赋值过的变量,
		使用来不存在的索引
		除0
		...
	- 这些错误在程序中,我们称之为异常
	- 程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行


## 处理异常
	- 程序运行时出现异常,目的并不是让我们的程序直接终止!!!
	- python是希望再出现异常时,我们可以编写代买来对异常进行处理

	- try语句
		try:
			代码块 (可能出现错误的语句)

		except 异常类型 as 异常名:
			代码块 (出现错误以后的处理方式)
		except 异常类型 as 异常名:
			代码块 (出现错误以后的处理方式)
		except 异常类型 as 异常名:
			代码块 (出现错误以后的处理方式)

		else:
			代码块 (没出错时要执行的语句)
		finally:
			代码块 (该代码块总会执行)

		- try是必须的,else语句有没有都行
		  except和finally至少有一个
		- 可以将可能出错的代码放入try语句,这样如果代码没有错误,则会正常执行
			如果出现错误,则会执行expect子句中的代码,这样我们就可以通过代码来处理异常
			避免因为一个异常导致程序终止

## 异常的传播 (抛出异常)
	- 当在函数中出现异常时,如果在函数中对异常进行 了处理,则异常不会继续传播
		如果函数中没有对异常进行处理,则异常则会继续向函数的调用处传播
		如果函数调用处处理了异常,则不再传播,如果没有处理则继续向调用处传播
		直到传递到全局作用域(主模块) 如果依然没有处理,则程序终止,并且显示异常信息

	- 当程序运行过程中出现异常以后,所有的异常信息会被保存到一个专门的异常对象中,
		而异常传播时,实际上就是异常对象抛给了调用处
		比如:
			ZeroDivisionError 类的对象专门用来表示除0的异常
			NameError  		  类的对象专门用来处理变量错误的异常
			....
	- 在python为我们提供了多个异常对象

## 抛出异常
	- 可以使用raise 语句来抛出异常,
		raise语句后需要跟一个异常类 或 异常的实例

## 文件(file)
	- 通过python程序来对计算机中的各种文件进行增删改查的操作
	- I/O (Input/Output) 
	- 操作文件的步骤:
		① 打开文件
		② 对文件进行各种操作(读/写),改完保存
		③ 关闭文件

01.异常

# # print(a)  # NameError: name 'a' is not defined
# # print(10 / 0)  # ZeroDivisionError: division by zero
# print('hello')
# try:
#     # 有可能出现错误的代码
#     print(10 / 2)  # 10/0就出错
# except:
#     # 出错之后的处理方式
#     print('哈哈哈,出错了')
# else:
#     print('程序正常执行,没有错误')  # 5.0 程序正常执行,没有错误

# print('你好')

# print(10 / 0)


def fn():
    print('hello fn')
    print(10 / 0)
    # try:
    #     print(10 / 0)
    # except:
    #     pass


def fn2():
    print('hello fn2')
    fn()


def fn3():
    print('hello fn3')
    fn2()

# try:
#     fn()
# except:
#     pass


# fn3()

print(a)


# Traceback (most recent call last):
#   File "01.异常.py", line 42, in <module>
#     fn3()
#   File "01.异常.py", line 34, in fn3
#     fn2()
#   File "01.异常.py", line 29, in fn2
#     fn()
#   File "01.异常.py", line 20, in fn
#     print(10 / 0)
# ZeroDivisionError: division by zero

02.异常对象

print('异常出现前')
l = []
try:
    print(c)
    print(10 / 0)
    l[10]
    1 + 'hello'
except NameError:
    # 如果except后不跟任何内容,则此时他会捕获到所有的异常
    # 如果在except后跟着一个异常类型,那么此时他只会捕捉该类型的异常
    print('出现 NameError 异常')
except ZeroDivisionError:

    print('出现 ZeroDivisionError 异常')
except IndexError:
    print('出现 IndexError 异常')
except Exception as e:
    # exception是所有异常的父类,所以如果except后跟的是exception也会捕获到所有的异常
    # 可以在异常类跟着一个 as xx 此时xx就是异常对象
    print('出现未知异常', e, type(e)) # unsupported operand type(s) for +: 'int' and 'str' <class 'TypeError'>
finally:
    print('无论是否出现异常该子局都会使用')


print('异常出现后')

03.抛出异常

# 也可以自定义异常类,只需要创建一个类继承exception即可
class MyError(Exception):
    pass


def add(a, b):
    # 如果a和b中有负数,就像调用处抛出异常
    if a < 0 or b < 0:
        # raise 用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
        # 抛出异常的目的,告诉调用者这里调用时出现问题,希望你处理一下
        # 也可以通过if else 来替代参数的异常
        raise MyError('两个参数中不能有负数')
    r = a + b
    return r


print(add(-123, 456))

04.打开文件

# open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
# 使用open函数来打开一个文件
# 参数:
#   file 要打开的文件的名字(路径)
# 返回值:
#   返回一个对象,这个对象就代表了当前打开的文件

# 创建一个变量,来保存文件的名字
# 如果目标文件和当前文件在同一目录下,则直接使用文件名即可
# file_name = 'demo.txt'

# 在win系统下使用路径时,可以使用/来代替\
# 或者可以使用\\来代替\
# 或者也可以使用原市字符串 加r
# file_name = 'hello/demo.txt'

# 表示路径时,可以使用..来返回一级目录
# file_name = '../hello/demo.txt'

# 如果目标文件距离当前文件比较远,此时可以使用绝对路径
# 绝对路径应该从磁盘的根目录开始书写
file_name = 'D:/python练习/lesson_07/hello/demo.txt'

file_obj = open(file_name)  # 打开file_name对应的文件
print(file_obj)

05.关闭文件

# # 打开文件
file_name = 'demo.txt'

# # 调用open()来打开文件
# file_obj = open(file_name)


# # 当我们获取了文件对象以后,所有的对文件的操作都应该通过对象来进行
# # 读取文件中的内容
# # read()方法,用来对去文件中的内容,它会将全部内容保存为一个字符串返回
# content = file_obj.read()
# print(content)

# # 关闭文件
# # 调用close()来关闭文件
# file_obj.close()

# with ... as 语句

# with open(file_name) as file_obj:
#     # 代码块
#     # 在with语句中可以直接使用file_obj来做文件操作
#     # 此时这个文件只能在with中使用,一旦with'结束则文件会自动close()
#     print(file_obj.read())

file_name = 'demo.txt'
try:
    with open(file_name) as file_obj:
        print(file_obj.read())
except FileNotFoundError: 
    print(f'{file_name} 文件不存在')


# file_obj.read() #ValueError: I/O operation on closed file.

06.文件的读取

file_name = 'demo2.txt'


try:
    # 调用open()来打开一个文件,可以将文件分成两种类型
    # 一种,时纯文本文件(使用utf-8等编码编写的文本文件)
    # 一种, 是二进制文件(图片,Mp3,ppt等这些文件)
    # open()打开文件时,默认是以文本文件的形式打开的,但是open()默认的编码默认为None
    # 所以处理文本文件时,必须要指定文件的编码
    with open(file_name, encoding='utf-8') as file_obj:
        # 通过read()来读取文件
        # 如果直接调用read()它会将文本文件的所有内容全部都读取出来
        #   如果要读取的文件比较大的话,会一次性将文件的内容加载到内存中,容易导致内存泄露
        #   所以对较大的文件,不要直接调用read()
        # help(file_obj.read)
        # read()可以接收一个size作为参数,该参数用来指定要读取的字符的数量
        #   默认值-1,他会读取文件中所有的字符
        #   可以为size指定一个值,这样read()会读取指定数量的字符,
        #       每一次读取都是从上次读取到的位置开始读取的
        #       如果字符的数量小于size,则会读取剩余所有的
        #       如果已经读取到了文件的最后了,则会返回''空串
        content = file_obj.read(6)
        content = file_obj.read(6)
        print(content)  # UnicodeDecodeError: 'gbk' codec can't decode byte 0xa6 in position 4: illegal multibyte sequence
        print(len(content))
except FileNotFoundError:
    print(f'{file_name}不存在!')


# 读取大文件的方式


file_name = 'demo.txt'


try:
    with open(file_name, encoding='utf-8') as file_obj:
        # 定义一个变量,来保存文件的内容
        file_content = ''

        # 定义一个变量,来指定每次读取的大小
        chunk = 100
        # 创建一个循环来读取文件的内容
        while True:
            # 读取chunk大小的内容
            content = file_obj.read(chunk)

            # 检查是否读取到了内容
            if not content:
                # 内容读取完毕
                break

            # 输入内容
            # print(content,end='')
            file_content += content


except FileNotFoundError:
    print(f'{file_name}不存在!')

print(file_content)

07.文件的读取 readline

import pprint
file_name = 'demo.txt'

with open(file_name, encoding='utf-8') as file_obj:
    # readline()
    # 该方法可以读一行的内容
    # print(file_obj.readline(), end='')
    # print(file_obj.readline(), end='')

    # readlines()
    # 该方法用于一行一行的读取内容,他会一次性将读取到的内容封装到一个列表中返回
    # r = file_obj.readlines()
    # pprint.pprint(r[0])
    for t in file_obj:
        print(t)

08.文件的写入

file_name = 'demo5.txt'

# 使用open()打开文件时,必须要指定打开文件所要做的操作(读,写,追加)
# 如果不指定操作类型,则默认时读取文件,而读取文件时,是不能向文件中写入的
# r 表示只读的
# w 表示是可写的 ,使用w写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件
#       截断文件是指删除原来文件中的所有内容
# a 表示追加内容,,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
# x 用来新建文件,如果文件不存在则创建,存在则报错
# + 为操作符增加功能 
#   r+ 即可读又可写,如果文件不存在不会会创建文件
#   w+ 即可读又可写
#   a+ 即可读又可写
# with open(file_name, 'w', encoding='utf-8') as file_obj:
# with open(file_name, 'r+', encoding='utf-8') as file_obj:
with open(file_name, 'x', encoding='utf-8') as file_obj:
    # write()来向文件中写入内容
    # 如果操作的是一个文本文件的话,则write()需要传递一个字符串作为参数
    # 该方法可以分多次向文件中写入内容
    # 该方法完成以后,该方法会返回写入的字符的个数
    file_obj.write('hello hello \n')
    file_obj.write('hello hello \n')
    file_obj.write('hello hello \n')
    file_obj.write(str(123))
    r = file_obj.write(str(123) + '123123\n')
    r = file_obj.write('今天天气不错')
    print(r)
    # io.UnsupportedOperation: not writable

09.二进制文件

file_name = '不为谁而作的歌.flac'

# 读取模式
# t 读取文本文件(默认值)
# b 读取二进制文件

with open(file_name, 'rb') as file_obj:
    # 读取文本文件时,size是以字符为单位的
    # 读取二进制文件时,size是以字节为单位的
    # print(file_obj.read(100))

    # 将读取到的内容写出来
    # 定义一个新的文件
    new_name = 'aa.flac'
    with open(new_name, 'wb') as new_obj:
        # 定义每次读取的大小
        chunk = 1024 * 100
        while True:
            # 从已有的对象中读取数据
            content = file_obj.read(chunk)
            # 内容读取完毕后,结束循环
            if not content:
                break
            # 读取的数据写入到新对象中
            new_obj.write(content)

10.读取文件位置

#  seek读取二进制文件

# with open('demo.txt', 'rb') as file_obj:
#     # print(file_obj.read(100))
#     # print(file_obj.read(10))

#     # seek() 可以修改当前读取位置
#     file_obj.seek(55)
#     file_obj.seek(80, 0)
#     file_obj.seek(70, 1)
#     file_obj.seek(-10, 2) # b'o hello \r\n'
#     # seek()需要两个参数
#     #   第一个 是要切换到的位置
#     #   第二个 计算位置方式
#     #       可选值:
#     #           0 从头计算,默认值
#     #           1 从当前位置开始计算
#     #           2 从最后位置开始计算

#     print(file_obj.read())

#     # tell()用来查看当前读取到的位置
#     print('当前读取到-->', file_obj.tell())  # 当前读取到--> 110


# 读取文本文件

with open('demo2.txt', 'rt', encoding='utf-8') as file_obj:
    # print(file_obj.read(100))
    # print(file_obj.read(10))

    # seek() 可以修改当前读取位置
    file_obj.seek(2) # UnicodeDecodeError: 'utf-8' codec can't decode byte 0x84 in position 0: invalid start byte
    # 中文是三个字节一个字符 
    # seek()需要两个参数
    #   第一个 是要切换到的位置
    #   第二个 计算位置方式
    #       可选值:
    #           0 从头计算,默认值
    #           1 从当前位置开始计算
    #           2 从最后位置开始计算

    print(file_obj.read())

    # tell()用来查看当前读取到的位置
    print('当前读取到-->', file_obj.tell())  # 当前读取到--> 110

11.文件的其他操作

import os
from pprint import pprint


# os.listdir获取指定目录机构
# 需要一个路径作为参数,会获取到该路径下的目录结构,默认路径为 .  当前目录
# 该方法会返回一个列表,目录中的每一个文件(夹)的名字都是列表中的一个元素
r = os.listdir('c:')


# getcwd() 获取当前位置
r = os.getcwd()

# os.chdir()切换当前所在的目录 作用相当于cd
# os.chdir('c:')
# r = os.getcwd()

# 创建目录 os.mkdir()
# os.mkdir('aaa')

# 删除目录 os.rmdir()
# os.rmdir('aaa')

# 删除文件 os.remove()
open('aa.txt', 'w')
# os.remove('aa.flac')

# 重命名 os.rename()
# 可以对一个文件重命名,也可以用来移动一个文件
os.rename('aa.txt','bb.txt')

pprint(r)

阶段二 网络编程

跟之前连不到一块去,一脸懵逼

赞(1) 打赏
未经允许不得转载:南涧之滨 » 阿里云 Python教程

评论 2

评论前必须登录!

 

  1. #0

    lambda是干嘛用的

    geekfx9个月前 (12-22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏