学习python之前的了解的几个基本概念

1、表达式:一般仅仅用于一些计算结果。10+5   8-4

2、语句:在程序中语句一般需要完成某种功能。比如打印信息 print() ,获取信息 input(),变量赋值 a = 10 。

3、程序(program):由一条一条的语句和表达式构成。

4、函数(function):是一种语句,函数专门用来完成特定的功能 xxx()

函数的分类:
    内置函数
        由python解释器提供的函数,可以在python中直接使用
    自定义函数
        自主创建的函数

函数的作用:当我们需要完成某个功能时,就可以去调用内置函数,或者自定义函数

函数的两个要素:
    参数:()中的内容就是函数的参数
    返回值:函数返回的结果 max(4,5,6) 返回值是6 ,不是所有的函数都有返回值

语法:
    python中严格区分大小写;
    每一行就是一条语句
    每一行语句的长度不要过长,一条语句可以分行书写以 \ 结尾 然后换行书写

变量:可以用来保存字面量(字面的值),变量中保存的字面量是不定的,它会根据不同的字面量表示不同的意思。

变量和标识符

数据类型

变量的值的类型,也就是变量赋的哪些值,整数、浮点数、复数。
所有的整数类型都是int类型,如果数字的长度过大,可以使用下划线作为分隔符 c = 123_456_210
十进制的数字不能以0开头。

可以通过运算符对数字进行运算,并且可以保证整数运算的精确

浮点数:所有的小数都是浮点数类型(float)

对浮点数进行运算时可能会得到一个不精确的结果

c = 0.1 + 0.2
print(c)   #0.30000000000000004

保留1位小数

print('c = %.1f '%c)  # c = 0.3

字符串:

用来表示一段文本信息,字符串需要用引号引起来。

相同的引号之间不能嵌套,应该这样写

s = '子曰:“温故而知新可以为师傅" '

单引号和双引号不能跨行使用,如果要跨行需要这样写,使用三重引号来表示长字符,并且保留字符串的格式

s=‘’‘锄禾日当午,
汗滴禾下土,
谁知盘中餐,
粒粒皆辛苦。’‘’
print(s)

转义字符
    可以使用 \ 作为转义字符,通过转义字符可以在字符串中使用特殊的内容

s = "子曰:“温故而知新可以为师傅\""
print(s)  # 子曰:“温故而知新可以为师傅"

\t 表示制表符   s = "子曰:“温故而知新,\t可以为师傅\""
\n 表示换行符   s = "子曰:“温故而知新,\n可以为师傅\""

格式化字符串
    
字符串之间可以进行加法运算

s= 'abc'+'def'   # 字符串拼接,两个字符串加起来,输出结果为:abcdef
print("s = " + s)  # 注意:字符串不能和其他类型进行加法运算。 输出结果: s = abcdef
print("s =",s)    # 输出结果:s = abcdef
print("s= %s"%s)   # 输出结果:s= abcdef

在创建字符串时,在字符串中制定占位符
%s 表示任意字符串
s= 'hello %s'%'孙悟空' # s就是孙悟空
s1= 'hello %s,你好,我是%s'%('tom','孙悟空')    # hello tom,你好,我是孙悟空  占位符按顺序填充
s2= 'hello %3s'%'1234567890'   #  %3.5s 字符串长度限制在3-5之间,最少填充3个,最多5个,如果要填充的内容不够就补空格。

%f 浮点数占位符
s3= 'hello %.3f'%3.1415926537 # hello 3.142 表示保留3位小数

%d 整数占位符
s2= 'hello %.3d'%3.1415926537  # hello 003

格式化字符串
    可以通过在字符串前添加一个f来创建一个格式化字符串
在格式化字符串中可以直接嵌入变量

a = '孙悟空,'
b = 'tom'
c = f'hello {a} {b}'
print(f'c={c}')   # c=hello 孙悟空, tom

字符串复制
    字符串和数字相乘
a = 'abc'
#如果将字符串和数字相乘,则解释器会将字符串重复指定的次数并返回
a = a * 2
print(a) #abcabc

布尔值和空值
#布尔值(bool) True False
    布尔值主要用来做逻辑判断
#None(空值)
表示不存在
    
类型检查:
    通过类型检查指定值或者(变量)的类型

a = 123
b = '123'
print(type(a)) # <class 'int'>
print(type(b)) # <class 'str'>

对象

每个对象中都要保存三个
id (标识)
    标识对象的唯一性,每一个对象都有唯一的id,可通过id()函数查看对象的id
    对象一旦创建,则它的id永远不能再改
type(类型)
    类型用来表示当前对象所属的类型,比如:int float str bool,类型决定了对象有哪些功能

value
    对象中存储的具体值

变量和对象的关系
    变量,对象的别名。变量中存储的不是对象的值而是对象的id(内存地址)
    当我们使用时,在id查找对象
    a = 3 a存的并不是3,而是3的id

类型转换:不是转换对象本身类型,而是将对象的值转换为新的对象
    int() float() str() bool()
int()可以用来将其他的对象转换为整型,不会对原来的变量产生影响,如果要修改原来的变量需要对变量进行重新赋值
规则:
    布尔值  True -> 1  False -> 0
    浮点数 对于浮点数,直接去掉小数部分取整
    字符串 合法的整数字符串直接转换为对应的数字,如果不是合法的整数字符串则报错。
    对其他不可转换的类型对象,直接抛出异常

a = True
print('a = ', a)  #a =  True
print('a 的类型是',type(a))   # a 的类型是 <class 'bool'>

# 对变量进行重新赋值

a = int(a)
print('a = ', a)     # a =  1
print('a 的类型是',type(a))    # a 的类型是 <class 'int'>

浮点数

a = 11.5
a = int(a)
print('a = ',a,type(a))  # a =  11 <class 'int'>

字符串

a = '11'
a = int(a)
print('a = ',a,type(a))  # a =  11 <class 'int'>

不是合法的整数字符串则报错

a = '11.5'
a = int(a)
print('a = ',a,type(a))  #  ValueError: invalid literal for int() with base 10: '11.5'

对其他不可转换的类型对象,直接抛出异常
a = None
a = int(a)
print('a = ',a,type(a))  # TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

bool() 任何对象都可以转换为布尔值
规则:对于所有表示空性的对象都会转换成False,其余的都会转换成True
a = ''
a = bool(a)
print(a,type(a))  # False <class 'bool'>

表示空性的: 0 、 None、 ''等等

可变对象:
    a = [1,2,3]
    a[0] = 10 (改对象)
    这个操作是在通过变量去修改对象的值,这种操作不会改变变量所指向的对象

a = [10,2,3] (改变量)
    这个操作是在给变量重新赋值,这种操作会改变变量所指向的对象。

a = [1, 2, 3]
        print('修改前:',a,id(a)) # 修改前: [1, 2, 3] 1839173004800
    # 通过索引修改
        a[0] = 10
        print('修改后:',a,id(a)) # 修改后: [10, 2, 3] 1839173004800
    # 为变量重新赋值
        a = [10,2,3]
        print('修改后:',a,id(a)) # 修改后: [10, 2, 3] 1839173005248

# 当我们去修改对象时,如果有其他变量也指向了该对象,则也修改了其他变量

a = [1, 2, 3]
b = a
print(a,id(a),b,id(b)) # [1, 2, 3] 2807470878208 [1, 2, 3] 2807470878208
b[0] = 10
print('修改后:',a,id(a),b,id(b)) # 修改后: [10, 2, 3] 2807470878208 [10, 2,3]2807470878208

# 当我们为一个变量重新赋值时都不会影响其他变量

a = [1, 2, 3]
b = a
print(a,id(a),b,id(b)) # [1, 2, 3] 2096425594368 [1, 2, 3] 2096425594368
# b[0] = 10
b = [10,2,3]
print('修改后:',a,id(a),b,id(b)) # 修改后: [1, 2, 3] 2096425594368 [10, 2, 3] 2096425594816

运算符

对一个或多个值进行运算或各种操作,比如 + - =
运算符的分类:
    算数运算符
    赋值运算符
    比较运算符(关系运算符)
    逻辑运算符
    条件运算符(三元运算符)

算数运算符:+ - * / , 整除 //
    如果是两个字符串之间进行加法运算,则会进行拼串操作   a = 'hello' + 'world'

整除
a = 10 // 3
print('a = ',a)   # a =  3

求16的平方根
a = 16 ** 0.5
print('a = ',a)  # a =  4.0

% 取模  求两个数相除的余数

a = 10 % 5
print('a = ',a)  # a =  0
a = 10 % 4
print('a = ',a)   # a =  2

关系运算符
    比较两个值之间的关系,总会返回一个布尔值,如果关系成立,返回 True,否则返回 False
> 、>= 、 < 、 <= 、 == 、 !=

在 Python 中可以对两个字符串进行大于等于或小于等于的运算,比较的是字符串的 Unicode 编码
result = 'a' > 'b'
print(result)  # False

比较两个字符串的Unicode编码是是逐比较。可利用该特性对字符串进行排序,但是对于中文来说意义不大。
result = 'ab' > 'b'
print(result)  # False

如果不希望比较两个字符串的Unicode编码,则需要将其转换为数字再比较
print(int('2') > int('11'))  # False

相等和不等比较的是对象的值

result = 1 == True
print(result)     #True
print(type(1),type(True))    # <class 'int'> <class 'bool'>
print(id(1),id(True))     # 140708035823280 140708035544912

is 、 is not 比较两个对象是否是同一个对象,比较的是对象的 id
result = 1 is True
print(result)  # False
result = 1 is not True
print(result)  # True

a = 10 < 20 > 15
print(a) # True

逻辑运算符
    not and or 非 与 或  主要用来做一些逻辑判断
not对右侧的值进行非运算
    对于布尔值,非运算会对其进行取反操作,对于非布尔值,非运算会将其转换成布尔值,然后再取反
a = True
a = not a # 对 a 进行非运算
print('a = ',a)  # a =  False

a = 1
a = not a # 对 a 进行非运算
print('a = ',a)  # a =  False

and 可以对符号两侧的值进行与运算
    只有在符号两侧的值都为 True 时,才会返回 True ,只要有一个 False 就会返回 False
    与运算找False,Python 中的与运算时短路与,如果第一个值为False则不在看第二个值。
result = True and True
print(result)  # True

与运算找False,Python 中的与运算时短路与,如果第一个值为False则不在看第二个值。
    第一个值是True会看第二个值
True and print('你猜我出来吗')  # 你猜我出来吗
result = False and print('你猜我出来吗') #

or 符号两侧进行或运算
    两个值中只要有一个 True 就为 True
    或运算是找 True ,如果第一个值为 True,则不再看第二个。

result = True or False
print(result)  # True

result = False or False
print(result)  # False

或运算是找 True ,如果第一个值为 True,则不再看第二个。
True or print('你猜我出来吗')  #
False or print('你猜我出来吗') # 你猜我出来吗

练习:对不同的布尔值进行3种逻辑运算,然后尝试对非布尔值进行3种逻辑运算,并观察返回结果。

非布尔值的与或运算
    非布尔值与或运算时,Python会将其当作布尔值运算,最终会返回原值

#True and True
result = 1 and 3
print(result)  # 3    与运算找False,第一个值是True于是去看第二个值,也是True 但是只能返回第二个值了

# False and True
result = 0 and 3
print(result)    # 0

# False and False
result = 0 and None
print(result)    # 0

或运算是找 True ,如果第一个值为 True,则不再看第二个。否则返回第二个。

result = 2 or 1
print(result)    # 2

result = 0 or 1
print(result)   # 1

条件运算符
语法:
    语句1  if 条件表达式 else 语句2
执行流程:
    条件是运算符在执行时,先对条件表达式求值判断,
        如果判断结果为True,则执行语句1,并返回执行结果
        如果判断结果为False,则执行语句2,并返回执行结果

a = 10
b = 20
print('a大') if a > b else print('b大')
# 获取较大值
max = a if a > b else b
print(max)

练习:a b c三个变量,通过条件运算符获取三个值中的较大值

a = 10
b = 20
c = 30
max = a if a > b else b
max = max if max > c else c
print(max)max = a if a > b and a > c else b if b > c else c #不方便阅读
print(max)

运算符的优先级
    和数学中一样,Python中,也是先乘除后加减

and or
先算and 再算or

a = 1 or 2 and 3
print(a) # 1

== 、 = 、 is 、 is not 、 !=
== 、!= 比较的是对象的值是否相等,
is 、is not 比较的id是否相等,比较两个对象是否是同一个对象

    a = [1,2,3]b = [1,2,3]print(a == b) # Trueprint(a is b)  # a 和 b 不是同一个对象,因此返回 False

控制流

流程控制语句
    改变程序的执行顺序,也可以让指定的程序反复执行多次。

条件判断语句,循环语句

条件判断语句(if 语句)
    if 条件表达式 : 语句
在执行时,先对条件表达式进行求职判断,如果为 True,则执行 if 后的语句

if True : print('你猜我出来吗')     # 你猜我出来吗

如果希望if可以控制多个语句,则可以增加代码块

number = 20
if number > 10 :print('number 比10大')   # number 比10大print('谁也管不了我')
else :print('number 比10小')print('你比较厉害')

可以使用逻辑运算符来连接多个条件,同时满足用 and ,只希望满足一个条件即可则使用 or

number = int(input('请输入一个数:'))
if number > 10 and number < 20:print('number 比10大,比20小')   # number number 比10大,比20小

练习1

while True:try:number = int(input('请输入一个整数:'))if number >=10 and number <= 20:print('number 在10和20之间')break              #break语句作用:跳出for 和 while循环,注意:只能跳出距离它最近的那一层循环except ValueError as e:print("您输入有误!请按照提示重新输入!")

练习2:
让用户输入一个用户名,获取用户输入,并进行判断,如果用户输入的用户名为admin,则显示欢迎管理员登录
如果用户输入发是其他的用户名则什么也不做。

user_name = input('请输入用户名:')
if user_name == 'admin':print('欢迎管理员登录!')

input()函数
    调用后,程序会立即暂停,等待用户输入,用户输入后以返回值(字符串)形式返回,
a = input('请输入内容:')
print('用户输入的是:‘,a)

也可以用以暂时阻止程序结束,例如:
print('hello')
input('按回车键退出。。。')

练习3:
让用户在控制台输入一个年龄,判断成年了没有

age = int(input('请输入你的年龄:'))
# input()返回值是一个字符串,必须转换成int类型才能比较
if age >= 18:print("你已经成年了")

if-elif-else 语句   只会有一个代码块会被执行。
语法:
    if 条件表达式:
        代码块
    elif 条件表达式:
        代码块
    elif 条件表达式:
        代码块
    else:
        代码块

# 如果表达式的结果为True,则执行当前代码块,然后语句结束,

age = int(input('请输入你的年龄:'))
if age > 200:print('活着可真没劲呢!')
elif age > 100:print('你也是老大不小了!')
elif age >= 60:print('你已经退休了!')
elif age >= 30:print('你已经是中年了')
elif age >= 18:print('你已经成年了!')
else:print('你还是个小孩!')

练习4
获取用户输入的整数,判断这个数是奇数还是偶数

while True:try:number = int(input('请输入一个整数:'))if number == 0:print('既不是奇数也不是偶数!')breakelif number%2 == 0:print(number,'是一个偶数!')breakelse:print(number,'是一个奇数!')breakexcept ValueError as e:print('你输入有误,请重新输入!')

练习5
检查任意一个年份是否是闰年,能够被4整除不能被100整除,或者可一个被400整除,这个年份就是闰年

while True:try:number = int(input('请输入一个年份:'))if number == 0:print('不能输入0')elif number%4 == 0 and number%100 != 0 or number%400 == 0:print(number,'是闰年!')breakelse:print(number,'是平年!')breakexcept ValueError as e:print('你输入有误,请重新输入!')

练习6
狗5岁了,相当于人类多少岁?
狗的前两年每一年相当于人类的10.5岁,然后每增加一年就增加四岁。5岁的狗相当于 10.5+10.5+4+4+4 = 33岁
用户输入狗的年龄,显示相当于人类的年龄狗多少岁了?如果是负数则提醒输入有误

# 当用户输入有误时,需要提示错误并且重新输入,所以此处需要一个循环,直到用户输入正确,执行相应语句,然后 break 跳出while循环程序结束

while True:try:number = float(input('请输入狗狗的年龄:'))  # 将输入的内容转换为浮点数类型,才可以输入小数。if number < 0:print('您输入的是负数')elif number <= 2 :age = number * 10.5print('狗狗的年龄相当于人年龄的:%.1f岁'% age )    # %.1f  保留小数点后一位breakelse:age = (number-2)*4 + 21print('狗狗的年龄相当于人年龄的:%.1f岁'% age )breakexcept ValueError as e:               #当输入的是字符串不是数字类型时,抛出错误print('你输入有误,请重新输入!')
while True:try:dog_age = float(input('请输入狗狗的年龄:'))like_person_age = 0if dog_age > 0:if dog_age <= 2 :like_person_age = dog_age * 10.5else:like_person_age = 2*10.5like_person_age += (dog_age-2)*4print(dog_age,'岁的狗相当于',like_person_age,'岁的人')breakelse:print('您输入不合法!')except ValueError as e:               #当输入的是字符串不是数字类型时,抛出错误print('你输入有误,请重新输入!')

练习7
从键盘输入小明的期末成绩:
当成绩为100时,奖励一辆BMW
当成绩是【80-99】时,奖励一台iphone
当成绩是【60-79】时,奖励一本参考书
其他时,什么奖励也没有

while True:try:number = int(input('请输入成绩:'))# 打印分割线print('='*40)if  number > 100 or number < 0:print('输入超出范围,重新输入!')elif number == 100:print('成绩优异!你将获得一辆BMW')breakelif 80 <= number:print('成绩不错!你将获得一台iphone' )breakelif 60 <= number:print('成绩一般!需要努力,奖励一本参考书')breakelse:print('成绩不合格!什么也没有')breakexcept ValueError as e:print('你输入有误,请重新输入!')

练习8
满足3个条件就嫁给他:1、身高超过180cm;2、超过1000万资产;3、颜值高于500分
满足其中一个,还将就,一个都不满足,不嫁

while True:try:high = int(input('请输入你的身高:'))money = int(input('请输入你的财富值:'))look = int(input('请评估自己的颜值分:'))if  high >= 180 and money >= 1000 and look >= 500:print('我一定嫁给他')breakelif high < 180 and money < 1000 and look < 500:print('我不嫁给他')breakelse:print('也不是一定不行,考虑一下吧!')breakexcept ValueError as e:print('你输入有误,请重新输入!')

fou ...else

for line in open('practice-for-else.txt'):if 'cat' in line:print(line)break
else:print('there is not cat,please search again')

#  practice-for-else.txt 这是一个练习用的自己随便编写的一些内容,文本文件需要和python放在同一个目录。

循环语句

while循环、for循环

while 循环
    while 条件表达式:
        代码块

先对while后的条件表达式进行求值判断
    如果判断结果为True,则执行循环体(代码块),循环体执行完毕,继续对条件表达式进行求值判断,
    如果判断结果仍为True,则执行循环体(代码块),直到判断结果为False,循环终止。如果循环有对应的else
    ,则执行else代码块。

循环的3个要件
初始化表达式,通过初始化表达式初始化一个变量
条件表达式:用来设置循环执行的条件
更新表达式:修改变量的值

i = 0           #  初始化表达式,通过初始化表达式初始化一个变量
while i < 10:   #  条件表达式:用来设置循环执行的条件
    i += 1      #  更新表达式:修改变量的值
    print('hello')

练习1
求100以内奇数的和

i = 1
sum = 0
while i < 100:sum += ii += 2
print(sum)

法二:

i = 0
sum = 0
while i < 100:i += 1# 判断是否是奇数if i % 2 != 0:sum += i
print(sum)

练习2
求100以内所有7的倍数和,以及个数

i = 0
sum = 0
while 7*i < 100:sum = sum + 7*ii += 1
print(i)
print(sum)

法二:

i = 7
sum = 0
# 创建一个计数的变量
count = 0
while i < 100:count += 1sum += ii += 7
print('总和是',sum,'总共次数是',count)

练习3
水仙花数是指一个 n 位数(n >= 3),它的每个位上的数字的n次幂之和等于它本身(如: 1**3 + 5**3 + 3**3 = 157)
求1000以内所有的水仙花数

a = 100
e = 0
while a < 1000:b = a % 10             # 个位数c = a // 10 % 10       # 十位数d = a // 100           # 百位数if b ** 3 + c ** 3 + d ** 3 == a:e += 1print(a)a += 1
print(e)

练习4
获取用户输入的任意数,判断是否是质数

num= int(input('输入一个任意的大于1的整数'))
# 要获取到所有可能整除num的整数。来判断有没有它的因数,全部给除一遍。
i = 2
flag = True
while i < num:# 判断num能否被i整除# 如果num能i整除,则说明num一定不是质数if num % i == 0:flag = Falseprint(num)i += 1if flag :print(num,'是质数')
else:print(num,'不是质数')

循环嵌套
外层循环每执行一次,内层循环就要执行一圈例如

i = 0
while i < 5:# 创建内层循环j = 0while j < 5:print('*',end = '')  #  end = '' 不换行j += 1print()i += 1

外层执行了5次,内层执行了25次

i = 0
while i < 5:
    # 创建内层循环
    j = 0
    while j < i + 1:
        print('*',end = '')  #  end = '' 不换行
        j += 1
    print()
    i += 1

i = 0
while i < 5:
    # 创建内层循环
    j = 5
    while j > i :
        print('*',end = '')  #  end = '' 不换行
        j -= 1
    print()
    i += 1

练习1
打印99乘法表

b  = 1
while b < 10:# 创建内层循环a = 1while a <= b :i = a * bprint(a,'*',b,'=',i,end='  ')a += 1print()b += 1

法二:

i = 0
while i < 9:i += 1j = 0while j < i:j += 1print(f'{j}*{j}={i*j} ',end='')print()

练习2
求100以内所有的质数

num = 2
while num < 100:flag = True   # 创建一个变量,记录num的状态,默认是质数。i = 2# 判断num能否被i整除# 如果num能i整除,则说明num一定不是质数while i < num:if num % i == 0:flag = False     # 如果 i 不能被 j 整除,修改为 Falsei += 1if flag:print(num,end=' ')num += 1

优化:
# 原始 9.2 秒
# 第一次优化 1.22 秒
# 第二次优化 0.07 秒
from time import *
# time() 函数用来获取当前的时间,返回单位是秒
begin = time()
num = 2
while num < 10000:
    flag = True
    i = 2
    while i <= num ** 0.5:  # 第二次优化点,时间 0.07 ,对 num 进行了开方,因数的出现是一对一对的。没有必要对因数重复判断例如 3*6=18,6*3=18;2*9=18,9*2=18
        if num % i == 0:
            flag = False
            # 一旦进入判断,则证明 num 一定不是质数,此时内层循环没有继续执行的必要,例如 8%2=0,则就没有必要再去对3、4、5、6、7继续取模判断了。
            break  # 第一次优化点,时间 1 .22秒
        i += 1
    if flag:
        print(num)
    num += 1
end = time()
print()
print('程序花费了',end-begin,'秒')

break
    用来立即跳出循环语句(包括 else)

num = 0
while num < 10:
        if num == 4:
            break
        print(num)
        num += 1
else:
    print('结束了')

continue 可以用来跳过当次循环

num = 0
while num < 5:
        num += 1
        if num == 4:
            continue
        print(num)
else:
    print('结束了')

range() 可以用来生成一个自然数序列的函数
    r = range(5)
    print(list(r))  # [0, 1, 2, 3, 4]
# 该函数需要三个参数:起始位置(默认位置是0),结束位置,步长(默认是1)
# 通过 range 可以创建一个执行指定次数的 for 循环
for i in range(10):
    print(i,end='')
#  for 循环除了创建方式以外,其余的都和 while 一样

综合练习:唐僧大战白骨精

# 显示提示信息
print('='*20,'欢迎光临《唐僧大战白骨精》','='*20)
# 选择你的身份:
print('请选择你的身份:')
print('\t 1、唐僧')
print('\t 2、白骨精')
# 提示用户选择,并获取用户的选择
player = input('请选择【1 - 2】:')
# 打印分隔线
print('-'*64)

# 根据用户的选择显示提示不同信息
if player == '1':
    print('你已经选择了1,你将以<唐僧>的身份来进行游戏')
elif player == '2':
    print('你竟然选择了白骨精,太不要脸了,系统将自动分配身份')
else:
    print('你的输入有误,系统将自动分配身份')

# 根据用户的选择分配身份:(显示不同的提示消息)
# 创建变量保存玩家和Boss的生命值和攻击力
player_life = 20      # 生命值
player_attack = 2    # 攻击力

Boss_life = 50
Boss_attack = 10
# 游戏进行:
# 显示玩家的基本信息(攻击力,生命值)
print(f'你的身份是唐僧,你的生命值是 {player_life},攻击力是 {player_attack}')
# 显示玩家可以进行的操作:练级、打Boss、逃跑
# 打印分隔线
print('-'*64)

# 由于游戏选项需要反复显示,所以必须要在循环中
while True:
    print('请选择你的操作:')
    print('\t 1、练级')
    print('\t 2、打Boss')
    print('\t 3、逃跑')
    game_choose = input('请选择你要进行的操作【1-3】')
# 练级:提升玩家的攻击力和生命值
    if game_choose == '1':
        # 增加玩家的生命和攻击
        player_life += 5
        player_attack += 2
        print('-'*64)
        print(f'唐僧,恭喜你升级了!你的生命值是 {player_life},攻击力是 {player_attack}')
# 打Boss:玩家对Boss进行攻击,减去Bosss生命值,减去的生命值应该等于玩家的攻击力
    elif game_choose == '2':
        Boss_life -= player_attack
        print('-'*64)
        print('<唐僧>攻击了<白骨精>')
        # 检查Boss是否死亡
        if Boss_life <= 0:
            # Boss死亡,玩家胜利,游戏结束
            print(f'<白骨精>收到了{player_attack}点伤害,重伤死了,<唐僧赢得了胜利!>')
            break
# Boss对玩家进行反击,减少玩家生命值
        player_life -= Boss_attack
        print('-'*64)
        print('<白骨精>攻击了<唐僧>')
        if player_life <= 0:
            print(f'你受到了{Boss_attack}点伤害,重伤死了!')

print('='*20,'GAME OVER!','='*20)
            break
    elif game_choose == '3':
        print('-' * 64)
        print('唐僧一扭头,撒腿就跑,游戏结束!')
        break
    else:
        print('-' * 64)
        print('你的输入有误,请重新输入!')

列表

列表(list)
    python 中的一个对象,列表可以用来保存多个有序的数据

1、创建列表

my_list = [] # 创建了一个空列表
    print(type(my_list))
    # 创建了一个包含了5个元素的列表
        my_list = [10,20,30,40,50]
    # 列表中可以存储任何对象
        my_list = [10,'hello',True,None,[1,2,3],print]
    # 列表中的对象按照插入顺序保存
        my_list = [10,20,30,40,50,60]
    # 通过索引(index)来获取列表中的元素,索引是元素在列表中的位置,索引是从0开始的整数
    # 通过索引(index)来获取列表中的元素
        print(my_list[0])
    # 如果使用的索引超过范围,抛出错误:IndexError: list index out of range
    # 获取列表中元素的长度,获取到的长度的值是列表的最大索引+1
        print(len(my_list))

练习1:
        创建一个列表,在列表中保存最好的5个朋友的名字,然后通过索引来获取朋友的名字。
        my_friend = ['A','B','C','D','E']
        print(my_friend[0])
        print(my_friend[1])
        ...
        print(my_friend[4])

切片
        切片指在现有列表中,获取一个子列表
        列表的索引可以是负数,如果索引是负数,则从列表的后往前获取。
        通过切片获取指定的元素:
            列表[起始:结束]
                my_list = [10,20,30,40,50,60]
                print(my_list[0:2])  # 获取到的元素包括起始位置,不包括结束位置元素
                # 切片操作返回的是一个新的列表
                如果省略结束位置,会一直截取到最后
                    print(my_list[0:])
                如果省略开始位置,会从第一个元素开始截取
                    print(my_list[:3])
                如果开始和结束位置全部省略,则相当于创建了一个列表的副本
                    print(my_list[:])
            列表[起始:结束:步长] 步长:每次获取元素的间隔,默认为 1
                print(my_list[0:6:2])
                步长不能是0,可以是负数(从列表后向前取)

2、操作列表中的数据
    +  将两个列表拼接为一个列表
        list1 = [10,20,30]
        list2 = [10,50,60]
        my_list = list1 + list2
        print(my_list)
    *  将列表复制指定次数
        list1 = [10,20,30]
        my_list = list1 * 3
        print(my_list)
    in 用来检查指定元素是否存在于列表中
        print(50 in my_list)
    not in 用来检查指定元素是否不在于列表中
        print(50 not in my_list)
    len() 获取列表长度
        print(len(my_list))
    max() 获取列表最大值
        print(max(my_list))
    min() 获取列表最小值
        print(min(my_list))
    方法:和函数基本一样,通过 对象.方法() 的形式调用
        s.index() 获取指定元素在列表中第一次出现的位置索引
            print(my_list.index(50))
            第二个参数:表示查找的起始位置
                print(my_list.index(50,4))
            第三个参数:表示查找的结束位置
                print(my_list.index(50,4,6))
        s.count() 统计指定元素在列表中出现的次数
            print(my_list.count(50))

序列:最基本的一种数据结构,用于保存一组有序的数据,所有的数据在序列当中都有一个唯一的位置(索引),
     序列的顺序会按照添加的顺序存储。
     分类:序列中的元素是否可以改变
        可变序列:列表(list)
        不可变序列:字符串(string),元组(tuple)
    可变序列修改元素
        创建一个列表:
            list1 = ['孙悟空',‘猪八戒’,'沙和尚','唐僧','蜘蛛精','白骨精']
                # 修改沙和尚为沙悟净
                    # list1[2] = '沙悟净'
                    # print(list1)
                # 删除索引为2的元素
                    # del list1[2]
                    # print(list1)
                # 通过切片修改列表,使用新的替换原来的
                    # list1[0:2] = ['牛魔王','红孩儿','二郎神']
                    # print(list1)  # ['牛魔王', '红孩儿', '二郎神', '沙和尚', '唐僧', '蜘蛛精', '白骨精']
                # 向索引为0的位置插入元素
                    # list1[0:0] = ['牛魔王']
                    # print(list1)  # ['牛魔王', '孙悟空', '猪八戒', '沙和尚', '唐僧', '蜘蛛精', '白骨精']
                # 当设置了步长时。序列中元素的个数必须和切片中元素的个数一致
                    # list1[::2] = ['牛魔王','红孩儿','二郎神']
                    # print(list1) # ['牛魔王', '猪八戒', '红孩儿', '唐僧', '二郎神', '白骨精']
                # 删除列表元素
                    # del list1[0:2]
                    # print(list1) # ['沙和尚', '唐僧', '蜘蛛精', '白骨精']
                    # list1[1:3] = []
                    # print(list1) # ['孙悟空', '唐僧', '蜘蛛精', '白骨精']
    不可变序列修改元素时可通过list()函数将其转换成列表类型之后再就行修改。

操作列表基本方法:

创建一个列表:
    list1 = ['孙悟空',‘猪八戒’,'沙和尚']
    # s.append(x) 向列表最后添加元素
        # list1.append('唐僧')

# s.insert(i,x) 向列表的指定位置添加元素
        # list1.insert(2,'唐僧')

# extend(t) 使用新的序列来扩展当前的序列,需要一个序列作为参数,将该序列中的元素添加到当前列表
        # list1.extend(['唐僧','白骨精'])
        # list1 += ['唐僧','白骨精']

# clear() 清空序列
        # list1.clear()

# pop() 根据索引删除,并返回被删除元素
        # result = list1.pop(2)
        # 删除最后一个元素
        # result = list1.pop()
        # print(result)

# remove() 删除指定值的元素,如果相同值的元素有多个,只会删除一个
        # list1.remove('猪八戒')

# reverse() 反转列表
        # list1.reverse()

# sort() 用来对列表中的元素进行排序,默认是升序
        # my_list = [5,9,3,8,-2,4,0]
        # my_list.sort()
        # 降序排列
        # my_list.sort(reverse=True)
        # print(my_list)

遍历列表:将列表中的所有元素取出来
    创建一个列表:
        list1 = ['孙悟空','猪八戒', '沙和尚','唐僧']
    # while 创建一个循环,来遍历
        # i = 0
        # while i < len(list1):
        #     print(list1[i])
        #     i += 1
    # for 循环遍历
        # 语法:
        #   for 变量 in 序列:
        #       代码块
        # 每执行一次就会将序列中的一个元素赋值给变量
            # for i in list1:
            #     print(i)

# 练习1:将去取出的变量保存到另外一个列表
        # list2 = []
        # for i in list1:
        #     list2.append(i)
        # print(list2)

练习2:EMS员工管理系统
做一个命令行版本的员工管理系统
# 创建列表保存员工信息
Employee_information = ['孙悟空\t刺客\t花果山','猪八戒\t坦克\t高老庄','沙和尚\t无\t流沙河']
# 欢迎使用员工管理系统
print('='*20,'欢迎使用员工管理系统','='*20)
# 请选择要做的操作
while True:
    print(' 请选择要做的操作: ')
    print('\t 1、查询员工')
    print('\t 2、添加员工')
    print('\t 3、删除员工')
    print('\t 4、退出系统')
    print('-'*60)
    # 1、查询员工
    # 显示当前系统的所有员工
    Operation = input('请输入你的选择【1-4】:')
    print('-'*60)
    if Operation == '1':
        # 打印表头
        print('\t序号\t姓名\t职业\t住址')
        # 创建一个变量保存员工的序号
        n = 1
        for Ei in Employee_information:
            print(f'\t{n}\t{Ei}')
            n += 1
    # 2、添加员工
    elif Operation == '2':
        name = input('请输入员工姓名:')
        job = input('请输入员工职业:')
        address = input('请输入员工住址:')
        # 创建员工信息
        infor = f'{name}\t{job}\t{address}'
        # 显示是否要添加
        # print('员工:',infor,'将会添加')
        print('一下员工将会被添加')
        print('姓名\t职业\t住址')
        print(infor)
        user_con = input('是否确认操作[Y/N]')
        if user_con == 'Y' or user_con == 'y':
            Employee_information.append(f'{name}\t{job}\t{address}')
            print('添加成功')
        else:
            print('添加取消')
            pass
    # 输入员工姓名、年龄、住址,添加到系统中
    # 3、删除员工,根据序号删除
    elif Operation == '3':
        # 获取要删除员工的序号
        del_num = int(input('请输入要删除员工序号:'))
        # 判断序号是否有效
        if 0 < del_num <= len(Employee_information):
            # 根据序号获取索引
            del_index = del_num - 1
            # 显示提示
            print('一下员工将会被删除')
            print('\t序号\t姓名\t职业\t住址')
            print(f'\t{del_num}\t{Employee_information[del_index]}')
            user_con = input('是否确认操作[Y/N]')
            if user_con == 'Y' or user_con == 'y':
                Employee_information.pop(del_index)
                print('删除成功')
            else:
                print('删除取消')
                pass
        else:
            print('输入有误!')
    # 将员工从系统中删除
    # 4、退出系统
    elif Operation == '4':
        input('点击回车键退出!')
        break
    else:
        print('你输入有误,重新选择!')
    print('='*60)

元组

元组 tuple 不可变的序列,操作方式基本和列表一致(导致对象改变的,在元组中不能使用)。当希望数据不改变时,使用元组。
    创建元组,使用 () 创建元组
        # my_tuple = ()
        # print(type(my_tuple)) # <class 'tuple'>
    # 创建了一个五个元素的元组
        # my_tuple = (1,2,3,4,5)
        # print(my_tuple[3])
    # 不能对元组中的元素重新赋值
        # my_tuple[3] = 10
        # print(my_tuple[3])   #  TypeError: 'tuple' object does not support item assignment
    # 当元组不是空元组时,括号可以省略
        # my_tuple = 10,20,30,40
        # print(type(my_tuple)) # <class 'tuple'>
    # 如果元组不是空元组,至少要有一个逗号
        # my_tuple = 10,
        # print(type(my_tuple)) # <class 'tuple'>
    # 元组解包:将元组中的每一个元素都赋值给一个变量
        # my_tuple = 10,20,30,40
        # a,b,c,d = my_tuple
        # print('a=',a)
        # print('b=',b)
        # print('c=',c)
        # print('d=',d)
    # 交换两个变量的值
        # a = 100
        # b = 300
        # print(a,b)
        # a,b = b,a
        # print(a,b)
    # 在对一个元组进行解包时,变量的数量必须和元组的元素数量一致
        # my_tuple = 10,20,30,40
        # a,b = my_tuple
        # print(a,b)  # ValueError: too many values to unpack (expected 2)
    # 也可以在变量前边加一个*,这样变量将会获取元组中剩余的元素
        # my_tuple = 10,20,30,40
        # a,b ,*c= my_tuple
        # a,*b,c = my_tuple
        # *a,b,c = my_tuple
        # print(*a)
    # 不能同时出现两个或以上的 * 变量

字典

字典(dict),一种数据结构,成为映射,和列表类似,都是用来存储对象的容器
    # 列表的查询性能很差,在字典中,每一个元素都有唯一的名字,通过唯一的名字可以快速查找到指定的元素
    # 字典中可以保存多个对象,每一个对象都会有一个唯一的名字,这个唯一的名字称为 键(key),通过 key 可以快速查询 value
    # 这个对象称其为 值(value),所以字典我们也叫键值对(key-value)结构,每一个键值对就是:项(item)
# 创建一个字典
    # d = {}
    # print(d,type(d)) # {} <class 'dict'>
# 语法
    # {key:vlaue,key:vlaue,key:vlaue}
# 字典的值(value)可以时任意对象,字典的键(key)可以是任意的不可变对象(int、str、bool、tuple...)
    d = {'name':'孙悟空','age':'18','gender':'男'}
# 字典的键(key)是不能重复的,如果出现了重复的,后面的会替换掉前面的
    # d = {'name':'孙悟空','age':'18','gender':'男','name':'sunwukong'}
    # print(d) # {'name': 'sunwukong', 'age': '18', 'gender': '男'}
# 需要根据键来获取值
    # print(d['name'],d['age'],d['gender'])
# 如果使用了字典中不存在的键,会报错
    # print(d['hello']) # KeyError: 'hello'

字典的官方文档

字典的使用
# 使用dict()函数来创建字典,,每一个参数都是一个键值对,这种方式创建的字典key都是字符串
    # d = dict(name = '孙悟空',age = 18,gender = '男')
# 也可以将一个包含双值序列的序列转换为字典。双值序列,序列中只有两个值,[1,2]、(a,3)
# 子序列,如果序列中的元素也是序列,那么我们称这个元素为子序列 [(1,2),(3,5)]
    # d = dict([('name','孙悟空'),('age',18)])
    # print(d,type(d))  # {'name': '孙悟空', 'age': 18} <class 'dict'>
# len()获取字典中键值对的个数
    # print(len(d))
# in、not in 检查字典中是否包含指定的键
    # print('hello' in d)
# 获取字典中的值,根据键来获取
    # 语法:d[key]
        # print(d['name'])
        # 通过[]来获取值时,如果键不存在会抛出异常 KerError
# get(key,default) 该方法用来根据键来获取字典中的值,如果获取的键在字典中不存在,会返回None
    # print(d.get('hello'))  # None
# 也可以指定一个默认值,来作为第二个参数,这样获取不到值时将会返回默认值
    # print(d.get('hello','默认值')) # 默认值
# 修改字典
    # dict[key] = value  如果key存在则修改,不存在则添加
    # d['name'] = '猪八戒'
    # d['address'] = '花果山'

# setdefault(key,default),如果key存在于字典中,则返回key的值,不会对字典进行任何操作。如果key不存在,则向字典中添加这个key,并设置value
    # d.setdefault('name','猪八戒')
    # result = d.setdefault('name','猪八戒')
    # print(result) # 孙悟空
    # d.setdefault('hello','猪八戒')
    # print(d) # {'name': '孙悟空', 'age': 18, 'hello': '猪八戒'}
# update[other] 将字典中的key-value添加到当前字典中, 如果有重复的key,后面的会替换前面的
    # d = {'a':1,'b':2,'c':3}
    # d2 = {'d':4,'e':5,'f':6,'a':7}
    # d.update(d2)
    # print(d)  # {'a': 7, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}

# 删除,可以使用del删除字典中的key、value
    # del d['a']
# popitem() 随机删除字典中的一个键值对。一般都会删除最后一个键值对,删除之后会将删除的key-value作为返回值返回,
# 返回的是一个元组,第一个元素是删除的key,第二个是删除的value
    # d.popitem()
    # result = d.popitem()
    # print(d) # {'b': 2, 'c': 3, 'd': 4}
    # print(result) # ('e', 5)
# pop(key,default) 根据key删除字典中的key-value,会将被删除的value返回
    # result = d.pop('d')
    # print(result) # 4
    # 如果删除不存在的key,会抛出错误
        # result = d.pop('z')  # KeyError: 'z'
    # 如果指定了默认值再删除不存在的key时不会报错,而是直接返回默认值
        # result = d.pop('z','这是默认值')
        # print(result)  # 这是默认值
# 用来清空字典
    # d.clear()
# copy() 该方法用于对字典进行复制,复制后的对象和原对象是独立的,不会相互影响。
    # d3 = d2.copy()
    # print(d3,id(d3))
    # print(d2,id(d2))
    # 该方法是浅复制,如果值也是一个可变对象,这个可变对象不会被复制
        # d4 = {'a':{'name':'孙悟空','age':18},'b':2,'c':3}
        # d5 = d4.copy()
        # print(d5)
        # d4['a']['name'] = '猪八戒'
        # print(d5)
# 字典的遍历
    # keys() 该方法会返回字典的所有的key,该方法会返回一个序列,序列中会保存所有字典的所有键。
        # d = {'name':'孙悟空','age':18,'gender':'男'}
        # print(d.keys())
        # 通过遍历keys()获取所有的键
            # for k in d.keys():
                # print(k)
        # 通过遍历keys()获取所有的值
            # for k in d.keys():
                # print(d[k])
        # 通过遍历keys()获取所有的键值
            # for k in d.keys():
                # print(k,d[k])
    # values()
        # 通过遍历values()获取所有的值
            # for v in d.values():
                # print(v)
    # items() 该方法会返回字典中所有的项,会返回一个序列,序列中包含双值序列
    # 双值分别是字典中的 key 和 value
        # for k,v in d.items():
            # print(k,'=',v)

集合

集合(set)
集合中只能存储不可变对象
集合中存储的对象是无序的(不是按照元素的插入顺序)
集合中不能出现重复的元素,重复的元素会自动删除,只是保留一个。

# 使用{}创建集合
    # s = {10,2,5,4}
    # print(s,type(s)) # {10, 2, 4, 5} <class 'set'>
# 集合中只能存储不可变对象
    # s = {[4,5,6],[1,2,3]}  # TypeError: unhashable type: 'list'
# 使用set()创建集合
    # s = set() # 空集合
# 可以通过set()来将序列和字典转化为集合
    # s = set([1,2,2,2,3,5,6,4])
    # print(s,type(s)) # {1, 2, 3, 4, 5, 6} <class 'set'>
# 使用set()将字典转换为集合时,只会包含字典中的键
    # s = set({'a':1,'b':2,'c':3,'d':4})
    # print(s,type(s)) # {'c', 'b', 'd', 'a'} <class 'set'>
# 集合不能通过索引,只能转换为列表再取值
    # s = {'a','b',1,2,3}
    # print(s[0])  # TypeError: 'set' object is not subscriptable
    # print(list(s)[0]) # 1
# 集合也可使用in 、not 检查集合中的元素
# 集合也可使用len()获取元素长度
# 向集合中添加元素
    # s = {'a', 'b', 1, 2, 3}
    # s.add(10)
    # print(s)
# update()将集合中的元素添加到当前集合中,可以传递序列或字典作为参数,字典只会使用键
    # s = {'a', 'b', 1, 2, 3}
    # s2 = {4,5,6}
    # s.update(s2)
    # s.update((7,8,9))
    # print(s)
# pop() 随机删除集合中的元素,并返回
    # s = {'a', 'b', 1, 2, 3}
    # s.pop()
    # result = s.pop()
    # print(result)
# remove()删除集合中指定的元素
    # s = {'a', 'b', 1, 2, 3}
    # s.remove('a')
# 清空集合
    # s = {'a', 'b', 1, 2, 3}
    # s.clear()
# 集合浅复制
    # s.copy()
# 集合的运算,在对集合进行运算时不会影响原来的集合,而是返回一个运算结果
    # 创建两个集合
        s = {1,2,3,4,5}
        s2 = {3,4,5,6,7}
    # 交集运算
        result = s & s2
    # 并集运算
        result2 = s | s2
    # 差集
        result3 = s - s2
    # 亦或集,获取只在一个集合出现的元素,两个集合不相交的部分
        result4 = s ^ s2
    # 检查一个集合是否时另一个集合的子集
        result5 = s <= s2  # False
    # 检查一个集合是否是另一个集合的真子集,一个集合含有另一个集合的全部元素,且这个集合还有另一个集合不含有的元素,另一个集合就为这个集合的真子集。

函数

'''
# 函数也是一个对象
# 对象是内存中专门用来存储数据的一块区域
# 函数可以用来保存一些可执行的代码,并且可以在需要时对这些语句多次的调用
# 创建函数:
#   def 函数名([形参1,形参2,形参n]):
#        代码块
# 定义一个函数
    def fun():
        print('这是我的第一个函数')
    # 打印函数
    print(fun)   # <function fun at 0x0000025D817AF430>
    print(type(fun))  # <class 'function'>
# 函数中保存的代码不会立即执行,需要调用才会执行
# 调用函数:
    # fun是函数对象,fun()是调用函数
    # print是函数对象,print()是调用函数
    fun()   # 这是我的第一个函数

# 定义函数,一般都是实现某种功能的。
# 定义一个函数,求任意两个数的和
# 定义函数时,可以在函数后的()中定义数量不等的形参,多个形参之间用逗号,隔开。
# 形参(形式参数),定义形参就相当于在函数内部声明了变量,但不是赋值
    def sum(a,b):
        print(a + b)
# 调用函数
# 如果函数定义时,指定了形参a,b,那么在函数调用时必须传递实参3,4。有几个形参传递几个实参.
    sum(3,4)  # 7

# 练习1
# 定义一个函数,可以用来求任意三个数的乘积
    def multiply(a,b,c):
         print(a*b*c)
    multiply(2,3,4)   # 24
# 练习2
# 定义一个函数,可以根据不同的用户名显示不同的欢迎信息
    def wel(name):
        print('欢迎',name,'光临')
    wel('孙悟空')  # 欢迎 孙悟空 光临
# 定义一个函数,可以为形参指定默认值,
# 指定了默认值以后,如果用户传递了参数,则默认值没有任何作用。如果用户没有传递,默认值生效。
    def fn(a,b,c = 20):
        print('a=',a)
        print('b=',b)
        print('c=',c)
    fn(1,2,3)
    fn(1,2)
# 实参的传递方式
# 位置参数,将对应位置的实参复制给对应位置的形参,即第一个实参赋值第一个形参,第二个实参赋值给第二个形参以此类推
    fn(1,2,3)
# 关键字参数,可以不按照形参定义的位置传递,而直接根据参数名去传递参数
    fn(a=1,b=2,c=3)
# 位置参数和关键字参数可以混合使用,混合使用时,必须将位置参数写到前面
    print('hello',end='')
# 实参的类型
    def fn2(a):
        print('a=',a)
#   b = 123
    b = 'hello'
# 函数在调用时,不会检查实参的类型
#   fn2(b) # a= 123
    fn2(b) # a= hello
    fn2(fn) # a= <function fn at 0x000001DF7DABF430>

def fn4(a):
    # 在函数中对形参进行重新赋值不会影响其他变量
    a = 20
    print('a=',a)
c = 10
fn4(c)  # a= 20
print(c)  # 10

def fn5(a):
    # 如果形参指向的是一个对象,当我们通过形参修改对象时,会影响到所有指向该对象的变量
    a[0] = 10
    print('a=',a,id(a))
c = [1,2,3]
# 形参修改对象时,会影响到所有指向该对象的变量
fn5(c)  # a= [10, 2, 3] 2096055512448
print(c,id(c)) # [10, 2, 3] 2096055512448
# 如果不改变,则复制一个副本进去传参
fn5(c.copy())  # a= [10, 2, 3] 2741749608320
print(c,id(c)) # [1, 2, 3] 2741749607872

# 不定长参数
# 要求定义一个函数,可以求任意个数字的和
def sum(*num):
    # 定义一个变量保存结果
    result = 0
    # 遍历元组,并将元组中的数值进行累加
    for i in num:
        result += i
    print(result)
sum(4,5,6)  # 15
sum(1,2)  # 3
# 定义函数时可以在形参前面加*,这样形参将会获取到所有的实参,将所有的实参保存到一个元组中。
def fn(*a):
    print(a,type(a))
fn() # () <class 'tuple'>
# 带 * 的形参只能写一个,带 * 的参数可以和其他参数配合使用
def fn2(a,b,*c):
    print('a =',a)
    print('b =',b)
    print('c =',c)
# 第一个参数给 a 第二个参数给 b ,剩下的参数给 c
fn2(1,2,3,4,5,6)

# 带 * 参数不是必须写在最后,但是带 * 参数后的所有参数,必须以关键字参数的形式传递
# 第一个参数给 a ,剩下的参数都给 b , c必须使用关键字参数
def fn3(a,*b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)
# 带 * 参数后的所有参数,必须以关键字参数的形式传递
fn3(1,2,3,4,5,c = 6)

# 要求所有的参数必须以关键字参数进行传递
def fn4(*,a,b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)

# * 形参只能接受位置参数,而不能接受关键字参数
def fn5(*a):
    print('a =',a)
# fn5(a =1) # TypeError: fn5() got an unexpected keyword argument 'a'

# **形参可以接受其他的关键字参数,将会把这些参数保存到一个字典中,字典的key就是参数的名字,字典的value就是参数的值
# **形参只能有一个,并且必须写在所有参数的最后
def fn6(b,c,**a):
    print('a =',a)
    print('b =',b)
    print('c =',c)
fn6(a =1,b = 2,c = 3)
# **形参只能有一个,并且必须写在所有参数的最后

# 参数的解包
def fn7(a,b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)
# 创建一个元组
t = (10,20,30)
# 解包,传递实参时,也可以在序列类型的参数前添加 * ,这样他会自动将序列中的元素依次作为参数传递。
# 要求序列中元素的个数必须和形参的个数一致。
fn7(*t)
# 创建一个字典
d = {'a':100,'b':200,'c':300}
# 通过 ** 来对一个字典进行解包
fn7(**d)

# 返回值,返回值就是函数执行以后返回的结果
    # 可以通过 return 来指定函数的返回值,可以通过一个变量来接受函数的返回值
        def sum(*num):
            # 定义一个变量保存结果
            result = 0
            # 遍历元组,并将元组中的数值进行累加
            for i in num:
                result += i
            return  result
        r = sum(1,2,3)
        print(r + 4)
    # return 后面跟什么值,返回的就是什么值。
    # return 后面可以跟任意的对象,返回值甚至可以是一个函数
        def fn():
            # return 100
            def fn2():
                print('hello')
            return fn2 # 返回值甚至可以是一个函数
        r = fn() # 这个函数的执行结果就是它的返回值
        print(r)  # <function fn.<locals>.fn2 at 0x000002570D6EF790>
    # 如果仅仅跟一个 return,或者不写 return,则相当于return none
        def fn2():
            return
        r = fn2()
        print(r)
    # 在函数中, return 后的代码都不会执行, return 执行,函数自动结束。
        def fn3():
            print('hello')
            return
            print('abc')
        r = fn3()
        print(r)

def fn4():
            return 10
        print(fn4) # fn4 是函数对象,<function fn4 at 0x00000234FF58F820>
        print(fn4()) # fn4()是在调用函数,打印的是fn4()函数的返回值 10

# help() 可以查询函数的用法
    help(print)

# 作用域与运行空间

# 作用域(scope):指的是变量生效的区域
    b = 20 # 全局变量
    def fn5():
        a = 10 # a 定义在函数内部,所以它的作用域就是函数内部,函数外部无法访问
        print('函数内部 a = ',a)
        print('函数外部 b = ',b)
    fn5()
    # 全局作用域:在程序执行时创建,在程序结束时销毁,所有函数以外的区域都是全局作用域
    # 全局作用域中定义变量,都属于全局变量,可以在程序的任意位置都可以被访问
    # 函数作用域,在函数调用时创建,在函数结束时销毁,每调用一次就会产生一个新的函数作用域
    # 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问。
    # 变量的查找
    #    会优先在当前作用域中寻找变量,如果有则使用,如果没有则继续去上一级作用域中寻找
    #    直到全局作用域依然没有找到,则会抛出异常
        def fn6():
            a = 30
            def fn7():
                # a = 40
                print('fn7中:','a =',a)
            fn7()
        # fn6()  # 优先在当前作用域中寻找变量   fn7中: a = 40
        fn6() # fn7中: a = 30

a = 20
        def fn7():
            # a = 10 # 在函数中为变量赋值时,默认都是为局部变量赋值
            # 如果希望在函数内部修改全局变量,则需要使用global关键字,来声明变量
            global a
            a = 10

print('函数内部 a = ',a)
        fn7()  # 函数内部 a =  10
        print('函数外部 a = ', a)  # 函数外部 a =  10

# 命名空间(namespce):

变量存储的位置,每一个变量都需要存储到指定的命名空间中,每一个作用域都有一个它对应的命名空间。
# 全局命名空间用来保存全局变量,函数命名空间用来保存函数中的变量
# 命名空间实际上就是一个专门用来存储变量的字典
    locals() # 用来获取当前作用域的命名空间
    print(locals())
# 如果在全局作用域中调用 locals()  则获取全局命名空间,如果在函数作用域中调用 locals() 则获取函数的命名空间
    globals() # 获取全局命名空间

# 递归
    # 尝试求10的阶乘(10!)
        # 1!=1
        # 2!= 1*2
        # 3!=1*2*3
        # 4!=1*2*3*4
    # 创建一个变量保存结果
        n = 10
        for i in range(1,10):
            n *= i
        print(n)
    # 创建一个函数,可以用来求任意数的阶乘
        def factorial(n):
            '''
            # 该函数用来求任意数的阶乘
            # 参数:
            #   n要求阶乘的数字
            '''
            # 创建一个变量,来保存结果
            result = n
            for i in range(1, n):
                result *= i
            return result
        print(factorial(10))

# 递归式的函数,在函数中自己调用自己
        # 无穷递归,类似于死循环
        # def fn():
        #     fn()

# 递归的整体思想是,将一个大问题分解成一个个的小问题,直到问题无法分解时,再去解决问题。
# 递归式函数的两个要件
    # 基线条件
    #   可以被分解为最小的问题,当满足基线条件时,递归不在执行
    # 递归条件
    #   将问题继续分解的条件
# 求10的阶乘
    # 10!= 10*9!
    # 9!=9*8!
    # 8!=8*7!
    # 1!=1
    def factorial(n):
        '''
        # 该函数用来求任意数的阶乘
        # 参数:
        # n要求阶乘的数字
        '''
        # 基线条件 判断n 是否为1 ,如果为1则此时不能再继续递归
        if n == 1:
            return 1
        # 递归的条件
        return n * factorial(n-1)
    print(factorial(10))

# 练习1 创建一个函数 power 来求任意数字的幂运算 n ** i
    def power(n,i):
        '''
        # 例如:
        #     10**4= 10*10**3
        #     10**3= 10*10**2
        #     10**2= 10*10**1
        '''
        if i == 0:
            return 1
        return n*power(n,i-1)
    print(power(2,4))

# 练习2 创建一个函数,用来检查任意的字符串是否是回文字符串,是,返回true否则返回false
# 回文字符串,从前往后念和从后往前念一样的。 abcba
    def fn2(str):
        '''
        # 检查 abcdefgfedcba 是不是回文
        # 检查 bcdefgfedcb 是不是回文
        # 检查 cdefgfedc 是不是回文
        # 检查 defgfed 是不是回文
        # 检查 efgfe 是不是回文
        # 检查 fgf 是不是回文
        # 检查 g 是不是回文
        '''
        # 基线添加
        if len(str) < 2:
            return True
        if str[0] != str[-1]:
            return False
        # 递归条件
        return fn2(str[1:-1])
    str = input("请输入一个字符串:")
    if fn2(str):
        print("该字符串为回文字符串")
    else:
        print("该字符串不是回文")

# 高阶函数:

#    接收一个或多个函数作为参数或者将函数作为返回值返回的函数
#    当使用一个函数作为参数时,我们实际上传进去的是函数的代码
# 创建一个列表
    l = [1,2,3,4,5,6,7,8,9,10]
# 定义一个函数检查任意的数字是否是偶数:
    def fn2(i):
        if i % 2 == 0:
            return True
        return False
# 定义一个函数用来检查任意数字是否大于5
    def fn3(i):
        if i > 5:
            return True
        return False
# 定义一个函数判断能都被3整除
    def fn5(i):
        if i % 3 == 0:
            return True
        return False
    # 可改写成:
    def fn5(i):
        return i % 3 ==0
    # 也可改写成匿名函数,见下方匿名函数lamdba
    lambda i :  i % 3 == 0

# 高阶函数
    def fn4(func,l):
        '''
        # func :可以传递一个函数作为参数
        '''
        # 创建一个新列表
        new_list = []
        # 对列表进行筛选
        for n in l:
            if func(n):
                new_list.append(n)
        # 返回新列表a
        return new_list
    print(fn4(fn5,l))

# filter() 可以从序列中过滤处符合条件的元素,然后保存到新的序列
    # 参数:
    #   1、函数,根据该函数来过滤序列(可迭代结构)
    #   2、需要过滤的序列(可迭代结构)
    # 返回值:
    #   过滤后的新序列(可迭代结构)
    # fn5作为参数传递进 filter() 函数中
        print(filter(fn5,l))  # 返回值是过滤后的新序列(可迭代结构)#  <filter object at 0x00000199CC08B520>
        print(list(filter(fn5,l))) # [3, 6, 9]
# fn5 改成匿名函数后
    print(list(filter(lambda i :  i % 3 == 0,l))) # [3, 6, 9]

# 匿名函数 lambda 函数表达式 ,专门用来创建一些简单的函数,是函数创建的又一种方式
    #   语法:lamdba 参数列表:返回值
    # 定义一个函数,返回任意两个数的和
    # 一般用来做参数,其他地方不用
        def fn6(a,b):
            return a + b
    # 匿名函数,返回任意两个数的和
        lambda c , d : c+d
        # (lambda c , d : c+d)(10,20)
    # 将匿名函数赋值给变量
        fn7 = lambda c , d : c+d
        print(fn7(10,20))
        print(fn6(10,20))

# map()函数可以对可迭代对象中的所有元素做指定的操作,然后将其保存到一个新的对象中返回
    l = [1,2,3,4,5,6,7,8,9,10]
    # 将列表中每个元素都 +1
    r = map(lambda i : i+1,l)
    print(r) # <map object at 0x0000022C3FB6B520>
    print(list(r)) # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

# sort() 用来对列表中的元素进行排序,默认直接比较列表中的元素大小
    l1 = ['bb','aaaa','c','dddddd','fff']
    l1.sort()
    print(l1) # ['aaaa', 'bb', 'c', 'dddddd', 'fff']
# 根据字符串长度排序。sort() 可以接受一个关键字参数:key
# key 需要一个函数作为参数,当设置了函数作为参数,每一次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小
    l1.sort(key=len)
    print(l1) # ['c', 'bb', 'fff', 'aaaa', 'dddddd']
    l3 = [2,5,'1',3,'6','4']
    l3.sort(key=int)
    print(l3)  # ['1', 2, 3, '4', 5, '6']
# sorted()和sort()用大基本一致,但是sorted()可以对任意的序列进行排序,并且使用sorted()排序不会影响原来的对象,而是返回一个新对象
    l2 = [2,5,'1',3,'6','4']
    print(sorted(l2,key=int)) # ['1', 2, 3, '4', 5, '6']
    print('排序后:',l2) # 排序后: [2, 5, '1', 3, '6', '4']

# 闭包

将函数作为返回值返回,也是一种高阶函数.
# 通过闭包可以创建一些只有当前函数能够访问的变量,例如可以创建一些私有的数据藏到闭包中
    def fn8():
        a = 10
        # 在函数内部再定义一个函数
        def fn1():
            print('我是fn1',a)
        # 将内部函数fn1作为返回值返回
        return fn1
# r是一个函数,是调用fn8()后返回的函数,这个函数是在fn8()内部定义,并不是全局函数,所以这个函数总是能访问到fn8()内的变量
    r = fn8()
    print(r) # <function fn8.<locals>.fn1 at 0x0000026D98E6FC10>
    r() # 我是fn1 10
# 求多个数的平均值(这种方法是固定的个数,当需要再添加一个数字的平均值时,需要更改nums)
    nums= [50,30,20,10,77]
    # sum()用来列表中所有元素的和
    print(sum(nums)/len(nums)) # 37.4
# 例如:求多个数的平均值(将 num 列表置于函数内部,当需要再添加一个数字的平均值时,直接在外部打印,在外部是无法更改 num 这个列表的。)
    def make_averager():
        # 创建一个列表用来保存数值
        num = []
        # 创建一个函数用来计算平均值
        def averager(n):
            # 将 n 添加到列表中
            num.append(n)
            # 求平均值
            return sum(num)/len(num)
        #   将内部函数作为返回值返回
        return averager
    averager = make_averager()
    print(averager(10)) # 10.0
    print(averager(20)) # 15.0
# 形成闭包的条件:
#   函数嵌套
#   将内部函数作为返回值返回
#   内部函数必须要使用到外部函数的变量

# 装饰器

# 创建几个函数
    def add(a,b):
        # 求和
        r = a + b
        return r
    def mul(a,b):
        # 求积
        r = a*b
        return r
# 希望在函数可以在计算前,打印开始计算,计算结束后打印计算完毕
# 可以直接通过修改函数中的代码来完成这个需求,但是会产生一些问题
    # 1、如果修改的函数较多,修改麻烦且不方便后期维护
    # 2、并且这样做会违反开闭原则(OCP)
    #    程序的设计要求开放对程序的扩展,而要关闭对程序的修改
# 在不修改原函数的情况下,对函数进行扩展
def fn9():
    print('我是fn9函数......')
# 只需要根据现有的函数创建一个新的函数
    def fn9_1():
        print('函数开始执行...')
        fn9()
        print('函数执行结束...')
    fn9_1()

def new_add(a,b):
        print('函数开始执行...')
        r = add(a,b)
        print('函数执行结束...')
        return r
    r = new_add(1,2)
    print(r)  # 3
# 上面的方式已经可以在不修改源代码的情况下对函数进行扩展,但是这种方式要求我们每扩展一个函数,就要求我们手动创建一个新的函数,实在是太麻烦了。
# 为了解决这个问题,可以创建一个函数,让这个函数可以自动的帮助我们生产函数
    def begin_end(old):
        '''
        # 用来对其他函数进行扩展,使其他函数可以在执行前打印执行开始,执行后打印执行结束
        # 参数:
            # old 要扩展的函数对象
        '''
        # 创建一个新的函数
        def new_func(*args,**kwargs):        
        # 如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用*args;
        # 如果我们不知道要往函数中传入多少个关键词参数,或者想传入字典的值作为关键词参数时,那就要使用**kwargs。
            print('函数开始执行...')
            # 调用被扩展的函数
            result = old(*args,**kwargs)
            print('函数执行结束...')
            # 返回函数执行结果
            return result
        # 返回新函数
        return new_func
    f = begin_end(fn9)
    f1 = begin_end(add)
    print(f1) # <function begin_end.<locals>.new_func at 0x00000212544C20D0>
    f2 = begin_end(mul)
    f()
    print(f)  # <function begin_end.<locals>.new_func at 0x000001DCB7382040>
    r1 = f1(1,2)
    print(r1) # 3
    r2 = f2(1,2)
    print(r2) # 2

# 像 begin_end()这种函数就是我们的装饰器
#   通过装饰器可以在不修改原来函数的情况下对函数进行扩展。
# 在定义函数时,可以通过@装饰器来使用指定的装饰器来装饰函数。可以同时为一个函数指定多个装饰器,函数将会按照从内向外装饰

@begin_end
    def say_hello():
        print('大家好...')
    say_hello()
    
    # 函数开始执行...
    # 大家好...
    # 函数执行结束...

'''

类与对象

# 面向对象
    对象:内存中专门用来存储数据的一块区域
    象中可以存储各种数据(比如:数字、布尔值、代码)
    对象有三部分组成:
        对象的标识(id)
        对象的类型(type)
        对象的值(value)
# 面向过程
    将程序的逻辑分解为一个一个的步骤
        通过对每个步骤的抽象,来完成程序,但是这种方式往往只适用用一个功能,如果要实现别的功能,往往要重新编写代码,可复用性较低。
        例子:
            孩子起床
                1、妈妈起床
                2、妈妈上厕所
                3、妈妈洗漱
                4、妈妈做早饭
                5、妈妈叫孩子起床
                6、孩子上厕所
                7、孩子洗漱
                8、孩子吃饭
                9、孩子背着书包上学
    这种编程方式符合我们人类的思维,编写起来相对比较简单。

# 面向对象的编程
    关注的是对象,而不关注过程。
    将所有的功能统一保存到对应的对象里
    例子:
        妈妈的功能保存到妈妈这个对象里,孩子的功能保存到孩子这个对象中,要使用某个功能直接找到对应的对象即可
    这种方式编写的代码比较容易阅读,并且比较易于维护。但是这种方式编写不太符合常规的思维
# 面向对象思维:
    1、找对象
    2、用对象

# 类(class)

对象是类的实例(instance),如果多个对象是通过一个类创建的,那么这些都是一类对象
    int()、float()、str()、list()......这些都是类
    a = int(10) # 创建一个类的实例,类似于 a = 10

# 定义一个简单的类
    使用class关键字来定义类
    class 类名:
        代码块
# 创建一个简单的类
    class MyClass():
        pass
    print(MyClass)  # <class '__main__.MyClass'>
# 使用类来创建对象
    mc = MyClass()
    mc1 = MyClass()
    print(mc)  # <__main__.MyClass object at 0x0000022AF48C2FD0>
    # mc mc1都是MyClass的实例,他们都是一类对象
# isinstance() 检查一个对象的是否是一个类的实例
    result = isinstance(mc,MyClass)
    print(result)  # True
# 类也是一个对象,用来创建对象的对象
    print(id(MyClass),type(MyClass))  # 2853281920496 <class 'type'>
# 向对象中添加变量,对象中的变量成为属性
    语法:对象.属性名 = 属性值
        mc.name = '孙悟空'
        print(mc.name) # 孙悟空
# 类的定义
    类和对象都是对程序中的内容的抽象
    实际上所有的事物都有两部分构成:
        1、数据(属性) 例如:人的属性:身高、体重、
        2、行为(方法) 例如:人的行为:行走、吃饭
    尝试定义一个表示人的类:
        class Person():
        # 在类的代码块中可以定义变量和函数
        # 在类中所定义的变量将会成为所有实例的公共属性,所有实例都可访问
            name = '孙悟空'
        # 在类中定义的函数,称为方法,这些方法可以通过该类的所有实例来访问
            def say_hello(a):
            print('你好!')
    # 创建Person的实例
        p1 = Person()
        p2 = Person()
    # 方法调用和函数调用的区别:
    # 函数调用,调用时传递几个参数,就会有几个实参,方法调用则默认回传递一个参数。所以方法中至少要定义一个形参。
    # 如果定义的函数不带形参则:TypeError: say_hello() takes 0 positional arguments but 1 was given
        p2.say_hello()  # 你好!
    # 在类的代码块中可以定义变量和函数,变量会成为该类的公共属性,所有该类的实例都可以通过 对象.属性名 的形式访问
    # 函数会成为该类的公共方法,所有该类的实例都可以通过 对象.方法名() 的形式调用方法
    # 方法在调用时,第一个参数由解析器自动传递,定义方法时至少要定义一个形参
    # 实例为什么能访问到类中的属性和方法
        # 类中定义的属性和方法都是公共的,任何该类实例都可访问

# 属性和方法的查找流程
        # 当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,如果有则直接返回当前对象的属性值,
        # 如果没有则取当前对象的类对象中取寻找,如果有则返回类对象属性值,如果没有则报错。
        p1.name = '猪八戒'
        print(p1.name,p2.name)  # 猪八戒 孙悟空
    # 类对象和实例对象中都可以保存属性(方法)
    #   如果这个属性(方法)是所有实例共享的,则应该保存到类对象中,
    #   如果这个属性(方法)是某个实例独有,则应该保存到实例对象中。
    # 一般情况下,属性保存到实例对象中。方法保存到类对象中
        p2.name = '沙和尚'
        print(p1.name,p2.name)  # 猪八戒 沙和尚

# del p2.name # 删除 p2 的 name 属性

class Person1():
    name = 'swk'
    # 在类中定义的函数,称为方法,这些方法可以通过该类的所有实例来访问
    def say_hello(self):
        # 方法每次被调用,解析器都会自动传递一个实参
        # 第一个参数,就是调用方法的对象本身
            # 如果是p1调的,则第一个参数就是p1对像
            # 如果是p2调的,则第一个参数就是p2对像
            # 一般我们将这个参数命名为 self
        # say_hello() 这个方法可以显示如下格式的数据:
        # 你好!我是xxx
        # 在方法中不能直接访问类中的属性
        print('你好!我是 %s'% self.name)
    pass
# 创建Person1的实例
p1 = Person1()
p2 = Person1()
p1.name = '孙悟空'
p2.name = '猪八戒'
p1.say_hello() # 你好!我是 孙悟空
p2.say_hello() # 你好!我是 猪八戒

class Person2:
    # 在类中可以定义一些特殊方法,特殊方法都是以__开头__结尾的方法
    # 特殊方法不需要我们自己调用,不要去尝试调用特殊方法,特殊方法在特殊的时刻自动调用
    # 学习特殊方法:特殊方法什么时候调用;特殊方法有什么作用
    # 创建对象的流程:
        # 1、创建一个变量
        # 2、在类中创建一个新对象
        # 3、__init__(self)执行
        # 4、将对象的id赋值给变量
    # __init__(self) 会在对象创建以后立即执行; __init__(self) 向新创建的对象中初始化属性
    # 调用类创建对象时,类后面的所有参数都会依次传递到 __init__()中
    def __init__(self,name1):
        # print("__init__执行了")
        # 通过self 向新建的对象中初始化属性
        self.name1 = name1
    def say_hell(self):
        print('大家好,我是%s'%self.name1)   
#################################################################################       
# 目前来讲,对于Person2类来讲name1是必须的,并且每一个对象中的name1属性基本上都是不同的。
# 而我们现在是将name1属性在定义对象以后,手动添加到对象中,这种方式很容易出现错误
p1 = Person2()
# 手动添加属性
p1.name1 = '孙悟空'
p2 = Person2()
# 手动添加属性
p2.name1 = '猪八戒'
#################################################################################     
# 我们希望用户在创建对象必须设置name1属性,如果不设置对象将无法创建,并且属性的创建应该是自动完成的,而不是在创建对象以后手动完成
# p1 = Person2()  # __init__执行了
p1 = Person2('沙和尚')
print(p1.name1)
p1.say_hell() # 大家好,我是沙和尚
# 类的基本结构
    class 类名(父类):
        公共的属性
        # 对象的初始化方法
        def __init__(self,...):
            ...
        # 其他的方法
        def method_1(self):
            ...
        def method_2(self):
            ...

# 练习

# 自定义一个类,表示狗的类(Dog)
# 属性:name、age、gender、height
# 方法:jiao() yao()

class Dog():
    def __init__(self,name,age,gender,height='12cm'):
        self.name = name
        self.age = age
        self.gender = gender
        self.height = height
    def dog_jiao(self):
        print('%s岁的'%self.age + self.gender +'%s狗又开始叫了!'%self.name)
    def dog_yao(self):
        print('%s岁的'%self.age + self.gender +self.height +'%s狗会咬人了!'%self.name)

dog1 = Dog('旺旺','1','公','20cm')
dog2 = Dog('旺财',age= 1,gender='公')
# 目前我们可以直接通过 对象.属性 的方式来修改属性的值,这种方式导致对象中的属性可任意修改,非常的不安全,值可以任意修改,不论对错
# 现在我们就需要一种方式来增强数据的安全性
# 1、属性不能随意修改
# 2、属性值不能修改喂任意的值(年龄不能是负数)
dog1.name = '小黑'
dog2.age = -10
dog1.dog_jiao()
dog1.dog_yao()
dog2.dog_yao()

封装

# 封装是面向对象的三大特性之一
# 封装指的是隐藏对象中一些不希望被外部所访问到的属性和方法
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):
        return  self.hidden_age
    def set_age(self,age):
        if age > 0:   #   使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
            self.hidden_age = age

# 如何隐藏
#   将对象的属性名修改为一个外部不知道的名字 name 改为 hidden_name 用户就不好猜这个名字了,就不好改
# 如何获取(修改)对象中的属性?
#   需要提供一个 getter 和 setter 方法使外部可以访问到属性
#   getter 获取对象中指定属性(get_属性名)
#   setter 用来设置对象的指定属性(set_属性名 )
d = Dog('旺财','10')
d.say_hello() # 大家好,我是旺财
print(d.get_name()) # 旺财
# 调用 setter 来修改 name 属性
d.say_hello() # 大家好,我是旺财
d.set_name('小黑')
d.say_hello() # 大家好,我是小黑
# 使用封装确实增加了类的定义复杂程度,但是也确保了数据的安全性
# 1、隐藏了属性名,使调用者无法随意修改对象中的属性
# 2、增加了getter 和setter方法,很好的控制属性是否是只读的。
#   如果只希望属性是只读的,则可以直接去掉setter方法
#   如果希望属性不能被外部访问,则可以直接去掉getter方法
# 3、使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
# 4、使用 getter 方法获取属性,使用 setter 方法设置属性
#   可以在读取属性和修改属性的同时做一些其他处理

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,3)
print(r.get_area()) # 15

# 可以为对象的属性使用双下划线开头 __xxx
# 双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部提问,无法通过对象访问。
# 一般不用,因为还是可以通过 _类名__属性名 从外部访问
# 一般情况下使用 _开头的都是私有属性,没有特殊需要不要修改
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) # __开头的是隐藏属性(实际上是将名字修改为了_类名__属性名),无法通过对象访问  'Person' object has no attribute '__name'
print(p._Person__name)  # 孙悟空  实际上是将名字修改为了_类名__属性名 ,依然可以在外部访问。
print(p.get_name())

property 装饰器

class Person():
    def __init__(self,name,age):
        self._name = name
    # property 装饰器,用来将一个get方法,转义为对象的属性
    # 添加 property 装饰器 以后,我们就可以调用属性一样使用 get 方法
    # 使用 property 装饰的方法,必须和属性名是一样的。
    @property
    def name(self):
        print('get 方法执行了')
        return self._name
    # setter 方法的装饰器:@属性名.setter
    @name.setter
    def name(self,name):
        print('set执行了')
        self._name = name
    @property
    def age(self):
        return self._age
    @age.setter
    def age(self,age):
        self._age = age
p = Person('孙悟空',18)
print(p.name)
p.name = '猪八戒'
p.age = 18
print(p.name,p.age)

继承和多态

继承

# 定义一个类 Animal(表示动物)
# 这个类中需要两个方法: run()、sleep()
class Animal():
    def run(self):
        print('this animal can run!')
    def sleep(self):
        print('This anmial can sleep!')

# 定义一个类 Dog()
# 这个类中需要三个方法:run()、sleep()、bark()
# 有一个类能够实现我们需要的大部分功能,但是不能实现全部的功能,如何能让这个类实现全部的功能
# 通过继承可以使一个类获取到其他类中的属性和方法
# 在定义类的时候,可以在类名后的括号中指定当前类的父类
#   子类可以继承如父类中的所有属性和方法
class Dog(Animal):
    def bark(self):
        print('汪汪汪...')

d = Dog()
d.sleep() # This anmial can sleep!
# 通过继承可以直接让子类获取到父类的方法或属性,避免重复编写,经常需要通过继承来对一个类进行扩展
class HaShiQi(Dog):
    def fang_sha(self):
        print('哈士奇犯傻')

h = HaShiQi()
h.run()
h.bark()

# 检查一个类是否是另一个类的子类
print(issubclass(Dog,Animal))  # True
# 检查一个对象是否是一个类的实例
print(isinstance(h,HaShiQi))  # True

方法重写

# 定义一个类 Animal(表示动物)
# 这个类中需要两个方法: run()、sleep()
class Animal():
    def run(self):
        print('this animal can run!')
    def sleep(self):
        print('This anmial can sleep!')

class Dog(Animal):
    def bark(self):
        print('汪汪汪...')
    def run(self):
        print('dog can run!')

d = Dog()
d.run() # dog can run!
# 在子类中如果有和父类同名的方法,则通过子类实例去调用方法时,会调用子类的方法而不是父类的方法
# 这个特点叫做方法的重写

class A(object):
    def test(self):
        print('AAA')
class B(A):
    pass
class C(B):
    pass

c = C()
c.test()
# 当我们调用一个对象的方法时,会优先去从当前对象中寻找方法,如果有则直接调用,如果没有则去当前的父类中寻找,如果还没有则去父类中的父类寻找,以此类推。

supper()

class Animal(object):
    def __init__(self,name):
        self._name = name
    def run(self):
        print('this animal can run!')
    def sleep(self):
        print('This anmial can sleep!')
    @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__ 来初始化父类定义的属性
        # supper() 动态获取当前类的父类,并且通过supper()调用父类的方法时,不需要传递 self
        super().__init__(name)  # 继续使用父类的属性
        self._age = age
    def bark(self):
        print('汪汪汪...')
    def run(self):
        print('dog can run!')
    @property
    def age(self):
        return self._age
    @age.setter
    def age(self,age):
        self._age = age
d = Dog('旺财',18)
print(d.age)
print(d.name)

多重继承

class A(object):
    def test(self):
        print('AAA')
class B(object):
    def test2(self):
        print('BBB')

# 在python中是支持多重继承的,也就是我们可以为一个类同事指定多个父类,
# 可以在类名()后添加多个类,用来实现多重继承,多重继承会让代码过于复杂。
# 如果多个父类中有同名的方法,则会在第一个父类中寻找,然后找第二个,前边父类的方法会覆盖后边父类的方法。
class C(A,B):
    pass
# 多重继承会使子类同时拥有多个父类,并且会获取到多有父类中的方法
# 类名.__bases__ 这个属性可以用来获取当前类的所有父类
print(C.__bases__)  #  (<class '__main__.A'>, <class '__main__.B'>)
c = C()
c.test2()  # BBB

多态

# 面向对象的三大特征之一
# 狗(狼狗、哈士奇、古牧、、、)
# 一个对象可以以不同的形态去呈现
# 定义两个类
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

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

say_Hello(a)  # 你好 孙悟空

# say_Hello_2() 中我们做了一个类型检查,也就是只有obj是A类型的对象时才可以正常使用,
# 其他类型的对象都无法使用该函数,这个函数就违反了多态,
# 违反了多态的函数,只适用于一种类型的对象,无法处理其他类型对象。导致函数的适应性非常差
# isinstance()函数,在开发中一般不使用
def say_Hello_2(obj):
    # 做类型检查
    if isinstance(obj,A):
        print('你好 %s'%obj.name)
    else:
        print('该对象不是A的实例')

say_Hello_2(a) # 你好 孙悟空
say_Hello_2(b) # 该对象不是A的实例
# 鸭子类型
# 如果一个东西走路像鸭子,叫声像鸭子,那么它就是鸭子

# len()
# 之所以一个对象能通过len()来获取长度,是因为对象中具有一个特殊方法__len__
# 只要对象中具有__len__特殊方法,就可以通过len()来获取它的长度
l = [1,2,3]
s = 'hello'
print(len(l))
print(len(s))

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

属性和方法

# 定义一个类
class A(object):
    # 类属性,直接在类中定义的属性是类属性,可以通过类或类的实例访问到
    count = 0
    def __init__(self):
        # 实例属性 通过实例对象添加的属性属于实例属性
        # 实例属性只能通过实例对象来访问和修改,类对象无法访问和修改
        self.name = '孙悟空'

# 实例方法
    # 在类中定义,以 self 为第一个参数的方法都是实例方法
    # 实例方法在调用时,python 会将调用对象作为 self 传入
    def test(self):
        print('这是 test 方法',self)

# 类方法
    # 在类内部使用 @classmethod 来修饰的方法属于类方法
    # 类方法的第一个参数时cls 也会被自动传递, cls 就是当前的类对象
    @classmethod
    def test_2(cls):
        print('这是test_2方法,它是一个类方法...',cls)
        print(cls.count)

# 静态方法
    # 在类中使用 @staticmethod 来修饰的方法属于静态方法
    # 静态方法,基本上是一个和当前类无关的方法,只是一个保存到当前类中的一个函数
    # 静态方法一般都是一些工具方法
    @staticmethod
    def test_3():
        print('这是test_3执行了')

a = A()
# 类属性,可以通过类或类的实例访问到
print(a.count)

# 类属性只能通过类对象来修改,不能通过实例对象修改
a.count = 10   # 通过实例对象添加的属性属于实例属性
print('A',A.count)
print('a',a.count)

a.test() # 这是 test 方法 <__main__.A object at 0x000002997082A400>

# 实例方法可以通过实例和类去调用
# A.test() # TypeError: test() missing 1 required positional argument: 'self'
A.test(a)  # 这是 test 方法 <__main__.A object at 0x000001AFBBD9A400>
# 当通过实例调用时,会自动将当前调用对象作为 self 传入
# 当通过类调用时,不会自动传递 self ,此时必须手动传递 self

A.test_2()  # 这是test_2方法,它是一个类方法... <class '__main__.A'>
# 类方法和实例方法的区别,实例方法的第一个参数是 self ,而类方法的第一个参数是 cls
# 类方法可以通过类调用也可以通过实例调用
a.test_2()  # 这是test_2方法,它是一个类方法... <class '__main__.A'>

# 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例调用
a.test_3() # 这是test_3执行了
A.test_3() # 这是test_3执行了

# 类属性
# 实例属性
# 类方法
# 实例方法
# 静态方法

垃圾回收

# 程序在运行过程中也会产生垃圾
# 在程序中没有被引用的对象就是垃圾
# 将垃圾对象从内存中删除
# python 会自动将这些没有被引用的对象删除,所以我们不用手动处理垃圾

class A:
    def __init__(self):
        self.name = 'A类'

# del 是一个特殊的方法,它会在对象被垃圾回收前调用
    def __del__(self):
        print('A()对象被删除了',self)

a = A()

print(a.name)  # A类
# 将 a 设置为了 None 此时没有任何的变量对 A() 对象进行引用,它就变成了垃圾
a = None  # A()对象被删除了 <__main__.A object at 0x0000022ED37BA400>

input('回车键退出')

特殊方法

# 特殊方法都是使用 __ 开头和结尾的
# 一般不需要我们手动调用,需要在一些特殊情况下自动执行

# 定义一个类
class Person(object):
    def __init__(self,name,age):
        self.age = age
        self.name = name

# 创建两个Person实例
p1 = Person('孙悟空',18)
# 当我们打印一个对象时,实际上打印的时对象中的特殊方法  __str__() 的返回值
print(p1) # <__main__.Person object at 0x00000133A8D1A400>

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

# __str__() 这个给特殊方法会在尝试将对象转换为字符串的时候调用
    # 它的作用可以用来指定对象转换为字符串的结果 (print 函数)
    def __str__(self):
        return 'Person [name=%s,age=%d]'%(self.name,self.age)

# __repr__() 对当前对象使用repr函数时调用
    # 它的作用是指定对象 在 交互模式 中直接输出的效果
    def __repr__(self):
        return 'hello'

p2 = Person('猪八戒',20)
print(p2) # Person [name=猪八戒,age=20]

# print(p1 > p2) # TypeError: '>' not supported between instances of 'Person' and 'Person'
'''
object.__lt__(self,other)  小于
object.__le__(self,other)  小于等于
object.__eq__(self,other)  等于
object.__ne__(self,other)  不等于
object.__gt__(self,other)  大于
object.__ge__(self,other)  大于等于

__len__() 获取长度
__bool__() 可以通过它指定对象转换为布尔值
'''
class Person(object):
    def __init__(self,name,age):
        self.age = age
        self.name = name
    # __gt__(self, other) 函数会在对象做大于比较的时候被调用,该方法的返回值将会作为比较的结果
    # 它需要两个参数,一个self 表示当前对象,other 表示和当前对象比较的对象
    # self > other
    def __gt__(self, other):
        return self.age > other.age

p2 = Person('猪八戒', 20)
p1 = Person('孙悟空',18)
print(p1 > p2) # False

class Person(object):
    def __init__(self,name,age):
        self.age = age
        self.name = name
    def __bool__(self):
        return self.age >= 18
p2 = Person('猪八戒', 20)
p1 = Person('孙悟空',11)
if p1:
    print(p1.name,'已经成年了')
else:
    print(p1.name,'还未成年')

模块

# 模块
# 模块化,指将完整的程序分解成一个一个小的模块,通过将模块组合,来搭建一个完整的程序
# 将程序分别编写到多个文件中
# 模块化的优点
#   方便开发与维护
#   模块可以复用
# 模块的创建,在python中一个.py文件就是一个模块
# 注意模块名要符合标识符的规范
# 在一个模块中引入外部模块
# import 模块名(就是python文件的名字)
import test_moudle   # test_moudle 是新建的一个.py文件,里面只有一行代码print('我是一个模块')
# 可以引入同一个模块多次,但是模块的实例只会创建一个
print(test_moudle) # <module 'test_moudle' from 'D:\\study\\阿里学习\\test_moudle.py'>
# 引入模块的第二种方式 import 模块名 as 模块别名
import test_moudle as test
print(test)  # <module 'test_moudle' from 'D:\\study\\阿里学习\\test_moudle.py'>
# 在每一个模块内部都有一个 __name__这个属性,通过这个属性可以获取到模块的名字
print(test.__name__)  # test_moudle
# __name__属性值为__main__的模块是主模块,一个程序只会有一个主模块
print(__name__)  # __main__

# Package 也是一个模块,当一个模块需要被分解为多个模块时或模块中代码过多时。需要使用包
# 创建一个包 hello
import hello
print(hello.a)  # 10
print(hello.b)   # 20
hello.test()  # test
from hello import hi
print(hi.c)  # 30

# __pycache__ 是模块的缓存文件

hello 是一个文件夹 里面包含了 __init__.py  hi.py两个文件

__init__.py

a = 10
b = 20
def test():
    print('test')

hi.py

c = 30
d = 40

python标准库

# 在这个标准库中有很多模块可以直接使用
# 官方文档中提供了很多模块 python module index
# 例如 sys
import sys
import pprint
# pprint()提供了一个方法,该方法可以用来对打印的数据做简单的格式化
# sys.argv  # 执行代码时,命令行中所包含的参数
print(sys.argv)  # ['D:/study/阿里学习/模块/python标准库.py']

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

# sys.path 列表中保存的是模块的搜索路径
pprint.pprint(sys.path)

# sys.platform 表示当前python运行的平台
print(sys.platform)

# sys.esit() 函数用来退出程序
# sys.exit('程序异常结束')

# os 模块让我们可以对操作系统进行访问
import os
print(os.path)

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

# os.system() 可以用来执行操作系统的名字
os.system('dir')
os.system('notepad')

异常

# print(a) # NameError: name 'a' is not defined
# 程序在运行过程中,不可避免的会出现一些错误,比如使用未赋值的变量,使用了不存在的变量,除0
# 程序出现异常将会导致程序立即终止
# 处理异常
try:
    pass  # 可能出现的错误语句
except:
    pass # 出错以后处理的方式
else:
    pass  # 没出错要执行的语句

# 可以将可能储蓄哦的代码块放入到 try 语句,这样如果代码没有错误,则会正常执行。
# 如果出现错误就会执行except子句中的代码,从而通过判断代码来处理异常,避免因为一个异常导致整个程序的停止

print('hello')
try:
    print(10/2)
except:
    print('出错了')
else:
    print('没有错误')
print('你好')

异常对象

print('异常出现之前')

try:
    #print(c)
    print(10/0)
except NameError:
    # 如果 except 不跟任何内容,则它会捕获到所有的异常
    # 如果在 except 后跟着一个异常的类,那么它只会捕获该类型的异常
    print('出现 NameErro 异常')
except ZeroDivisionError:
    print('被除数为0')

print('异常出现之后')

# Exception 所有异常类的父类
# 可以在异常类后面跟一个 as xx 此时 xx就是异常类
try:
    print(10/0)
except Exception as e:
    print('出现异常',e,type(e))
# 出现异常 division by zero <class 'ZeroDivisionError'>

try:
    print(10/0)
except Exception as e:
    print('出现异常',e,type(e))
finally:
    print('无论是否有异常,都会执行')

try:
    print(10/2)
finally:
    print('无论是否有异常,都会执行2')

# 自定义异常对象
# 可以使用 rasie 来抛出异常

def add(a,b):
    # 如果 a 和 b 中有负数,就像外部调用出抛出异常
    if a < 0 or b < 0:
        # raise 用于向外部抛出异常,后边可以跟一个异常类或者异常类的实例
        # raise Exception
        raise Exception('两个参数中不能有负数')
    resulet = a+b
    return resulet
print(add(-1,2))
'''
Traceback (most recent call last):
  File "D:/study/阿里学习/异常和文件/异常对象.py", line 47, in <module>
    print(add(-1,2))
  File "D:/study/阿里学习/异常和文件/异常对象.py", line 44, in add
    raise Exception('两个参数中不能有负数')
Exception: 两个参数中不能有负数
'''

# 自定义类
# 只需要创建一个类继承Exception即可
class MyError(Exception):
    pass

异常的传播

# print(10/0)
'''
Traceback (most recent call last):
  File "D:/study/阿里学习/异常和文件/异常的传播.py", line 3, in <module>
    print(10/0)
ZeroDivisionError: division by zero
'''
def fn():
    print('Hello fn')
    #print(10 / 0)
    '''
    Traceback (most recent call last):
        File "D:/study/阿里学习/异常和文件/异常的传播.py", line 23, in <module>
            fn()
        File "D:/study/阿里学习/异常和文件/异常的传播.py", line 12, in fn
            print(10 / 0)
    ZeroDivisionError: division by zero
    Hello fn

'''
fn()

# 当在函数中出现异常时,如果函数中对异常进行了处理,则异常不会再继续传播
def fn():
    print('Hello fn')
    try:
        print(10 / 0)
    except:
        pass
fn()

# 如果函数中对异常没有进行处理,则异常会继续想函数调用出传播
def fn():
    print('hello fn')
    #print(10/0)
def fn2():
    print('hello fn2')
    fn()
def fn3():
    print('hello fn3')
    fn2()
fn3()
'''
Traceback (most recent call last):
  File "D:/study/阿里学习/异常和文件/异常的传播.py", line 44, in <module>
    fn3()
  File "D:/study/阿里学习/异常和文件/异常的传播.py", line 43, in fn3
    fn2()
  File "D:/study/阿里学习/异常和文件/异常的传播.py", line 40, in fn2
    fn()
  File "D:/study/阿里学习/异常和文件/异常的传播.py", line 37, in fn
    print(10/0)
ZeroDivisionError: division by zero
'''

# 如果函数调用出处理了异常,则不再传播
def fn():
    print('Hello fn')
    print(10/0)
try:
    fn()
except:
    pass

# 异常传播时,实际上就是异常对象抛给了调用处,
print(NameError)  # <class 'NameError'>
# 比如:ZeroDivisionError 类的对象,专门用来表示除0的异常。python 中提供了多个异常类的对象

文件

# 操作文件的步骤
#   打开文件
#   对文件进行操作(读写),然后保存
#   关闭文件

# 打开文件(函数在官方文档 Librery Reference - built in function)
# 创建文件 demo.txt
# open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
# 使用 open()函数打开一个文件
#   参数:file 要打开文件的名字(路径)
# 创建一个变量,来保存文件的名字
# 如果目标文件和当前文件在同一及目录下,则直接使用文件名即可。如果不在同一级需要写全部的目录路径
file_name = 'demo.txt'
# 如果目标文件距离当前文件较远,此时可以使用绝对路径
# file_name = r'D:\study\阿里学习\异常和文件\demo.txt'

open(file_name)  # 打开 file_name 对应的文件
# open()函数的返回值是一个对象,代表了当前代开的文件
file_obj = open(file_name)
# print(file_obj)   # <_io.TextIOWrapper name='demo.txt' mode='r' encoding='cp936'>
# 当我们获取了文件对象以后,所有对文件的操作都应该通过对象来操作
# read()读取文件内容,它会将内容全部保存为一个字符串返回
content = file_obj.read()
print(content)
# 关闭文件
file_obj.close()

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

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

file_name = 'demo2.txt'
try:
    # 调用open()来打开一个文件,可以将文件分成两种类型
    # 纯文本文件(使用utf-8等编码编写的文本文件)
    # 二进制文件(图片,PPT、MP3等这些文件)
    # open()打开文件,默认是以文本文件的形式打开的,但是open()默认的编码为None
    # 处理文本文件时,必须指定文件编码
    with open(file_name,encoding='utf-8') as file_obj:
        # 如果直接调用read()它会将文本文件的所有内容全部读取出来,如果文本文件较大,容易导致内存溢出
        # content = file_obj.read() # 读取文件返回给content
        # help(file_obj.read()) # 查看read()帮助
        content = file_obj.read(6) # 读取指定数量的字符,每一次读取都是从上一次读取到的位置开始读取。
        print((content))  # 锄禾日当午
except FileNotFoundError:
    print(f'{file_name} 文件不存在')

# 读取大文件,分块儿读取,即每一次读取指定数量的字符串,然后保存到一个变量中,
# 最后把每一次读取到的内容拼接起来就是整个完整的文件内容
file_name = 'demo2.txt'
try:
    with open(file_name,encoding='utf-8') as file_obj:
        # 定义一个变量来保存文件内容
        file_content = ''
        # 定义一个变量,来指定每次读取的大小
        chunk = 6
        # 创建一个循环来读取文件内容
        while True:
            # 读取 chunk 大小的内容
            content = file_obj.read(chunk)
            # 检查是否读取到了内容
            if not content:    # 当文件中没有内容读取时,返回为一个空字符串,空字符串会转换为False
                # 内容读取完毕
                break
            # 输出内容
            # print(content,end='')
            file_content += content
except FileNotFoundError:
    print(f'{file_name} 文件不存在')
print(file_content)
print('')
import pprint
file_name = 'demo2.txt'
with open(file_name,encoding='utf-8') as file_obj:
    # readline() 可以用来读取一行内容
    # print(file_obj.readline())
    # print(file_obj.readline(),end='')
    # print(file_obj.readline())
    # readlines() 该方法用于一行一行的读取内容,它会将一次性读取的内容封装到一个列表中返回
    # r = file_obj.readlines()
    # pprint.pprint(r)   # ['锄禾日当午\n', '汗滴禾下土\n', '谁知盘中餐\n', '粒粒皆辛苦']
    # pprint.pprint(r[0]) # '锄禾日当午\n'
    # 利用循环读取
    for t in file_obj:
        print(t)

文件的写入

file_name = 'demo.txt'
# 使用open() 打开文件时必须要制定打开文件所要做的操作
# 如果不指定操作类型,则默认时读取文件,而读取文件时不不能向文件中写入的。
# mode = r 表示读,w表示写,a 表示追加内容
# 使用w来写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件(删除原来文件中的所有内容)
with open(file_name,mode='wt',encoding='utf-8') as file_obj:
    # write() 向文件中写入内容,如果操作的是文本文件,则需要传递一个字符串作为参数
    file_obj.write('Hello,How are you\n')  # \n 换行
    # write() 可以分多次向文件中写入内容
    file_obj.write('Hello,How are you')
    # 写入完成以后,该方法会返回写入字符的个数
    r = file_obj.write('今天天气真好呀')
    print(r) # 7

# mode = 'a' 表示追加内容,如果文件存在则追加,不存在则新建文件
with open(file_name,mode='a',encoding='utf-8') as file_obj:
    file_obj.write('这是追加的文字')

# mode = 'r+' 表示可读可写,文件不存在会报错

# 读取二进制文件(除了文本文件以外都是二进制文件)
# t 读取文本文件
# b 读取二进制文件
file_name_1 = 'jiuci.mp3'
with open(file_name_1,mode='rb') as file_obj_1:
    # 读取文本文件时,size是以字符为单位的,读取二进制文件时,size是以字节为单位的
    # print(file_obj_1.read(100))

# 将读取到的内容写出来
    # 定义一个新的文件
    new_name = 'new_jiuci'
    with open(new_name,'wb') as new_obj:
        # 定义每次读取的大小
        ch = 1024 * 100
        while True:
            # 读取数据
            content = file_obj_1.read(ch)
            # 内容读取完毕终止循环
            if not content:
                break
            # 将读取的数据写入到新对象中
            new_obj.write(content)

文件的其他操作

# 删除文件
# 重命名文件
# 查看目录
import os   # 模块使用方法详见官方文档
from pprint import pprint
# os.listdir() 获取指定目录结构
# 需要一个路径作为参数,回获取到该路径下的目录结构,默认路径为.(当前目录)
# 该方法会返回一个列表,目录中的每一个文件(夹)的名字都是列表中的一个元素
r = os.listdir()
pprint(r)
# os.getcwd() 获取当前所在的目录
r = os.getcwd()
pprint(r)
# os.chdir() 切换当前的目录
os.chdir('..')
r = os.getcwd()
pprint(r)
# 创建目录
os.mkdir('aaa') # 在当前目录下创建一个 aaa 目录
# 删除目录
os.rmdir('aaa')
# 创建文件
open('aaa.txt','w')
# 删除文件
os.remove('aaa.txt')
# 创建一个文件
open('abc.txt',mode='w')
# 重命名文件(两个参数第一个是原来文件所在的路径,第二个是重命名后文件所在路径)
os.rename('abc.txt','cba.txt')   # 例如:实现文件移动功能  os.rename('abc.txt','C:\Users\Administrator\Desktop\cba.txt')
os.remove('cba.txt')

seek()tell()

with open('demo.txt','rb') as file_obj:
    # print(file_obj.read()[0])  # 72
    # print(file_obj.read(5))

# seek() 修改当前读取的位置
    file_obj.seek(6)         # 从第6个开始读,读5个
    print(file_obj.read(5))
    # seek() 需要两个参数
    # 1、是要切换到的位置
    # 2、计算位置:可选值 0(从头计算,默认值);1(从当前位置计算);2(从最后位置开始)
# tell() 方法用来查看当前读取的位置
    print('当前读取到了',file_obj.tell())

with open('demo2.txt','rt',encoding='utf-8') as file_obj_1:
    file_obj_1.seek(6)   # 3个字节为一个汉字
    print(file_obj_1.read())
    print('当前位置在',file_obj_1.tell())

demo2.txt

锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦

demo.txt

Hello,How are you Hello,How are you

待更......

python 从0-1笔记相关推荐

  1. Python基础语法学习笔记

    Python基础语法学习笔记 想淘宝省钱看我简介,博客www.liangxin.name (一) 一.Print()函数 1.数字可以直接输出,无需加引号 只能理解数字,却读不懂文字.因为数字和数学运 ...

  2. Python基础教程学习笔记:第一章 基础知识

    Python基础教程 第二版 学习笔记 1.python的每一个语句的后面可以添加分号也可以不添加分号:在一行有多条语句的时候,必须使用分号加以区分 2.查看Python版本号,在Dos窗口中输入&q ...

  3. python数据分析入门学习笔记儿

    转载: http://www.cnblogs.com/zzhzhao/p/5269217.html 学习利用python进行数据分析的笔记儿&下星期二内部交流会要讲的内容,一并分享给大家.博主 ...

  4. Python 数据分析与展示笔记4 -- Pandas 库基础

    Python 数据分析与展示笔记4 – Pandas 库基础 Python 数据分析与展示系列笔记是笔者学习.实践Python 数据分析与展示的相关笔记 课程链接: Python 数据分析与展示 参考 ...

  5. Python 数据分析与展示笔记3 -- Matplotlib 库基础

    Python 数据分析与展示笔记3 – Matplotlib 库基础 Python 数据分析与展示系列笔记是笔者学习.实践Python 数据分析与展示的相关笔记 课程链接: Python 数据分析与展 ...

  6. Python 数据分析与展示笔记2 -- 图像手绘效果

    Python 数据分析与展示笔记2 – 图像手绘效果 Python 数据分析与展示系列笔记是笔者学习.实践Python 数据分析与展示的相关笔记 课程链接: Python 数据分析与展示 参考文档: ...

  7. Python 数据分析与展示笔记1 -- Numpy 基础

    Python 数据分析与展示笔记1 – NumPy 基础 Python 数据分析与展示系列笔记是笔者学习.实践Python 数据分析与展示的相关笔记 课程链接: Python 数据分析与展示 参考文档 ...

  8. Python GUI编程(Tkinter)笔记

    Python GUI编程Tkinter笔记 1 显示任意格式图片 2 固定框架Frame大小 3 选择文件夹或文件 4 展示菜单栏 5 展示选择的图片 1 显示任意格式图片 Tkinter只支持显示G ...

  9. 过拟合解决方法python_《python深度学习》笔记---4.4、过拟合与欠拟合(解决过拟合常见方法)...

    <python深度学习>笔记---4.4.过拟合与欠拟合(解决过拟合常见方法) 一.总结 一句话总结: 减小网络大小 添加权重正则化 添加 dropout 正则化 1.机器学习的根本问题? ...

  10. python基础入门学习笔记 (2)

    python基础入门学习笔记 2021年2月8日 1 编译器和解释器的区别 编译器/解释器:高级语言与机器之间的翻译官 2 值传递: print "a = ",a print &q ...

最新文章

  1. linux 进入单用户模式修改root密码
  2. Android App压力测试(Monkey和ADB)
  3. Linux组管理和权限管理
  4. CSS中clear属性的both、left和right浅析
  5. Netbeans学习总结
  6. 【干货】Html与CSS入门学习笔记12-14【完】
  7. Codeforces Global Round 10
  8. 酒精测试仪检定设备设计与验证
  9. Openresty各个阶段的执行次序
  10. 详细讲解黑客常用的远程控制木马
  11. Datawhale组队学习周报(第026周)
  12. Oracle_登录数据库系统
  13. 求助:大文件mp4恢复
  14. conda离线安装包
  15. 【自然语言处理】Gensim中的Word2Vec
  16. [Vuforia] 详解·高通Vuforia识别追踪3D物体/模型,Unity开发
  17. MYSQL练习题:连续两天登录的游戏玩家比率
  18. Mac M1 python 连点器脚本
  19. matlab中估计丢失的数据,空间计量经济学基本模型的matlab估计
  20. (附源码)计算机毕业设计SSM智能导诊系统

热门文章

  1. 不懂语言代码,超级菜鸟的建站分享(一):建站流程
  2. 分布式光伏并网的流程、技术及资料要求
  3. 励志成为博文美眉的第一天
  4. 如何写出高效的软件测试用例?微信朋友圈动态发送为例
  5. Windows 7下的Aero效果
  6. python解析xml文件最好选用的模块_用Python解析XML文件
  7. cmd中at命令用法
  8. win7删除计算机 网络连接,win7本地连接有2个怎么办|win7删除本地连接2的方法
  9. 《工具箱-服务器相关》Linux搭建FTP服务器
  10. C语言写一个小程序,胖胖的爱心桃