1、变量
- 推荐
- 驼峰体:AgeOfOldboy = 73
- 下划线:age_of_oldboy = 73
# 变量的小高级:age1 = 18age2 = age1age3 = age2age2 = 12print(age1,age2,age3)18 12 18Process finished with exit code 0#********************************************************************************************************# 变量的小高级:age1 = 18age2 = 12age2 = age1age3 = age2print(age1,age2,age3)18 18 18Process finished with exit code 0
解析:首先从上到下执行,
2、常量
- 常量:一直不变的量。python中没有真正的常量,为了应和其他语言的口味,全部大写的变量称之为常量。
- 将变量全部大写,放在文件的最上面。
# 常量# 约定俗成不能改变 NAME = '太白' print(NAME)太白Process finished with exit code 0
3.注释(重点)
- 单行注释: #
- 多行注释: ”’被注释内容”’ “””被注释内容”””
3、流程控制语句if基本结构:
if 条件: 结果
1、单独if
- 如果条件成立,就执行if语句中的代码,如果条件不成立,就不执行if语句中的代码。
#单独ifif 2 1: print(222)222Process finished with exit code 0
2、if else 二选一
# if else 二选一age = input('请输入年龄:')if int(age) > 18: print('恭喜你,成年了')else: print('小屁孩儿') 请输入年龄:1小屁孩儿Process finished with exit code 0
3、if elif elif …. 多选一
- 语句测成立后就打印该语句中的代码,下面的判断语句就不执行了
num = int(input('猜点数:'))if num == 1: print("晚上请你吃饭")elif num == 3: print("一起溜达")elif num==2: print("请你大宝剑")
4、if elif elif …. else 多选一
- 当所有条件都不满足,就执行else中的代码、有一个成立了,就不会执行else中的代码
num = int(input('猜点数:'))if num == 1: print("晚上请你吃饭")elif num == 3: print("一起溜达")elif num==2: print("请你大宝剑")else: print("太笨了")
5、嵌套的if
#思路 -----然后将pass改为对应的用户名、密码的条件判断if your_code == code: passelse: print("验证码错误")
username=input("请输入用户名:")password=input("请输入密码")code= 'qwer'your_code=input("请输入验证码:")if your_code == code: if username == 'mike' and password == '123456': print('登录成功') else: print('账号或者密码错误')else: print("验证码错误")
4、while 循环1,基本结构:
while:无限循环 for :有限循环
while 条件: 循环体
2,基本原理:
3,循环如何终止?
1,改变条件。
flag = Truewhile flag: print('狼的诱惑') print('我们不一样') print('月亮之上') flag = False print('庐州月') print('人间')
#加入计数器,使其达到条件后退出循环i=0while True: print('狼的诱惑') print('我们不一样') print('月亮之上') print('庐州月') print('人间') i=i+1 if i==2: break
2,break
- 退出循环
while True: print('狼的诱惑') print('我们不一样') print('月亮之上') print('庐州月') print('人间') break
3,系统命令(今天不讲)4,continue
- continue : 退出本次循环,继续下一次循环
# continue : 退出本次循环,继续下一次循环i=0while True: print('狼的诱惑') i=i+1 if i==2: continue print('月亮之上') if i == 3: break 狼的诱惑 #i=1月亮之上狼的诱惑 #i=2 continue退出本次循环 只打印了狼的诱惑狼的诱惑 #i=3月亮之上Process finished with exit code 0
# while else: while 循环如果被break打断,则不执行else语句。count = 1while count < 5: print(count) if count == 2: break count = count + 1else: print(666)
优化登录的代码;
#有三次错误输入的机会count=1while count<4: username=input("请输入用户名:") password=input("请输入密码") code= 'qwer' your_code=input("请输入验证码:") if your_code == code: if username == 'mike' and password == '123456': print('登录成功') else: print('账号或者密码错误') else: print("验证码错误") count=count+1
练习题:打印1~ 100 所有的数字
#第一种方案count=1while True: print(count) count=count+1 if count==101: break #第二种方案count = 1flag = Truewhile flag: print(count) count = count + 1 if count == 101: flag = False#第三种方案 count = 1while count < 101: print(count) count = count + 1
练习题:1 + 2 + ….. 100 的最终结果
# 1 + 2 + 3 + ...... 100 的最终结果:s = 0count = 1while count < 101: s = s + count count = count + 1print(s)
day2:1、代码块(重点)
- 代码块:我们所有的代码都需要依赖代码块执行。
- 一个模块,一个函数,一个类,一个文件等都是一个代码块。。
- 而作为交互方式输入的每个命令都是一个代码块。
而对于一个文件中的两个函数,也分别是两个不同的代码块:
2、代码块的缓存机制
两个机制: 同一个代码块下,有一个机制。不同的代码块下,遵循另一个机制。
同一个代码块下的缓存机制。
前提条件:同一个代码块内。
机制内容:pass
机制内容:Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用。换句话说:执行同一个代码块时,遇到初始化对象的命令时,他会将初始化的这个变量与值存储在一个字典中,在遇到新的变量时,会先在字典中查询记录,如果有同样的记录那么它会重复使用这个字典中的之前的这个值。所以在你给出的例子中,文件执行时(同一个代码块)会把i1、i2两个变量指向同一个对象,满足缓存机制则他们在内存中只存在一个,即:id相同
适用的对象: int bool str
具体细则:所有的数字,bool,几乎所有的字符串。
优点:提升性能,节省内存。
不同代码块下的缓存机制: 小数据池。
前提条件:不同代码块内。
机制内容:pass
Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。python会将一定规则的字符串在字符串驻留池中,创建一份,当你将这些字符串赋值给变量时,并不会重新创建对象, 而是使用在字符串驻留池中创建好的对象。 其实,无论是缓存还是字符串驻留池,都是python做的一个优化,就是将~5-256的整数,和一定规则的字符串,放在一个‘池’(容器,或者字典)中,无论程序中那些变量指向这些范围内的整数或者字符串,那么他直接在这个‘池’中引用,言外之意,就是内存中之创建一个
适用的对象: int bool str
具体细则:-5~256数字,bool,满足规则的字符串。
优点:提升性能,节省内存。
总结:
- 面试题考。
- 回答的时候一定要分清楚:同一个代码块下适用一个缓存机制。不同的代码块下适用另一个缓存机制(小数据池)
- 小数据池:数字的范围是-5~256.
- 缓存机制的优点:提升性能,节省内存。
3、深浅copy(面试会考)1,先看赋值运算
- 对于赋值运算来说,a1与a2指向的是同一个内存地址,所以他们是完全一样的,在举个例子,比如张三李四合租在一起,那么对于客厅来说,他们是公用的,张三可以用,李四也可以用,但是突然有一天张三把客厅的的电视换成投影了,那么李四使用客厅时,想看电视没有了,而是投影了,对吧?a1,a2指向的是同一个列表,任何一个变量对列表进行改变,剩下那个变量在使用列表之后,这个列表就是发生改变之后的列表。
#赋值运算a1=[1,2,3,[22,33]]a2=a1a1.append(666)print(a1,id(a1))print(a2,id(a2))[1, 2, 3, [22, 33], 666] 1768812233480[1, 2, 3, [22, 33], 666] 1768812233480Process finished with exit code 0
2,浅拷贝copy。
- 对于浅copy来说,只是在内存中重新创建了开辟了一个空间存放一个新列表,但是新列表中的元素与原列表中的元素是公用的。
所以a1 和a2的ID不同,但是内容ID相同
#浅copy#同一代码块下:a1=[1,2,3,[22,33]]a2=a1.copy()a1.append(666)print(a1,id(a1))print(a2,id(a2))[1, 2, 3, [22, 33], 666] 1321060254472[1, 2, 3, [22, 33]] 1321059431112Process finished with exit code 0#*********************************************# 不同代码块下:a1=[1,2,3,[22,33]]a2=a1.copy()a1[-1].append(666)print(a1,id(a1))print(a2,id(a2))#打印a1 a2中小列表的存储IDprint(id(a1[-1]))print(id(a2[-1]))[1, 2, 3, [22, 33, 666]] 2332545303304[1, 2, 3, [22, 33, 666]] 233254447994423325452860882332545286088Process finished with exit code 0
思考1:a2 浅拷贝a1后,a1列表中的小列表添加元素后,a2的小列表为啥也添加?---因为a1 a2 是两个不同的大列表,但是列表里边的元素都是公用一个,所以a1的小列表添加666 a2的小列表也添加666
思考2:如果使a1[0]=90,此时a2[0]是否等于90? 为什么a2[0]不变?因为之前的a1[0],a1[1],a1[3]是1,2,3 是字符串,是不可变的,我改变的不是a1[0]本身,我只是改变了a1这个列表第一个槽位的内存关系
a1=[1,2,3,[22,33]]a2=a1.copy()a1[0]=90print(a1)print(a2)[90, 2, 3, [22, 33]][1, 2, 3, [22, 33]]Process finished with exit code 0
3,深拷贝deepcopy
- 深copy则会在内存中开辟新空间,将原列表以及列表里边的可变的数据类型重新创建一份,不可变的数据类型则沿用之前的(即公用一个)
#深copyimport copya1=[1,2,3,[22,33]]a2=copy.deepcopy(a1)print(a1,id(a1))print(a2,id(a2))[1, 2, 3, [22, 33]] 1512146813512[1, 2, 3, [22, 33]] 1512146814664Process finished with exit code 0
示例:a1[-1].append(666)
#深copyimport copya1=[1,2,3,[22,33]]a2=copy.deepcopy(a1)a1[-1].append(666)print(a1)print(a2)[1, 2, 3, [22, 33, 666]][1, 2, 3, [22, 33]]Process finished with exit code 0
4,深浅拷贝面试题
#面试题:# 考察的内容是:切边是深拷贝还是浅拷贝?--浅拷贝a1=[1,2,3,[22,33]]a2=a1[:]a1[-1].append(666)print(a1)print(a2)# 浅copy: list dict: 嵌套的可变的数据类型是同一个。# 深copy: list dict: 嵌套的可变的数据类型不是同一个 。
def eat(a,b,c,d): print('我请你吃:{},{},{},{}'.format(a,b,c,d))eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','烧花鸭')# 急需要一种形参,可以接受所有的实参。# 万能参数: *args, 约定俗称:args,# 函数定义时,*代表聚合。 他将所有的位置参数聚合成一个元组,赋值给了 args。def eat(*args): print(args) print('我请你吃:{},{},{},{}'.format(*args))eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','烧花鸭')
day3:1、形参角度:(重点)万能参数: *args
- *的魔性用法——-在函数的定义时,表示聚合 ;在函数的调用时,表示打散或解包。
def eat(a,b,c,d): print('我请你吃:{},{},{},{}'.format(a,b,c,d))eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','烧花鸭')# 急需要一种形参,可以接受所有的实参。# 万能参数: *args, 约定俗称:args,# 函数定义时,*代表聚合。 他将所有的位置参数聚合成一个元组,赋值给了 args。def eat(*args): print(args) print('我请你吃:{},{},{},{}'.format(*args))eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','烧花鸭')我请你吃:蒸羊羔,蒸熊掌,蒸鹿邑,烧花鸭('蒸羊羔', '蒸熊掌', '蒸鹿邑', '烧花鸭')我请你吃:蒸羊羔,蒸熊掌,蒸鹿邑,烧花鸭Process finished with exit code 0
- 练习题:写一个函数:计算你传入函数的所有的数字的和。
#练习题:写一个函数:计算你传入函数的所有的数字的和。# tu1=(1,2,3,4,5,6,7)# count=0# for i in tu1:# count=count+i# print(count)def func(*args): count = 0 for i in args: count = count + i return countprint(func(1,2,3,4,5,6,7))28Process finished with exit code 0
万能参数:**kwargs
# **kwargs# 函数的定义时: ** 将所有的关键字参数聚合到一个字典中,将这个字典赋值给了kwargs.def func(**kwargs): print(kwargs)func(name='alex',age=73,sex='laddyboy'){'name': 'alex', 'age': 73, 'sex': 'laddyboy'}Process finished with exit code 0
# * **在函数的调用时,*代表打散---也可以说 解包
def func(*args): print(args)func([1,2,3],[22,33])func(*[1,2,3],[22,33])func(*[1,2,3],*[22,33])([1, 2, 3], [22, 33])(1, 2, 3, [22, 33])(1, 2, 3, 22, 33)Process finished with exit code 0
def func(*args,**kwargs): print(args) print(kwargs)func({'name': '太白'},{'age': 18})print('*********')func(**{'name': '太白'},**{'age': 18})({'name': '太白'}, {'age': 18}){}*********(){'name': '太白', 'age': 18}Process finished with exit code 0
形参角度的参数的顺序
(位置参数,默认参数,万能参数)
- 思考?——*args的位置?
- 必须放在位置参数后面,默认参数前面(如果默认参数放在*args 前面,那么调用的时候,默认参数的值会被改变)
#形参角度的参数的顺序(位置参数,默认参数,万能参数)# *args的位置?----必须放在位置参数后面,默认参数前面def func(a,b,*args,sex='男'): print(a,b) print(sex) print(args)func(1,2,3,4)1 2男(3, 4)Process finished with exit code 0
- 思考?—-**kwargs的位置?
- 要放在默认参数后面,
#**kwargs的位置?def func(a,b,*args,sex='男',**kwargs): print(a,b) print(sex) print(args) print(kwargs)func(1,2,3,4,age=12)1 2男(3, 4){'age': 12}Process finished with exit code 0
仅限关键字参数(了解)
- 放在*args和**kwargs的位置之间,类似于默认参数
def func(a,b,*args,sex='男',c,**kwargs): print(a,b) print(sex) print(args) print(c) print(kwargs)func(1,2,3,4,age=12,c='666')1 2男(3, 4)666{'age': 12}Process finished with exit code 0
形参角度最终的顺序:
- 位置参数,*args,默认参数,仅限关键字参数,**kwargs
函数的嵌套(高阶函数)
# 例1:---思考打印结果和顺序def func1(): print('in func1') print(3)def func2(): print('in func2') print(4)func1()print(1)func2()print(2)in func131in func242Process finished with exit code 0
# 例2:---思考打印结果和顺序def func1(): print('in func1') print(3)def func2(): print('in func2') func1() print(4)print(1)func2()print(2)1in func2in func1342Process finished with exit code 0
# 例3:---思考打印结果和顺序def fun2(): print(2) def fun3(): print(6) print(4) fun3() print(8)print(3)fun2()print(5)324685Process finished with exit code 0
day11:1、补充:默认参数的陷阱
- 陷阱只针对于默认参数是可变的数据类型:
- 结论:如果你的默认参数指向的是可变的数据类型,那么你无论调用多少次这个默认参数,都是同一个。
# 陷阱只针对于默认参数是可变的数据类型:def func(name,alist=[]): alist.append(name) return alistret1=func("mike")print(ret1,id(ret1)) #打印结果 ['mike']ret2=func('太白金星')print(ret2,id(ret2))['mike'] 2579283148744['mike', '太白金星'] 2579283148744Process finished with exit code 0
- 图形讲解
面试题1:
def func(a,list=[]): list.append(a) return listprint(func(10))print(func(20,[]))print(func(100))[10][20][10, 100]Process finished with exit code 0#等同于如下:l1 = []l1.append(10)print(l1)l2 = []l2.append(20)print(l2)l1.append(100)print(l1)
面试题2:
- 面试题2是 3个执行完才打印,面试题1是执行一个打印一次
def func(a,list=[]): list.append(a) return listret1=func(10)ret2=(func(20,[]))ret3=(func(100))print(ret1)print(ret2)print(ret3)[10, 100][20][10, 100]Process finished with exit code 0
2、补充:局部作用域的坑:global:1, 在局部作用域声明一个全局变量。
注意:要先调用函数,这样函数里的全局变量才可以被引用
#global#1, 在局部作用域声明一个全局变量。name = 'alex'def func(): global name name = '太白金星' print(name)func()print(name)太白金星太白金星Process finished with exit code 0
- 如果先调用局部变量中的全局变量,会报错—–要先调用函数
def func(): global name name = '太白金星' print(name)print(name)func()NameError: name 'name' is not definedProcess finished with exit code 1
2.修改一个全局变量
- 原来这样写会报错
- 加入global 就可以完成引用全局变量并且修改全局变量
#2. 修改一个全局变量count=1def func(): global count count=count+1print(count)func()print(count)12Process finished with exit code 0
nonlocal1.不能够操作全局变量
count = 1def func(): nonlocal count count += 1func()SyntaxError: no binding for nonlocal 'count' foundProcess finished with exit code 1
2.局部作用域:内层函数对外层函数的局部变量进行修改
# 2. 局部作用域:内层函数对外层函数的局部变量进行修改。def wrapper(): count = 1 def inner(): nonlocal count count += 1 print(count) inner() print(count)wrapper()
3、函数名的运用1.函数名指向的是函数的内存地址。
函数名 + ()就可以执行次函数。
def func(): print(666)func()# 1. 函数名指向的是函数的内存地址。# 函数名 + ()就可以执行次函数。print(func,type(func))666 Process finished with exit code 0
2, 函数名就是变量。
def func(): print(666)f=funcf1=ff2=f1f2()666Process finished with exit code 0
- 图形解释
def func(): print('in func')def func1(): print('in fun1')func1=funcfunc1()#打印那个结果?#分析:类比如下a=1b=2a=bprint(a)in func2Process finished with exit code 0
3.函数名可以作为容器类数据类型的元素
# 3. 函数名可以作为容器类数据类型的元素# a = 1# b = 2# c = 3# l1 = [a,b,c]# print(l1)def func1(): print('in fun1')def func2(): print('in fun2')def func3(): print('in fun3')a1=[func1,func2,func3]for i in a1: i() in fun1in fun2in fun3Process finished with exit code 0
4.函数名可以作为函数的参数
def func(): print('in func')def fun1(x): x() #相当于func() print('in func1')fun1(func)in funcin func1Process finished with exit code 0
5.函数名可以作为函数的返回值
# 5. 函数名可以作为函数的返回值def func(): print('in func')def func1(x): print('in func1') return xret=func1(func)ret()in func1in funcProcess finished with exit code 0
4、新特性:格式化输出1.旧的格式化方式
%s 和 format
#旧个格式化输出,太麻烦了name = '太白'age = 18msg = '我叫%s,今年%s' %(name,age)print(msg)msg1 = '我叫{},今年{}'.format(name,age)print(msg)我叫太白,今年18我叫太白,今年18Process finished with exit code 0
2.新特性:格式化输出
# 新特性:格式化输出name = '太白'age = 18msg = f'我叫{name},今年{age}'print(msg)我叫太白,今年18Process finished with exit code 0
2.1 可以加表达式
# 可以加表达式dic = {'name':'alex','age': 73}msg = f'我叫{dic["name"]},今年{dic["age"]}'print(msg)count = 7print(f'最终结果:{count**2}')name = 'barry'msg = f'我的名字是{name.upper()}'print(msg)最终结果:49我的名字是BARRYProcess finished with exit code 0
2.2 结合函数写
# 结合函数写:def _sum(a,b): return a + bmsg = f'最终的结果是:{_sum(10,20)}'print(msg)# ! , : { } ;这些标点不能出现在{} 这里面。最终的结果是:30Process finished with exit code 0
优点:
结构更加简化。
可以结合表达式,函数进行使用。
效率提升很多。
5、迭代器:(重点)1.可迭代对象
字面意思:对象?python中一切皆对象。一个实实在在存在的值,对象。
可迭代?:更新迭代。重复的,循环的一个过程,更新迭代每次都有新的内容,
可以进行循环更新的一个实实在在值。
专业角度:可迭代对象? 内部含有
'__iter__'
方法的对象,可迭代对象。目前学过的可迭代对象?str list tuple dict set range 文件句柄
获取对象的所有方法并且以字符串的形式放在列表里
语法:dir()
- 判断一个对象是否是可迭代对象
- 用dir()获取所有方法,如果内部含有
'__iter__'
方法,就是可迭代对象
- 用dir()获取所有方法,如果内部含有
#获取对象的所有方法# s1 = 'fjdskl'# print(dir(s1))l1=[1,2,3]print(dir(l1))print('__iter__' in dir(l1)) #打印结果True 就表示可迭代对象['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']TrueProcess finished with exit code 0
小结
字面意思:可以进行循环更新的一个实实在在值。
专业角度: 内部含有
'__iter__'
方法的对象,可迭代对象。判断一个对象是不是可迭代对象:
'__iter__'
in dir(对象)str list tuple dict set range
优点:
- 存储的数据直接能显示,比较直观。
- 拥有的方法比较多,操作方便。
缺点:
占用内存。
不能直接通过for循环,不能直接取值(索引,key)。
2.迭代器的定义
- 字面意思:更新迭代,器:工具:可更新迭代的工具。
- 专业角度:内部含有
'__iter__'
方法并且含有'__next__'
方法的对象就是迭代器。 - 可以判断是否是迭代器:
'__iter__'
and'__next__'
在不在dir(对象)
3.判断一个对象是否是迭代器
#判断文件是否是迭代器with open('文件1',encoding='utf-8',mode='w') as f1: print(('__iter__' in dir(f1)) and ('__next__' in dir(f1)))s1 = 'fjdag'print('__next__' in dir(s1))TrueFalseProcess finished with exit code 0
4.可迭代对象如何转化成迭代器
`iter([1,2,3])`#或者s1.__iter__()
s1 = 'fjdag'obj=iter(s1)#s1.__iter__()print(obj)print('__next__' in dir(obj))TrueProcess finished with exit code 0
5.迭代器的取值
一个next 取一个值
多一个next 就会报错
s1 = 'python'obj=iter(s1)print(next(obj))print(next(obj))print(next(obj))print(next(obj))print(next(obj))print(next(obj))pythonProcess finished with exit code 0
- 报错结果
Traceback (most recent call last): File "D:/PycharmProjects/zixue_python/day_01/12_迭代器.py", line 25, in print(next(obj))StopIteration
6.要迭代器有什么用?
- 当数据很大的时候,我要考虑把这些大批量数据存储起来,需要节省内存,所以要考虑迭代器
节省内存。
自己理解如何节省内存:比如定义一个列表,那根据内存机制,内置中会保存这个列表中所有数据,然后分配内存地址(即id)
如果把这个列表转换为迭代器,这时候next一次,取一个值,然后存储这个值
惰性机制,next一次,取一个值。
7.while循环模拟for循环机制
l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]# 将可迭代对象转化成迭代器。obj = iter(l1)while 1: try: print(next(obj)) except StopIteration: break
8.可迭代对象与迭代器的对比
- 可迭代对象是一个操作方法比较多,比较直观,存储数据相对少(几百万个对象,8G内存是可以承受的)的一个数据集。
- 当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
- 是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。
- 当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。
day12:1、生成器(重点)
- 生成器:python社区,生成器与迭代器看成是一种。生成器的本质就是迭代器。唯一的区别:生成器是我们自己用python代码构建的数据结构。迭代器都是提供的,或者转化得来的
1.获取生成器的三种方式:
- 生成器函数。
- 生成器表达式。
- python内置函数或者模块提供。
- (其实1,3两种本质上差不多,都是通过函数的形式生成,只不过1是自己写的生成器函数,3是python提供的生成器函数而已)
2.获取迭代器的方式:
- python提供的,比如文件句柄
- 通过 iter 转化的
3.生成器函数 获得生成器:
- 将函数中的return换成yield,这样func就不是函数了,而是一个生成器函数
- 读取生成器的内容是,一个next 对应一个yield
#函数# def func():# print(111)# print(222)# return 3## ret=func()# print(ret)#生成器def func(): print(111) print(222) yield 3 a=1 b=2 c=a+b print(c) yield 4ret=func()print(ret)print(next(ret))print(next(ret))#一个next 对应一个yield111222334Process finished with exit code 0
面试题:return 和 yield 的区别?
- return:函数中只存在一个return结束函数,并且给函数的执行者返回值。
- yield:只要函数中有yield那么它就是生成器函数而不是函数了。生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next。
练习题:吃包子
- 需求:老男孩向楼下卖包子的老板订购了5000个包子.包子铺老板非常实在,一下就全部都做出来了
def func(): l1=[] for i in range(1,5001): l1.append(f'{i}号包子') return l1ret=func()print(ret)
- 这样做没有问题,但是我们由于学生没有那么多,只吃了2000个左右,剩下的8000个,就只能占着一定的空间,放在一边了。如果包子铺老板效率够高,我吃一个包子,你做一个包子,那么这就不会占用太多空间存储了,完美。(用生成器做)
def eat_func(): for i in range(1,5001): yield f'{i}号包子'ret=eat_func()for i in range(200): print(next(ret))
思考:这两者的区别:
第一种是直接把包子全部做出来,占用内存。
第二种是吃一个生产一个,非常的节省内存,而且还可以保留上次的位置。
4.yield from
- 比如我yield 一个列表,我不想返回一个列表,想返回列表里的内容,就用yield from,将这个列表变成了迭代器返回
#yield fromdef func(): l1=[1,2,3,4,5] yield l1ret=func()print(next(ret))#得到的是一个列表#但是我不希望是一个列表 ----(1)yield from (2)用*解包#(1)yield fromdef func(): l1=[1,2,3,4,5] yield from l1 #将l1这个列表变成了迭代器返回ret=func()print(next(ret))# (2)用*解包def func(): l1=[1,2,3,4,5] return l1ret=func()print(*ret)[1, 2, 3, 4, 5]11 2 3 4 5Process finished with exit code 0
2.列表推导式,生成器表达式
列表推导式:用一行代码构建一个比较复杂有规律的列表
列表推导式:
- 循环模式:[变量(加工后的变量) for 变量 in iterable]
- 筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
1,列表推导式练习题:
#需求:创建一个有规律的列表,如1-10#(1).常规方法:l1=[]for i in range(1,11): l1.append(i)print(l1)#(2).用列表推导式:l2=[i for i in range(1,11)]print(l2)#用列表推导式(两种模式)#1.循环模式#需求1:将10以内所有整数的平方写入列表l3=[i**2 for i in range(1,11)]print(l3)#需求2:将100以内所有偶数写入列表l4=[i for i in range(2,101,2) ]print(l4)#需求3:从python1期到python100期写入列表l5=[f'python{i}期' for i in range(1,101)]print(l5)#2.筛选模式 (在循环模式后面加上 if条件)#需求1:将100以内所有偶数写入列表l6=[i for i in range(1,101) if i%2 == 0]print(l6)#需求2:将30以内能被3整取的数写入列表l7=[i for i in range(1,31) if i%3 ==0]print(l7)#需求3:过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母l8 = ['wusir', 'laonanhai', 'aa', 'b', 'taibai']print([i.upper() for i in l8 if len(i)>3 ])#需求4:找到嵌套列表中名字含有两个‘e’的所有名字(有难度)names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]#传统方法:l9=[]for i in names: for name in i: if name.count('e')==2: l9.append(name)print(l9)#列表推导式:筛选模式l10=[name for i in names for name in i if name.count('e')==2]print(l10)
2,生成器表达式
生成器表达式和列表推导式的语法上一模一样,只是把[]换成()就行了。
比如将十以内所有数的平方放到一个生成器表达式中
l11=(i for i in range(1,11))print(l11)print(next(l11))print(next(l11))<generator object at 0x00000264FE056048>12Process finished with exit code 0
- 生成器表达式也可以进行筛选
# 获取1-100内能被3整除的数gen = (i for i in range(1,100) if i % 3 == 0)for num in gen: print(num)
3.生成器表达式和列表推导式区别:
- 列表推导式比较耗内存,所有数据一次性加载到内存。而.生成器表达式遵循迭代器协议,逐个产生元素。
- 得到的值不一样,列表推导式得到的是一个列表.生成器表达式获取的是一个生成器
- 列表推导式一目了然,生成器表达式只是一个内存地址
3,内置函数
- 函数就是以功能为导向,一个函数封装一个功能,那么Python将一些常用的功能(比如len)给我们封装成了一个一个的函数,供我们使用
- python 提供了68个内置函数
一带而过:all() any() bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int() iter() locals() next() oct() ord() pow() repr() round()重点讲解:abs() enumerate() filter() map() max() min() open() range() print() len() list() dict() str() float() reversed() set() sorted() sum() tuple() type() zip() dir() 未来会讲:classmethod() delattr() getattr() hasattr() issubclass() isinstance() object() property() setattr() staticmethod() super()
eval() —掌握
- 执行字符串类型的代码(外衣),并返回最终结果
#-----如将字符串类型转化为数字类型s1='1+3'print(s1)print(type(s1))print(eval(s1))print(type(eval(s1)))1+34Process finished with exit code 0
#-----如将字符串类型转化为字典类型s2='{"name":"mike"}'print(s2,type(s2))print(eval(s2),type(eval(s2))){"name":"mike"} {'name': 'mike'} Process finished with exit code 0
exec()
- 与eval几乎一样 ,执行字符串类型的代码
msg="""list=[]for i in range(10): list.append(i)print(list)"""print(msg)exec(msg)#**************************************************************************list=[]for i in range(10): list.append(i)print(list)[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Process finished with exit code 0
hash()
- hash:获取一个对象(可哈希对象:int,str,Bool,tuple)的哈希值。
- 加密算法需要哈希值
print(hash('123456'))4362549678203584146Process finished with exit code 0
#使用python 进行 MD5加密接口测试:import hashlibpassword='123456'obj=hashlib.md5(password.encode())passwordmd5=obj.hexdigest()print(passwordmd5)e10adc3949ba59abbe56e057f20f883eProcess finished with exit code 0
help() —掌握
- help:函数用于查看函数或模块用途的详细说明。
print(help(str.split))Help on method_descriptor:split(...) S.split(sep=None, maxsplit=-1) -> list of strings Return a list of the words in S, using sep as the delimiter string. If maxsplit is given, at most maxsplit splits are done. If sep is not specified or is None, any whitespace string is a separator and empty strings are removed from the result.NoneProcess finished with exit code 0
callable() —重点
- callable:函数用于检查一个对象是否是可调用的。如果返回True,object仍然可能调用失败;但如果返回False,调用对象ojbect绝对不会成功。
s1='12332312'def func(): passprint(callable(s1))print(callable(func))FalseTrueProcess finished with exit code 0
bytes() —重点
把字符串转换成bytes(字节)类型
#bytess1='太白'b=s1.encode('utf-8')print(b)#或者用bytes将字符串转换为字节b=bytes(s1,encoding='utf-8')print(b)# 将字节转换成字符串c= str(b,encoding='utf-8')print(c)太白b'\xe5\xa4\xaa\xe7\x99\xbd'b'\xe5\xa4\xaa\xe7\x99\xbd'Process finished with exit code 0
print() 屏幕输出
int():pass
str():pass
bool():pass
set(): pass
**list() **
将一个可迭代对象转换成列表
l1='qweqeqweqweqweaaddff'l2=list(l1)print(l2)['q', 'w', 'e', 'q', 'e', 'q', 'w', 'e', 'q', 'w', 'e', 'q', 'w', 'e', 'a', 'a', 'd', 'd', 'f', 'f']Process finished with exit code 0
**tuple() **
将一个可迭代对象转换成元组
tu1 = tuple('abcd')print(tu1) # ('a', 'b', 'c', 'd')
**dict() **
通过相应的方式创建字典
#dict#创建字典的方式#1.直接创建dic=dict([(1,'one'),(2,'two')])print(dic)#2.元组的解构dic=dict(one=1,twe=2)print(dic)#3.fromkeys#4.update#5.字典的推导式{1: 'one', 2: 'two'}{'one': 1, 'twe': 2}Process finished with exit code 0
**abs() **
- 返回绝对值
i = -5print(abs(i)) # 5
reversed()与reverse() —重点
**reversed() ** 将一个序列翻转, 返回翻转序列的迭代器 —
reverse() 对原列表进行倒序。翻转
l1=[i for i in range(10)]print(l1)l1.reverse() # 对原列表进行倒序。翻转print(l1)l2=[i for i in range(10)]print(l2)obj=reversed(l2)print(obj)print(next(obj))print(list(obj))[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][9, 8, 7, 6, 5, 4, 3, 2, 1, 0][0, 1, 2, 3, 4, 5, 6, 7, 8, 9]9[8, 7, 6, 5, 4, 3, 2, 1, 0]Process finished with exit code 0
zip() 拉链方法 —重点
函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元祖组成的内容,如果各个迭代器的元素个数不一致,则按照长度最短的返回
lst1 = [1,2,3]lst2 = ['a','b','c','d']lst3 = (11,12,13,14,15)obj=zip(lst1,lst2,lst3)print(obj)for i in zip(lst1,lst2,lst3): print(i) (1, 'a', 11)(2, 'b', 12)(3, 'c', 13)Process finished with exit code 0
以下方法最最要:min( ),max()
l1=[33,2,1,54,7,-1,-9]print(min(l1))#以绝对值的方式取最小值#(1)第一种方法l2=[]def func(a): return abs(a)for i in l1: l2.append(func(i))print(l2)print(min(l2))
#(2)第二种方法l1=[33,2,1,54,7,-1,-9]#print(min(l1,key=abs))def abss(a): return abs(a)''' 第一次:a = 33 以绝对值取最小值 33 第二次:a = 2 以绝对值取最小值 2 第三次:a = 3 以绝对值取最小值 2 ...... 第六次:a = -1 以绝对值取最小值 1'''print(min(l1,key=abss))#凡是可以加key的:它会自动的将可迭代对象中的每个元素按照顺序传入key对应的函数中1Process finished with exit code 0
练习题:
dic={'a':3, 'b':2, 'c':1}#求出值最小的键值print(min(dic)) #a min默认会按照字典的键去比较大小。## def func(i):# return dic[i]# print(min(dic,key=func))#优化下函数--使用匿名函数print(min(dic,key=lambda i:dic[i]))acProcess finished with exit code 0
sorted() 排序函数
[ˈsɔːtɪd] sou 太d
排序函数, 不是对原列表进行排序,返回的是一个新列表,默认从低到高
l1=[22,33,1,2,8,7,6,5]l2=sorted(l1)print(l1)print(l2)[22, 33, 1, 2, 8, 7, 6, 5][1, 2, 5, 6, 7, 8, 22, 33]Process finished with exit code 0
l2=[('太白',18),('alex',73),('wu',35),('口天吴',41)]print(sorted(l2))#想按照成绩去排序print(sorted(l2,key=lambda x:x[1])) #返回的是一个列表 ,默认从低到高print(sorted(l2,key=lambda x:x[1],reverse=True)) #设置从高到底[('alex', 73), ('wu', 35), ('口天吴', 41), ('太白', 18)][('太白', 18), ('wu', 35), ('口天吴', 41), ('alex', 73)][('alex', 73), ('口天吴', 41), ('wu', 35), ('太白', 18)]Process finished with exit code 0
filter()
筛选过滤(类似于列表推导式的帅选模式) 返回的是迭代器
语法: filter(function,iterable)function: 用来筛选的函数,在filter中会自动的把iterable中的元素传递给function,然后根据function返回的True或者False来判断是否保留此项数据iterable:可迭代对象
# filter 筛选过滤l1=[2,3,4,1,6,7,8]#把小于3的元素剔除#(1)列表推导式筛选print([i for i in l1 if i>3]) #返回的是列表#(2)使用 filterret=filter(lambda x:x>3,l1) #返回的是迭代器print(ret)print(list(ret))[4, 6, 7, 8][4, 6, 7, 8]Process finished with exit code 0
map()
- 会根据提供的函数对指定的序列做映射。(相当于列表推导式的循环模式)
- 通俗地讲就是以参数序列中的每个元素分别调用参数中的函数(func()),把每次调用后返回的结果保存到返回值中
映射函数语法: map(function,iterable) 可以对可迭代对象中的每一个元素进映射,分别取执行function>>> def square(x):>>> return x ** 2>>> map(square,[1,2,3,4,5])
#map()#计算列表中每个元素的平方,返回新列表lst = [1,2,3,4,5]#(1)列表推导式的循环模式print([i**2 for i in lst])#(2)map()ret=map(lambda x:x**2,lst)print(ret)print(list(ret))[1, 4, 9, 16, 25]
4、匿名函数
语法:
函数名 = lambda 参数:返回值fun1=lambda a,b:a+b
- 此函数不是没有名字,他是有名字的,他的名字就是你给其设置的变量,比如func.
- lambda 是定义匿名函数的关键字,相当于函数的def.
- lambda 后面直接加形参,形参加多少都可以,只要用逗号隔开就行。
- 返回值在冒号之后设置,返回值和正常的函数一样,可以是任意数据类型。
- 匿名函数不管多复杂.只能写一行.且逻辑结束后直接返回数据
def func(a,b): return a+b#构建匿名函数fun1=lambda a,b:a+bprint(fun1(1,2))
接下来做几个匿名函数的小题:
写匿名函数:接收一个可切片的数据,返回索引为0与2的对应的元素(元组形式)。
fun2=lambda a:(a[0],a[2])print(fun2([22,33,44,55]))(22, 44)Process finished with exit code 0
5、闭包5.1闭包的定义:
闭包是嵌套在函数中的函数。
闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
闭包的现象:
被引用的非全局变量也称作自由变量,这个自由变量会与内层函数产生一个绑定关系。
自由变量不会再内存中消失。
'''由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包。给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求。例如:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。比如大众推出了一款新车:小白轿车。第一天价格为:100000元,平均收盘价:100000元第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元........'''# 封闭的东西: 保证数据的安全。# 方案一:# l1=[] #全局变量# def make_averager(new_value):# l1.append(new_value)# total=sum(l1)# average=total/len(l1)# return average## print(make_averager(100000))# print(make_averager(110000))# print(make_averager(120000))# 方案二: 数据安全,l1不能是全局变量。# def make_averager(new_value):# l1 = []# l1.append(new_value)# total = sum(l1)# averager = total/len(l1)# return averager# print(make_averager(100000))# print(make_averager(110000))# 此方案不行 因为每次执行的时候,l1列表都会重新赋值成空列表[]# 方案三: 闭包def make_averager(): l1 = [] def averager(new_value): l1.append(new_value) total = sum(l1) averager1 = total/len(l1) return averager1 return averageravg=make_averager() #avg这个变量得到是 averager这个函数名print(avg(100000)) #相当于执行---averager(100000)print(avg(110000))#思考:avg=make_averager() 这句代码执行完,理论上l1这个列表消失了,可是为什么没有消失?#因为产生了闭包100000.0105000.0Process finished with exit code 0
5.2闭包的作用:
- 保存局部信息不被销毁,保证数据的安全性
5.3如何判断一个嵌套函数是不是闭包
- 1,闭包只能存在嵌套函数中
- 2, 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 例一:def wrapper(): a = 1 def inner(): print(a) return innerret = wrapper()#例一是闭包# 例二:a = 2def wrapper(): def inner(): print(a) return innerret = wrapper()#例二不是闭包
# 例三:def wrapper(a,b): def inner(): print(a) print(b) return innera = 2b = 3ret = wrapper(a,b)#也是闭包
5.4如何用代码判断闭包?
- 查看这个函数有没有自由变量就行了
# 函数名.__code__.co_freevars 查看函数的自由变量print(avg.__code__.co_freevars) # ('series',)# 函数名.__code__.co_varnames 查看函数的局部变量print(avg.__code__.co_varnames) # ('new_value', 'total')# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。# (,)# cell_contents 自由变量具体的值print(avg.__closure__[0].cell_contents) # []
上面例三最难判断是不是闭包,为了进一步确认,用代码判断闭包
# 例三:def wrapper(a,b): def inner(): print(a) print(b) return innera = 2b = 3ret = wrapper(a,b)#如何用代码判断闭包?print(ret.__code__.co_freevars)('a', 'b')Process finished with exit code 0
5.5闭包的应用:
- 可以保存一些非全局变量但是不易被销毁、改变的数据。
- 装饰器。
5.6闭包面试题怎么问?
- 什么是闭包? 闭包有什么作用。
day14:1、开放封闭原则:
装饰器:装饰,装修,房子就可以住,如果装修,不影响你住,而且体验更加,让你生活中增加了很多功能:洗澡,看电视,沙发。器:工具。开放封闭原则:开放:对代码的拓展开放的, 更新地图,加新枪,等等。封闭:对源码的修改是封闭的。闪躲用q。就是一个功能,一个函数。 别人赤手空拳打你,用机枪扫你,扔雷.....这个功能不会改变。装饰器:完全遵循开放封闭原则。所以装饰器最终最完美的定义就是: 在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。装饰器就是一个函数。(装饰器就是一个闭包)
2、装饰器的初识:
版本一: Mike 写一些代码,测试一下index()函数的执行效率。
#import time#print(time.time()) # 格林威治时间。def index(): '''有很多代码.....''' time.sleep(2) # 模拟的网络延迟或者代码效率 print('欢迎登录博客园首页')def dariy(): '''有很多代码.....''' time.sleep(3) # 模拟的网络延迟或者代码效率 print('欢迎登录日记页面')start_time=time.time()index()end_time=time.time()print(end_time-start_time)start_time=time.time()dariy()end_time=time.time()print(end_time-start_time)欢迎登录博客园首页2.0008764266967773欢迎登录日记页面3.0002834796905518Process finished with exit code 0
版本一的问题:如果测试别人的代码,必须重新复制粘贴。
版本二:利用函数,解决代码重复使用的问题(解决版本一问题)
#版本二:利用函数,解决代码重复使用的问题(解决版本一问题)import timedef index(): '''有很多代码.....''' time.sleep(2) # 模拟的网络延迟或者代码效率 print('欢迎登录博客园首页')def dariy(): '''有很多代码.....''' time.sleep(3) # 模拟的网络延迟或者代码效率 print('欢迎登录日记页面')def timmer(f): start_time = time.time() f() end_time = time.time() print(f'测试本函数的执行效率:{end_time-start_time}')timmer(index)#版本二还是有问题: 原来index函数源码没有变化,给原函数添加了一个新的功能测试原函数的执行效率的功能。#满足开放封闭原则么?原函数的调用方式改变了。欢迎登录博客园首页测试本函数的执行效率:2.0008556842803955Process finished with exit code 0
版本三:不能改变原函数的调用方式。
#版本三:不能改变原函数的调用方式。import timedef index(): '''有很多代码.....''' time.sleep(2) # 模拟的网络延迟或者代码效率 print('欢迎登录博客园首页')def timmer(f): # f = index (funciton index123) def inner(): # inner :(funciton inner123) start_time = time.time() f() #index() (funciton index123) end_time = time.time() print(f'测试本函数的执行效率:{end_time-start_time}') return inner # (funciton inner123)# ret=timmer(index)# ret() #相当于inner()index=timmer(index) # inner (funciton inner123)index() # inner()
版本四:具体研究(装饰器的本质就是闭包)
import timedef index(): '''有很多代码.....''' time.sleep(2) # 模拟的网络延迟或者代码效率 print('欢迎登录博客园首页')def timmer(f): f = index # f = def inner(): start_time = time.time() f() end_time = time.time() print(f'测试本函数的执行效率{end_time-start_time}') return innerindex = timmer(index)index()
版本五:python做了一个优化;提出了一个语法糖的概念。 标准版的装饰器
(@timmer #等于index = timmer(index))
#版本五:python做了一个优化;提出了一个语法糖的概念。 标准版的装饰器import time#timmer装饰器def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print(f'测试本函数的执行效率:{end_time-start_time}') return inner@timmer #等于index = timmer(index)def index(): '''有很多代码.....''' time.sleep(2) # 模拟的网络延迟或者代码效率 print('欢迎登录博客园首页')@timmerdef dariy(): '''有很多代码.....''' time.sleep(3) # 模拟的网络延迟或者代码效率 print('欢迎登录日记页面')# index = timmer(index)# index()## dariy = timmer(dariy)# dariy()index()dariy()
版本六:被装饰函数带返回值
#版本六:被装饰函数带返回值import time#timmer装饰器def timmer(f): def inner(): start_time = time.time() r=f() #print(f'这个是f():{f()}!!!') end_time = time.time() print(f'测试本函数的执行效率:{end_time-start_time}') return r return inner@timmer #等于index = timmer(index)def index(): '''有很多代码.....''' time.sleep(0.6) # 模拟的网络延迟或者代码效率 print('欢迎登录博客园首页') return 666# 加上装饰器不应该改变原函数的返回值,所以666 应该返回给我下面的ret,# 但是下面的这个ret实际接收的是inner函数的返回值,而666返回给的是装饰器里面的# f() 也就是 r,我们现在要解决的问题就是将r给inner的返回值。ret=index()print(ret)欢迎登录博客园首页测试本函数的执行效率:0.6006889343261719666Process finished with exit code 0
版本七:被装饰函数带参数
#版本七:被装饰函数带参数import time#timmer装饰器def timmer(f): def inner(*args,**kwargs): # 函数的定义:* 聚合 args = ('李舒淇',18) start_time = time.time() r=f(*args,**kwargs) # 函数的执行:* 打散:f(*args) --> f(*('李舒淇',18)) --> f('李舒淇',18) #print(f'这个是f():{f()}!!!') end_time = time.time() print(f'测试本函数的执行效率:{end_time-start_time}') return r return inner@timmerdef index(name): '''有很多代码.....''' time.sleep(0.6) # 模拟的网络延迟或者代码效率 print(f'欢迎{name}登录博客园首页') return 666@timmerdef dariy(name,age): '''有很多代码.....''' time.sleep(0.5) # 模拟的网络延迟或者代码效率 print(f'欢迎{age}岁{name}登录日记页面')dariy('李舒淇',18) # inner('李舒淇',18)index('吴亦凡') #相当于 inner('吴亦凡')欢迎18岁李舒淇登录日记页面测试本函数的执行效率:0.5007114410400391欢迎吴亦凡登录博客园首页测试本函数的执行效率:0.6006894111633301Process finished with exit code 0
标准版的装饰器;def wrapper(f): def inner(*args,**kwargs): # 函数的定义:* 聚合 args = ('李舒淇',18) '''添加额外的功能:执行被装饰函数之前的操作''' ret = f(*args,**kwargs) #* 打散:f(*args) --> f(*('李舒淇',18)) --> f('李舒淇',18) ''''添加额外的功能:执行被装饰函数之后的操作''' return ret return inner
3、装饰器的执行过程(步骤详解)
- 图片详解:
- 文字详解:
1.第一步执行@wrapper装饰器,相当于执行 func=wrapper(func);2.第二步把被装饰函数的函数名当做参数,带入到装饰器参数,即f=func,执行装饰器;3.第三步返回 inner ,即wrapper(fun)的结果是得到 inner 即func=inner ;4.第三步执行func(),等同于执行inner(),所以会执行装饰器的的内嵌函数,先执行print('此处代码执行在被装饰函数调用之前'),然后执行被装饰函数func(),执行print('in func'),在执行print('此处代码执行在被装饰函数调用之后'),并且把被装饰函数的执行结果(print('in func'))返回给执行者func()
- 具体代码:
def wrapper(f): def inner(*args,**kwargs): print('此处代码执行在被装饰函数调用之前') ret=f(*args,**kwargs) print('此处代码执行在被装饰函数调用之后') return ret return inner@wrapper #相当于 func=wrapper(fun)def func(): print('in func')func()此处代码执行在被装饰函数调用之前in func此处代码执行在被装饰函数调用之后Process finished with exit code 0
4、python 中多个装饰器的执行顺序:
- 装饰器函数在被装饰函数定义好后立即执行。多个装饰器的调用顺序是自下往上的。
- 被装饰函数执行时,装饰器的执行顺序是从上往下的。
def wrapper1(f1): print('in wrapper1') def inner1(*args,**kwargs): print('in inner1') ret = f1(*args,**kwargs) return ret return inner1 def wrapper2(f2): print('in wrapper2') def inner2(*args,**kwargs): print('in inner2') ret = f2(*args,**kwargs) return ret return inner2 def wrapper3(f3): print('in wrapper3') def inner3(*args,**kwargs): print('in inner3') ret = f3(*args,**kwargs) return ret return inner3 @wrapper1@wrapper2@wrapper3def func(): print('in func')func()# in wrapper3# in wrapper2# in wrapper1# in inner1# in inner2# in inner3# in func#装饰器函数在被装饰函数定义好后立即执行。多个装饰器的调用顺序是自下往上的。# 被装饰函数执行时,装饰器的执行顺序是从上往下的。
分析:
分析: func = wrapper3(func) ---> 先打印'in wrapper3', func = inner3 ,f3 = func func = wrapper2(func) --->即 inner3 = wrapper2(inner3) ---> 先打印'in wrapper2', inner3 = inner2 ,f2 = inner3 --> func = inner2 func = wrapper1(func) --->即 inner2 = wrapper1(inner2) --->先打印'in wrapper1', inner2 = inner1 ,f1 = inner2 --> func = inner1 执行 func() 即执行执行 inner1() ,先打印 'in inner1',再执行 f1(),即执行inner2() 即执行inner2() ,先打印 'in inner2',再执行 f2(),即执行inner3() 即执行inner3() ,先打印 'in inner3',再执行 f3(),即执行func(),打印 'in func '
5、装饰器的应用
# 装饰器的应用:登录认证# 这周的周末作业:模拟博客园登录的作业。装饰器的认证功能。
- 基本框架
def login(): passdef register(): passstatus_dict={ 'username':None, 'status':False}def auth(f): ''' 你的装饰器完成:访问被装饰函数之前,写一个三次登录认证的功能。 登录成功:让其访问被装饰得函数,登录没有成功,不让访问。 :param f: :return: ''' def inner(*args,**kwargs): '''访问函数之前的操作,功能''' ret = f(*args,**kwargs) '''访问函数之后的操作,功能''' return ret return innerdef article(): print('欢迎访问文章页面')def comment(): print('欢迎访问评论页面')def dariy(): print('欢迎访问日记页面')article()comment()dariy()
def login(): dic = {} username = input('请输入用户名').strip() password = input('请输入密码').strip() if username in register() and password == register()[username]: dic[username] = password print('登录成功') else: print('用户名或者密码错误') return dicdef register(): dic_1 = {} with open('a',encoding='utf-8') as f: for line in f: a,b = line.strip().split() dic_1[a] = b return dic_1status_dict = {'username':None,'status':False}def auth(f): def inner(*args,**kwargs): if status_dict['status']: ret = f(*args,**kwargs) return ret else: status_dict['username'] = login() status_dict['status'] = True ret = f(*args, **kwargs) return ret return inner
day:151、自定义模块:
什么是模块:本质就是.py文件,封装语句的最小单位。
- 多个模块对应包,在代码中包就是一个文件夹
自定义模块:实际上就是定义.py,其中可以包含:变量定义,可执行语句,for循环,函数定义等等,他们统称模块的成员。
2、模块的运行方式:
脚本方式:直接用解释器执行。或者PyCharm中右键运行。
模块方式:被其他的模块导入。为导入它的模块提供资源(变量,函数定义,类定义等)。
'''测试自定义模块的导入'''#自定义模块被其他模块导入时,其中的可执行语句会路基执行import a[2, 11, 15, 7]Process finished with exit code 0#*******************************************************#a.pynums=[2,11,15,7]def fun(nums): print(nums)fun(nums)
思考:自定义模块被其他模块导入时,其中的可执行语句会路基执行,如何控制可执行语句不执行?
—–python中提供一种可以判断自定义模块是属于开发阶段还是使用阶段:
__name__
就是通过 if __name__ == '__main__': 来实现 把可执行语句放在主函数main中执行, 自定义模块在右键运行时,print(__name__) 的属性值 __main__,但是在以模块方式被导入时,在其他脚本中右键运行,print(__name__) 的属性值就是模块名了 如a , 所以通过if __name__ == '__main__': 可以控制被调用的模块中的可执行语句不被执行。
3、__name__
属性的使用:
- 在脚本方式运行时(右键执行),
__name__
是固定的字符串:__main__
nums=[2,11,15,7]def fun(nums): print(nums)fun(nums)print(__name__)[2, 11, 15, 7]__main__Process finished with exit code 0
- 在以模块方式被导入时,
__name__
就是本模块的名字。 - 在自定义模块中对
__name__
进行判断,决定是否执行可执行语句:开发阶段,就执行,使用阶段就不执行。
#开发阶段--会执行nums=[2,11,15,7]def fun(nums): print(nums)#__name__if __name__ == '__main__': fun(nums)[2, 11, 15, 7]Process finished with exit code 0#**********************************************************#使用阶段(调用阶段)---不执行import a#右键执行Process finished with exit code 0
4、系统导入模块的路径
- 内存中:如果之前成功导入过某个模块,直接使用已经存在的模块
- 内置路径中:安装路径下:Lib
- PYTHONPATH:import时寻找模块的路径。
- sys.path:是一个路径的列表。
如果上面都找不到,就报错。
通过动态修改sys.path的方式将自定义模块添加到sys.path中。
os.path.dirname():获取某个路径的父路径。通常用于获取当前模块的相对路径
#使用相对路径找到aa文件夹#print(__file__) #当前文件的绝对路径#使用os模块获取一个路径的父路径#os.path.dirname()#添加 a.py所在的路径到 sys.path 中#sys.path.append()import sysimport ossys.path.append(os.path.dirname(__file__) + '/aa')
5、导入模块的多种方式:
- import xxx:导入一个模块的所有成员
- import aaa,bbb:一次性导入多个模块的成员。不推荐这种写法,分开写。
- from xxx import a:从某个模块中导入指定的成员。
- from xxx import a,b,c:从某个模块中导入多个成员。
- from xxx import *:从模块中导入所有成员。
5.1、import xxx 和 from xxx import * 的区别:
第一种方式在使用其中成员时,必须使用模块名作为前缀。不容易产生命名冲突。
第二种方式在使用其中成员时,不用使用模块名作为前缀,直接使用成员名即可。但是容易产生命名冲突。在后定义的成员生效(把前面的覆盖了。)
5.2、怎么解决名称冲突的问题?
- 改用import xxx这种方式导入。
- 自己避免使用同名
- 使用别名解决冲突
5.3、使用别名:alias
给成员起别名,避免名称冲突。
from my_module import age as a
给模块起别名,目的简化书写。
import my_module as m
5.4、from xxx import * 控制成员被导入
默认情况下,所有的成员都会被导入。
__all__
是一个列表,用于表示本模块可以被外界使用的成员。元素是成员名的字符串。
注意:
__all__
只是对from xxx import *这种导入方式生效。其余的方式都不生效。
5.5、相对导入
针对某个项目中的不同模块之间进行导入,称为相对导入。
只有一种格式:
from 相对路径 import xxx
相对路径:包含了点号的一个相对路径。
. 表示的是当前的路径。
..表示的是父路径。
…表示的是父路径的父路径。
# 相对导入同项目下的模块# from ..z import zz # 容易向外界暴露zz模块from ..z.zz import *
# 不使用相对导入的方式,导入本项目中的模块# 通过当前文件的路径找到z的路径import osimport syssys.path.append(os.path.dirname(os.path.dirname(__file__)) + '/z')from zz import *
6、内置模块random
此模块提供了和随机数获取相关的方法:
- random.random():获取 [0.0,1.0) 范围内的浮点数
- random.randint(a,b):获取 [a,b] 范围内的一个整数
- random.uniform(a,b):获取 [a,b) 范围内的浮点数
- random.shuffle(x):把参数指定的数据中的元素打乱。参数必须是一个可变的数据类型。
- random.sample(x,k):从x中随机抽取k个数据,组成一个列表返回。
import random#random.random():获取 **[0.0,1.0)** 范围内的浮点数l1=random.random()print(l1)#random.uniform(a,b):获取 **[a,b)** 范围内的浮点数l2=random.uniform(3,5)print(l2)#random.randint(a,b):获取 **[a,b]** 范围内的一个整数l3=random.randint(1,3)print(l3)#random.shuffle(x):把参数指定的数据中的元素打乱。参数必须是一个可变的数据类型。l4=list(range(10))print(l4)random.shuffle(l4)print(l4)#random.sample(x,k):从x中随机抽取k个数据,组成一个列表返回l5=(1,2,3)l6=random.sample(l5,2)print(l6)0.414109240959746664.7256436233679552[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][5, 4, 2, 6, 7, 8, 1, 0, 3, 9][1, 3]Process finished with exit code 0
time:和时间相关
封装了获取时间戳和字符串形式的时间的一些方法。
- time.time():获取时间戳
- time.gmtime([seconds]):获取格式化时间对象:是九个字段组成的
- time.localtime([seconds]):获取格式化时间对象:是九个字段组成的
- time.mktime(t):时间对象 -> 时间戳
- time.strftime(format[,t]):把时间对象格式化成字符串
- time.strptime(str,format):把时间字符串转换成时间对象
import time#(1)获取时间戳 #时间戳:从时间元年(1970 1 1 00:00:00)到现在经过的秒数。l1=time.time()print(l1)#(2)获取格式化时间对象:是九个字段组成的。print(time.gmtime()) #GMTprint(time.localtime())# 默认参数是当前系统时间的时间戳。print(time.gmtime(1)) # 时间元年过一秒后,对应的时间对象#(3)格式化时间对象 转换为 字符串s=time.strftime("year:%Y %m %d %H:%M:%S")print(s)#(4)把时间字符串 转换成 时间对象time_obj = time.strptime('2010 10 10','%Y %m %d')print(time_obj)#(5)时间对象 -> 时间戳t1 = time.localtime() # 时间对象t2 = time.mktime(t1) # 获取对应的时间戳print(t2)print(time.time())1663252449.9508088time.struct_time(tm_year=2022, tm_mon=9, tm_mday=15, tm_hour=14, tm_min=34, tm_sec=9, tm_wday=3, tm_yday=258, tm_isdst=0)time.struct_time(tm_year=2022, tm_mon=9, tm_mday=15, tm_hour=22, tm_min=34, tm_sec=9, tm_wday=3, tm_yday=258, tm_isdst=0)time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=1, tm_wday=3, tm_yday=1, tm_isdst=0)year:2022 09 15 22:34:09time.struct_time(tm_year=2010, tm_mon=10, tm_mday=10, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=283, tm_isdst=-1)1663252859.01663252859.9694235Process finished with exit code 0
time模块三大对象之间的转换关系:
其他方法:
- time.sleep(x):休眠x秒.
datetime:日期时间相关
封装了一些和日期,时间相关的类,主要有:
- date: 需要年,月,日三个参数
- time: 需要时,分,秒三个参数
- datetime: 需要年,月,日,时,分,秒六个参数.
- timedelta: 需要一个时间段.可以是天,秒,微秒
获取以上类型的对象,主要作用是和时间段进行数学运算.
timedelta可以和以下三个类进行数学运算:
- datetime.time,datetime.datetime,datetime.timedelta
import datetime#(1):date类: 需要年,月,日三个参数d=datetime.date(2010,10,10) #把年月日三个参数转换成一个对象print(d)#获取date对象的各个属性print(d.year)print(d.month)print(d.day)#(2):time类: 需要时,分,秒三个参数t=datetime.time(10,58,59)print(t)#获取time对象的各个属性print(t.hour)print(t.minute)print(t.second)#(3):datetime 类: 需要年,月,日,时,分,秒六个参数.dt=datetime.datetime(2022,9,15,23,58,59)print(dt)#获取datetime对象的各个属性#(4):timedelta类: 理解:时间的变化量 需要一个时间段.可以是天,秒,微秒td=datetime.timedelta(days=1)print(td)#参与数学运算#创建时间对象:l1=datetime.date(2010,10,10)res=l1+tdprint(res)2010-10-102010101010:58:591058592022-09-15 23:58:591 day, 0:00:002010-10-11Process finished with exit code 0
练习:
- 显示当前日期前三天是什么时间.
#练习:显示当前日期前三天是什么时间.year=int(input("输入年份:"))month=int(input("输入年份:"))day=int(input("输入年份:"))#创建指定年月日的date对象d=datetime.date(year,month,day)# 创建三天 的时间段td = datetime.timedelta(days=3)res = d - tdprint(res)输入年份:2020输入年份:10输入年份:42020-10-01Process finished with exit code 0
- 显示任意一年的二月份有多少天.
#练习:计算某一年的二月份有多少天?# 普通算法:根据年份计算是否是闰年.是:29天,否:28# 用datetime模块. # 首先创建出指定年份的3月1号.然后让它往前走一天.year=int(input("输入年份:"))# 创建指定年份的date对象d=datetime.date(year,3,1)# 创建一天 的时间段td = datetime.timedelta(days=1)res = d - tdprint(res.day)输入年份:201428Process finished with exit code 0
os:操作系统接口
此模块提供了灵活的和操作系统相关的函数.
1、删除:
- os.remove(file_path) : 删除文件
- os.rmdir(dir_path) : 删除空文件夹
- 删除非空文件夹使用另一个模块 : shutil
- shutil.rmtree(path)
- os.removedirs(name) : 递归删除空文件夹,先删除最里面的目录 然后继续删除父级
2、重命名
- os.rename(src, dst) : 文件,目录重命名,目标不能事先存在.
#os:操作系统接口import os#和文件操作相关(重命名,删除等)#删除#os.remove(r'a.txt')#删除目录,必须时空目录#os.removedirs('aa') #OSError: [WinError 145] 目录不是空的。: 'aa'#使用shutil模块可以删除待内容的目录import shutilshutil.rmtree('aa')#重命名#os.rename('a.txt','b.txt')
3、和路径相关的属性,更多相关的操作被封装在os.path这个模块中.
- os.curdir : 当前路径
- os.sep : 路径分隔符
- os.altsep : 备用的分隔符
- os.extsep : 扩展名分隔符
- os.pathsep : 路径分隔符
- os.linesep : 行分隔符,不要在写文件的时候,使用这个属性
os.path 模块
此模块实现了一些在路径操作上的方法.
os.path.dirname(path) : 返回一个路径中的父目录部分
如果只是一个盘符,或者是以路径分隔符结尾的字符串,则整体返回.
否则返回的是路径中的父目录部分.
样例:
import osprint(os.path.dirname('.')) # print(os.path.dirname('/aa/')) # /aa print(os.path.dirname('D:/test')) # D:/ print(os.path.dirname('D:/')) # D:/
os.path.basename(path) : 返回path指定的路径的最后一个内容.
如果只是一个盘符,或者是以路径分隔符结尾的字符串,则返回空;
否则返回的是路径中的最后一部分内容.
样例:
import os print(os.path.basename('.')) # . print(os.path.basename('/aa')) # aa print(os.path.basename('/aa/')) # print(os.path.basename('D:/test')) # test
os.path.split(path) : 把路径中的路径名和文件名切分开 ,结果是元祖
返回一个元组,第二个元素表示的是最后一部分的内容,第一个元素表示的是剩余的内容.
如果只是一个盘符或者是以路径分隔符结尾的字符串,则第二个元素为空.
否则第二个元素就是最后一部分的内容.
如果path中不包含路径分隔符,则第一个元素为空.
样例:
import os print(os.path.split('D:/')) # ('D:/', '') print(os.path.split('.')) # ('', '.') print(os.path.split('/aa')) # ('/', 'aa')
os.path.join(path,*paths) : 连接若干个路径为一个路径.
如果路径中有绝对路径,则在这个路径之前的路径都会被丢弃,而从这个路径开始往后拼接.
Windows中盘符一定要带,否则不认为是一个盘符.
样例:
res = os.path.join('aa','bb','cc')print(res) # aa\bb\cc res2 = os.path.join('D:/','test') print(res2) # D:/test
os.path.abspath(path) :返回一个路径的绝对路径.
如果参数路径是相对的路径,就把当前路径和参数路径的组合字符串当成结果返回.
如果参数路径已经是绝对路径,就直接把参数返回.
如果参数路径以/开始,则把当前盘符和参数路径连接起来组成字符串返回.
注意:
此方法只是简单的将一个拼接好的字符串返回,并不会去检查这个字符串表示的文件是否存在.
样例:
import os print(os.path.abspath('aa')) # D:\PycharmProjects\test03\test01\aaprint(os.path.abspath('D:/test/aa')) # D:\test\aa print(os.path.abspath('/bb')) # D:\bb
判断功能
os.path.exists(path) : 判断路径是否真正存在.
os.path.isabs(path) : 判断是否是绝对路径
os.path.isfile(path) : 判断是否是文件
os.path.isdir(path) : 判断是否是目录
sys:和python解释器相关
提供了解释器使用和维护的变量和函数.
sys.argv :当以脚本方式执行程序时,从命令行获取参数.
argv[0]表示的是当前正在执行的脚本名.argv[1]表示第一个参数,以此类推.
样例:
有脚本 test.py 内容如下:
import sys print('脚本名称:',sys.argv[0]) print('第一个参数是:',sys.argv[1]) print('第二个参数是:',sys.argv[2])
使用命令行方式运行该脚本: python test.py hello world
解释器去寻找模块的路径
sys.path :系统寻找模块的路径.可以通过PYTHONPATH来进行初始化.
由于是在程序执行的时候进行初始化的,所以,路径的第一项path[0]始终是调用解释器的脚本所在的路径.如果
是动态调用的脚本,或者是从标准输入读取到脚本命令,则path[0]是一个空字符串.程序中可以随时对这个路径
进行修改.以达到动态添加模块路径的目的.
json模块
JSON : JavaScript Object Notation ——Java脚本对象标记语言.
已经成为一种简单的数据交换格式.
序列化和反序列化
序列化 : 将其他数据格式转换成json字符串的过程.
反序列化 : 将json字符串转换其他数据类型的过程.
涉及到的方法:
json.dumps(obj) : 将obj转换成json字符串返回到内存中. (dumps是从内存到内存)
json.dump(obj,fp) : 将obj转换成json字符串并保存在fp指向的文件中.
json.loads(s) : 将内存中的json字符串转换成对应的数据类型对象
json.load(f) : 从文件中读取json字符串,并转换回原来的数据类型.
注意:
json并不能序列化所有的数据类型:例如:set.
元组数据类型经过json序列化后,变成列表数据类型.
json文件通常是一次性写入,一次性读取.但是可以利用文件本身的方式实现:一行存储一个序列化json字符串,在反序列化时,按行反序列化即可.
内存中的数据:结构化的数据:
磁盘上的数据:线性数据:
序列化比喻:
以下是Python中的可以被序列化的数据类型:
补充:写入文件必须要字符串
#t---text#b---binary 字节#a---append 追加写#w---write 覆盖写#r---read#默认是 rt#写入文件必须要字符串with open('a.txt',mode='wt',encoding='utf-8')as f : f.write(10) TypeError: write() argument must be str, not intProcess finished with exit code 1
序列化案例:
1、考察 json.dumps
import jsons=json.dumps([1,2,3]) #把指定的对象转换成json格式的字符串,并保存在内存中print(type(s))print(s)s2=json.dumps((1,2,3)) #元祖序列化后变成列表print(type(s2))print(s2)s3=json.dumps(10)print(type(s3))print(s3)s4=json.dumps({'name':'andy','age':10})print(type(s4))print(s4)[1, 2, 3][1, 2, 3]10{"name": "andy", "age": 10}Process finished with exit code 0
2、考察 json.dump—–将json 结果写入文件中
#将json 结果写入文件中with open('a.txt',mode='at',encoding='utf-8')as f: json.dump([1,2,3],f)
反序列化案例:
3、考察json.loads
#反序列化s1=json.dumps([1,2,3])s2=json.loads(s1)print(type(s2))print(s2)[1, 2, 3]Process finished with exit code 0
4、考察 json.load
#如何从文件中反序列化with open('a.txt',encoding='utf-8')as f: res=json.load(f) print(type(res)) print(res) [1, 2, 3]Process finished with exit code 0
pickle 序列化
python专用的序列化模块. (屁寇)
pickle: 将python中所有的数据类型,转化成字节串 序列化过程 将字节串,转化成python中的数据类型 反序列化过程
pickle模块中的方法基本和json模块中的方法一样.只是适用的数据类型范围比json更广泛.
将其他数据类型序列化成字节
将字节反序列化成其他数据类型
将其他数据类型序列化成字节并写入文件
从文件中读取字节并反序列化成其他数据类型
区别在于:
json: 1.不是所有的数据类型都可以序列化.结果是字符串. 2.不能多次对同一个文件序列化. 3.json数据可以跨语言 pickle: 1.所有python类型都能序列化,结果是字节串. 2.可以多次对同一个文件序列化 3.不能跨语言.
案例:
1.在内存中序列化和反序列化
import pickle#序列化列表类型bys=pickle.dumps([1,2,3])print(type(bys)) #print(bys) #b'\x80\x03]q\x00(K\x01K\x02K\x03e.'#序列化元祖类型s1=pickle.dumps((1,2,3))print(s1) #b'\x80\x03K\x01K\x02K\x03\x87q\x00.'#反序列化后,保存了元祖的数据类型 (json不行,json反序列化元祖后,类型是列表)res=pickle.loads(s1)print(type(res)) ##序列化集合类型s2=pickle.dumps(set('abc'))res=pickle.loads(s2)print(type(res)) #
2、在文件中序列化和反序列化
#把pickle 序列化内容写入文件中with open('c.txt',mode='wb')as f: pickle.dump([1,2,3,4],f)#从文件中反序列化pickle数据with open('c.txt',mode='rb')as f: q=pickle.load(f) print(type(q)) print(q) [1, 2, 3, 4]Process finished with exit code 0
**hashlib: **加密模块
封装一些用于加密的类.
md5()等
import hashlibprint(dir(hashlib))['md5', 'new', 'pbkdf2_hmac', 'sha1', 'sha224', 'sha256', 'sha384', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'sha512', 'shake_128', 'shake_256']
加密的目的:用于判断和验证,而并非解密.
特点:
- 把一个大的数据,切分成不同块,分别对不同的块进行加密,再汇总的结果,和直接对整体数据加密的结果是一致的.
- 单向加密,不可逆.
- 原始数据的一点小的变化,将导致结果的非常大的差异,’雪崩’效应.
使用摘要算法加密一个数据的三大步骤:
获取一个加密算法对象.
调用加密对象的update方法给指定的数据加密.
调用加密对象的digest或者是hexdigest获取加密后的结果.
样例:
import hashlib#获取一个加密对象m=hashlib.md5()#使用加密对象的update,进行加密m.update('abc中文'.encode('utf-8')) # 对参数进行加密,参数必须是字节类型#或 m.update(b'abc')# 通过hexdigest获取加密后的字符串res=m.hexdigest()print(res)# 字节形式的结果print(m.digest())1af98e0571f7a24468a85f91b908d335b'\x1a\xf9\x8e\x05q\xf7\xa2Dh\xa8_\x91\xb9\x08\xd35'Process finished with exit code 0
简化写法
加密对象的update方法可以调用多次,意味着在在前一次的update结果之上,再次进行加密.如果只是一次更新的话,
还可以直接把数据当成参数传递给构造方法.
例如:
m = md5() m.update(data) res = m.hexdigest()
和下面的语句是等价的:
res = md5(data).hexdigest()
这种在创建加密对象的时候,就指定初始化的数据,称为 salt (盐).
目的就是为了让加密的结果更加复杂.
练习:
需求:把用户名和密码信息加密后,通过序列化的方式存储到本地文件中.并通过控制台输入信息进行验证.
import hashlib#注册,登录程序:#加密方法def get_md5(username,password): m=hashlib.md5() m.update(username.encode('utf-8')) m.update(password.encode('utf-8')) return m.hexdigest()#注册方法def register(username,password): #加密 res=get_md5(username,password) #写入文件 with open('login',mode='at',encoding='utf-8') as f : f.write(res) f.write('\n')#登录方法:def login(username,password): # 获取当前登录信息的加密结果 res = get_md5(username, password) # 读文件,和其中的数据进行对比 with open('login', mode='rt', encoding='utf-8') as f: for line in f: if res == line.strip(): return True else: return False while True: op = int(input("1.注册 2.登录 3.退出 ----请输入:")) if op == 3: break elif op == 1 : username=input("请输入用户名:") password=input("请输入密码:") register(username,password) elif op == 2 : username=input("请输入用户名:") password=input("请输入密码:") res=login(username,password) if res: print('登录成功') else: print('登录失败')
day20:1、递归函数
- 递归函数:在一个函数里在调用这个函数本身
- 递归的最大深度:998
def foo(n): print(n) n += 1 foo(n)foo(1)998Traceback (most recent call last):
面试题:可以修改递归函数的最大深度吗?–可以
import syssys.setrecursionlimit(10000)
看电脑性能,最大深度
1.1、递归示例讲解
例一:现在你们问我,alex老师多大了?我说我不告诉你,但alex比 egon 大两岁。你想知道alex多大,你是不是还得去问egon?egon说,我也不告诉你,但我比武sir大两岁。你又问武sir,武sir也不告诉你,他说他比太白大两岁。那你问太白,太白告诉你,他18了。这个时候你是不是就知道了?alex多大?
def age(n): if n == 1: return 18 else: return age(n-1)+2print(age(4))24Process finished with exit code 0
day:22 面向对象一:创建\调用类和类实例化
1.类名()—-会自动调用类中的 init 方法
2.类中直接写个打印语句,不用实例化类对象,直接运行可以打印
class Person: print(123) def __init__(self): print('-'*20) self.name='mike' print('*' * 20) print(456) 123456Process finished with exit code 0
3、类的实例化
其实实例化一个对象总共发生了三件事:
1,在内存中开辟了一个对象空间。
2,自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
3,在__init__ 方法中通过self给对象空间添加属性。
alex=Person() #alex就是对象,Alex=Person()的过程,是通过类获取一个对象的过程,叫类的实例化
4、类调用执行 init 方法的过程
class Person: print(123) def __init__(self): print('-'*20) self.name='mike' self.sex='不详' self.job='搓澡工' self.level=0 self.hp=250 self.weapon='搓澡巾' self.ad=1 print('*' * 20) print(456)alex=Person() #alex就是对象,Alex=Person()的过程,是通过类获取一个对象的过程,叫类的实例化123456--------------------********************Process finished with exit code 0
- 图片解析
5、类和对象之间的关系?
- 类 是一个大范围,是一个模子,它约束了事务有哪些属性,但是不能约束具体的值
- 对象 是一个具体的内容,是模子的产物,它遵循了类的约束,同时给属性赋上具体的值
6、查看类中的所有内容
类名.__dict__方式
class Person: def __init__(self,name,sex,job,hp,weapon,ad,): print('-'*20) self.name=name self.sex=sex self.job=job self.level=0 self.hp=hp self.weapon=weapon self.ad=ad print('*' * 20) print(self.__dict__)alex=Person('alex','不详','搓澡工',250,'搓澡巾',1) print(alex.__dict__)print(Person.__dict__)--------------------********************{'name': 'alex', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}{'name': 'alex', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}{'__module__': '__main__', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}Process finished with exit code 0
7、self 是什么?
类有一个空间,存储的是定义在class中所有名字
每一个对象又拥有自己的空间,通过对象名.__dict__方法就可以查看这个对象的属性和值
类中的方法一般都是通过对象执行的(除去类方法,静态方法外),并且对象执行这些方法都会自动将对象空间传给方法中的第一个参数self.
所以self 是什么?
self其实就是类中方法(函数)的第一个位置参数,只不过解释器会自动将调用这个函数的对象传给self。所以咱们把类中的方法的第一个参数约定俗成设置成self, 代表这个就是对象。
8、实例化所经历的过程
- 1.类名()之后的第一件事:开辟一块内存空间,用来存放对象
- 2.调用__init__方法(或者类中其他方法),把空间的内存地址作为self参数,传递到函数内部
- 3.所有的着一个对象需要使用的属性都需要和self关联起来
- 4.执行完init中的逻辑后,self变量会自动的被返回到调用处(发送实例化的地方,方便这个类实例化下一个对象)
对象查找属性的顺序:先从对象空间找 ——> 类空间找 ——> 父类空间找 ——->…..
类名查找属性的顺序:先从本类空间找 ——-> 父类空间找——–> ……..
9、属性的增,删,改,查
修改 列表/字典/对象 中 的某一个值,都不会影响这个 列表/字典/对象 所在的内存空间地址
class Person: def __init__(self,name,sex,job,hp,weapon,ad,): print('-'*20) self.name=name self.sex=sex self.job=job self.level=0 self.hp=hp self.weapon=weapon self.ad=ad print('*' * 20) alex=Person('alex','不详','搓澡工',250,'搓澡巾',1) #属性的增加alex.momey=1000#属性的删除del alex.money#属性的修改alex.name='qweewqew'#属性的查看print(alex.name)
二、类中定义方法和调用方法1、案例:人狗大战
需求:人搓狗,狗掉血,狗掉的血是人的攻击力狗舔人,人掉血,人掉的血是狗的攻击力
class Person: def __init__(self,name,sex,job,hp,weapon,ad,): self.name=name self.hp=hp self.ad=ad def 搓(self,dog): #人搓狗,狗掉血,狗掉的学是人的攻击力 dog.dog_hp=dog.dog_hp-self.ad print('{}给{}搓了澡,{}掉了{}点血,{}当前血量是{}'.format(self.name,dog.dog_name,dog.dog_name,self.ad,dog.dog_name,dog.dog_hp))class Dog: def __init__(self,name,blood,ad,kind): self.dog_name = name self.dog_hp=blood self.dog_ad=ad def 舔(self,person): #狗舔人,人掉血 人掉的血是狗的攻击力 if person.hp >= self.dog_ad : person.hp=person.hp-self.dog_ad else: person.hp=0 print('{}舔了{},{}掉了{}点血,{}当前血量是{}'.format(self.dog_name,person.name,person.name,self.dog_ad,person.name,person.hp))alex=Person('alex','不详','搓澡工',250,'搓澡巾',1)小白=Dog('小白',5000,249,'柴犬')#人攻击狗alex.搓(小白)#狗攻击人小白.舔(alex)小白.舔(alex)alex给小白搓了澡,小白掉了1点血,小白当前血量是4999小白舔了alex,alex掉了249点血,alex当前血量是1小白舔了alex,alex掉了249点血,alex当前血量是0Process finished with exit code 0
2、当通过 类名.方法名() 调用类的方法时,会报错,要穿一个参数进入才行
class A: country='中国' def __init__(self): pass def fun1(self): print(11)A.fun1()TypeError: fun1() missing 1 required positional argument: 'self'Process finished with exit code 1
class A: country='中国' def __init__(self): pass def fun1(self): print(11)A.fun1(2)11Process finished with exit code 0
3、所以通常我们 先实例化一个对象,用对象.方法名 去调用就不会报错
#类:class A: country='中国' def __init__(self): pass def fun1(self): print(11)obj=A()obj.fun1() #=A.func1(a)11Process finished with exit code 0
4、思考:类和对象的命名空间讲解
print(obj.country) 和 print(A.country) 的区别?---为什么 print(obj.country) 打印的是'印度';而 print(A.country) 打印的是 '中国'?前置说明:创建类的时候会创建一块类的空间,用来存放 类里面的属性,方法,和init方法 等等;实例化类的对象时,会创建一块对象的空间,存放一个类指针(用来指对应的类空间地址),然后对象名作为init方法的第一个位置参数(self),传递给init方法,init方法通过self给对象的空间添加属性(即name='mike',age=83,country='印度')。 解答;print(obj.country)先在obj对象的空间中找有没有country属性,结果有,并且是'印度',所以打印'印度'---(如果对象的空间中没有country属性,那在去类的空间去找,这时候打印的就是'中国'了)print(A.country) 先在A类的空间中找有没有country属性,结果有,并且是'中国',所以打印'中国'
class A: country='中国' def __init__(self,name,age,q): self.name=name self.age=age self.country=qobj=A('mike',83,'印度')print(obj.country)print(A.country)印度中国Process finished with exit code 0
- 图片解析:
- 拓展:打印结果是?
class A: country='中国' def __init__(self,name,age,q): self.name=name self.age=age self.country=qa=A('mike',83,'印度')b=A('wusir',73,'日本')a.country='泰国'print(a.country)print(b.country)print(A.country)泰国日本中国Process finished with exit code 0
5、总结
类中的变量是静态变量
对象中的变量只输入对象本身,每个对象有属于自己的空间来存放对象的变量
当使用对象名去调用某一个属性的时候会优先在自己的空间中寻找,找不到再去对应的类中寻找
如果自己没有就去类空间去找,如果类也没有就报错
对于类来说,类中的变量,所有的对象都是可以读取的,并且读取的是同一个变量
三、组合:一个类的对象是另一个类对象的属性
- 对象变成一个属性
- 或者说对象变成另一个对象的参数了
#组合:一个类的对象是另一个类对象店的属性#需求:想查看大壮所在班级的开班日期是多少?#学生类 #属性:姓名,性别,年龄,学号,班级,手机号#班级信息类 #班级名字 #开班时间 #当前讲师
class student: def __init__(self,name,sex,age,number,class_name,phone): self.name=name self.sex=sex self.age = age self.number = number self.class_name = class_name self.phone = phoneclass banji: def __init__(self,cname,ctime,teacher): self.cname=cname self.ctime = ctime self.teacher = teacherpy22=banji('python全站22期','2019-4-26','小白')py23=banji('python全站23期','2019-5-26','宝元')大壮=student('大壮','male',23,10086,py22,13812345678)雪飞=student('雪飞','male',18,10088,py23,13812345670)#想查看大壮所在班级的开班日期是多少?print(大壮.class_name.ctime)2019-4-26Process finished with exit code 0
示例2
#班级类 #属性:课程#课程: #课程名称 #周期 #价格#需求: #创建两个班级 linux57 python22 #查看linux57期的班级所学课程的价格 #查看python22期的班级所学课程的周期、
class banji: def __init__(self,kcheng): self.kcheng=kchengclass kecheng: def __init__(self,name,zhouqi,jiage): self.name=name self.zhouqi=zhouqi self.jiage=jiage#查看linux57期的班级所学课程的价格linux57_课程=kecheng('mike','6个月',18000)linux57=banji(linux57_课程)print(linux57.kcheng.jiage)#查看python22期的班级所学课程的周期、python22_课程=kecheng('anuo','3个月',20000)python22=banji(python22_课程)print(python22.kcheng.zhouqi)180003个月Process finished with exit code 0
四、三大特性:继承,封装,多态(1)继承1、继承时实例化的过程
class animal: def __init__(self,name): self.name=name def eat(self): print("{}吃".format(self.name))class cat(animal): def pashu(self): print("{}爬树".format(self.name))cat('小白') # 先开辟空间,空间里有一个类指针-->指向Cat # 调用init,对象在自己的空间中找init没找到,到Cat类中找init也没找到, # 找父类Animal中的init
- 图片解析
2、子类想要调用父类的方法的同时还想执行自己的同名方法?
# 子类想要调用父类的方法的同时还想执行自己的同名方法# 猫和狗在调用eat的时候既调用自己的也调用父类的,# ---在子类的方法中调用父类的方法 :父类名.方法名(self) animal.eat(self)
class animal: def __init__(self,name,food): self.name=name self.food=food self.blood = 100 self.waise = 100 def eat(self): print("{}吃{}".format(self.name,self.food)) def drink(self): print("{}喝".format(self.name)) def sleep(self): print("{}睡".format(self.name))class cat(animal): def eat(self): self.blood = self.blood+100 animal.eat(self) def pashu(self): print("{}爬树".format(self.name))class dog(animal): def eat(self): self.waise = self.waise+100 def kanjia(self): print("{}看家".format(self.name))小猫 = cat('小白','猫粮')小狗 = dog('小黑','狗粮')小猫.eat()小狗.eat()print(小猫.__dict__,小猫.blood)print(小狗.__dict__,小狗.waise)小白吃猫粮{'name': '小白', 'food': '猫粮', 'blood': 200, 'waise': 100} 200{'name': '小黑', 'food': '狗粮', 'blood': 100, 'waise': 200} 200Process finished with exit code 0
- 图片讲解调用过程
3、父类和子类方法的选择
# 继承语法 class 子类名(父类名):pass# 父类和子类方法的选择: # 子类的对象,如果去调用方法 # 永远优先调用自己的 # 如果自己有 用自己的 # 自己没有 用父类的 # 如果自己有 还想用父类的:直接在子类方法中调父类的方法 父类名.方法名(self)--animal.eat(self) # 如果有 父类没有的 想在自己独有的方法内调用父类的方法 : 直self.方法名()---self.drink()
4、思考一:下面代码的输出?
class Foo: def __init__(self): self.func() # 在每一个self调用func的时候,我们不看这句话是在哪里执行,只看self是谁(这里self指向son()这个对象的) def func(self): print('in foo')class Son(Foo): def func(self): print('in son')Son()
in sonProcess finished with exit code 0
5、派生属性
class Cat(animal): def __init__(self,name,food,eye_color): animal.__init__(self,name,food) # 调用了父类的初始化,去完成一些通用属性的初始化 self.eye_color = eye_color # 派生属性
6、单继承
- 当一个子类只有一个父类时称为单继承
语法:
class 子类名(父类名):
#B继承A A继承C C继承D B-A-C-Dclass D: def func(self): print('in D')class C(D):passclass A(C): def func(self): print('in A')class B(A):passB().func()in AProcess finished with exit code 0
7、多继承
- 多继承指一个子类可以有多个父类,它继承了多个父类的特性。多继承可以看作是对单继承的扩展
语法:
class 子类名(父类名1,父类名2…):
7.1:多继承时,调用多个父类中同名方法时,那个优先级高?
—看那个父类离子类近
class C(B,A):pass ------优先Bclass C(A,B):pass ------优先A
举例:
class B: def func(self): print('in B')class A: def func(self): print('in A')class C(B,A):passC().func()in BProcess finished with exit code 0#*********************************************class C(A,B):passC().func()in AProcess finished with exit code 0
8、object类
- 类祖宗,所有在python3当中的类都是继承object类的
9、类的继承顺序
- 背诵
- 只要继承object类就是新式类
- 不继承object类的都是经典类
python3 所有的类都继承object类,都是新式类
在py2中 不继承object的类都是经典类,继承object类的就是新式类了
# 算法的内容 # 如果是单继承 那么总是按照从子类->父类的顺序来计算查找顺序 # 如果是多继承 需要按照自己本类,父类1的继承顺序,父类2的继承顺序,... # merge的规则 :如果一个类出现在从左到右所有顺序的最左侧,并且没有在其他位置出现,那么先提出来作为继承顺序中的一个 # 或 一个类出现在从左到右顺序的最左侧,并没有在其他顺序中出现,那么先提出来作为继承顺序中的一个 # 如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后继续找其他顺序中符合上述条件的类# 经典类 - 深度优先 (总是在一条路走不通之后再换一条路,走过的点不会再走了)#新式类 - 广度优先# 深度优先要会看,自己能搞出顺序来# 广度优先遵循C3算法,要会用mro,会查看顺序# 经典类没有mro,但新式类有
9.1、C3算法:
在代码中打印调用顺序(mro方法)
语法: 类名.mro()print(F.mro()) # 只在新式类中有,经典类没有的
class A: def func(self): passclass B(A): def func(self): passclass C(A): def func(self): passclass D(B): def func(self): passclass E(C): def func(self): passclass F(D,E): def func(self): passprint(F.mro())F-D-B-E-C-A-object
[, , , , , , ]Process finished with exit code 0
9.2、深度优先(经典类)
9.3、广度优先(新式类)
10、继承总结:
# 单继承# 调子类的 : 子类自己有的时候# 调父类的 : 子类自己没有的时候# 调子类和父类的 :子类父类都有,在子类中调用父类的# 多继承# 一个类有多个父类,在调用父类方法的时候,按照继承顺序,先继承的就先寻找
10.1、复习实例化对象的过程
1--.前面是什么--就在对应的空间去找(如 A.role 表示在类A的空间中去找role; 如 a=A() a.l 表示在对象的空间中去找l)2--实例化的时候总是先开空间,再调用init, 调用init的时候总是把新开的的空间作为参数传递给self(即self 等于对象,比如 self.l 表示在对象的空间中去找l )3--分析 a=A() a.append(1) 的调用过程?解答:先在对象a空间中去找append方法,发现没有,再通过类指针去A类中找append方法,找到了,然后执行 self.l.append(obj)--首先self.l 是保存在对象空间中的一个空列表[],然后append(1),表示对象空 间的 self.l 空列表添加了一个元素1 (补充:此时A().l 是空列表[],没有1)
代码:
class A: role = [] def __init__(self): self.l = [] def append(self,obj): self.l.append(obj) def pop(self,index=-1): self.l.pop(index)print(A.role)a=A()
10.2、复习继承
重点案例:继承的调用过程
(2)多态
一个类型表现出来的多种状态(多态,同一个对象,多种形态)
# 一个类型表现出来的多种状态# 支付 表现出的 微信支付和苹果支付这两种状态# 在java情况下: 一个参数必须制定类型# 所以如果想让两个类型的对象都可以传,那么必须让这两个类继承自一个父类,在制定类型的时候使用父类来指定
# 多态 # 什么是多态? : 一个类表现出的多种形态,实际上是通过继承来完成的 # 如果狗类继承动物类,猫类也继承动物类 # 那么我们就说猫的对象也是动物类型的 # 狗的对象也是动物类型的 # 在这一个例子里,动物这个类型表现出了猫和狗的形态 # java中的多态是什么样 # def eat(动物类型 猫的对象/狗的对象,str 食物): # print('动物类型保证了猫和狗的对象都可以被传递进来') # python中的多态是什么样 : 处处是多态 # 鸭子类型 # 子类继承父类,我们说子类是父类类型的(猫类继承动物,我们说猫也是动物) # 在python中,一个类是不是属于某一个类型 # 不仅仅可以通过继承来完成 # 还可以是不继承,但是如果这个类满足了某些类型的特征条件 # 我们就说它长得像这个类型,那么他就是这个类型的鸭子类型
0、多态是什么?
多态 :不同的 子类对象调用 相同的 父类方法,产生 不同的 执行结果。
- 多态以 继承 和 重写 父类方法 为前提
- 多态是调用方法的技巧,不会影响到类的内部设计
class animal: def sleep(self): print("animal睡")class dog(animal): def sleep(self): print("dog睡觉")class cat(animal): def sleep(self): print("cat睡觉")class pig(animal): pass#不同的子类dog = dog()cat = cat()pig = pig()#调用相同的父类的同名方法,产生 不同的 执行结果dog.sleep()cat.sleep()pig.sleep()dog睡觉cat睡觉animal睡Process finished with exit code 0
1、多态是什么?
- 同一事件在不同对象上产生不同的相应
- 同一事件:即调用相同函数
- 不同对象:被调用函数的参数是不同的实例
- 不同的相应:输出结果不同
举例分析:
分析:同一事件:调用 func 函数不同对象:三个对象本质都是Animal类型的不同实例不同相应:输出结果不同,输出如下图:
代码:
class animal: def sleep(self): print("animal睡")class dog(animal): def sleep(self): print("dog睡觉")class cat(animal): def sleep(self): print("cat睡觉")#调用函数def func(x) : x.sleep()#创建3个实例,本质都是Animal类型animal = animal()dog = dog()cat = cat()#重点重点重点func(animal)func(dog)func(cat)animal睡dog睡觉cat睡觉Process finished with exit code 0
2、Python中【多态】的作用?
- 让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同功能(内容)的函数。
3、Python中多态的特点
1、只关心对象的实例方法是否同名,不关心对象所属的类型;
2、对象所属的类之间,继承关系可有可无;
3、多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
4、多态是调用方法的技巧,不会影响到类的内部设计。
4、多态的应用场景?4.1、 对象所属的类之间没有继承关系
- 调用同一个函数
fly()
, 传入不同的参数(对象),可以达成不同的功能
class Duck(object): # 鸭子类 def fly(self): print("鸭子沿着地面飞起来了")class Swan(object): # 天鹅类 def fly(self): print("天鹅在空中翱翔")class Plane(object): # 飞机类 def fly(self): print("飞机隆隆地起飞了")def fly(obj): # 实现飞的功能函数 obj.fly()duck = Duck()fly(duck)swan = Swan()fly(swan)plane = Plane()fly(plane)鸭子沿着地面飞起来了天鹅在空中翱翔飞机隆隆地起飞了Process finished with exit code 0
4.2、 对象所属的类之间有继承关系(应用更广)
class gradapa(object): def __init__(self, money): self.money = money def p(self): print("this is gradapa")class father(gradapa): def __init__(self, money, job): super().__init__(money) self.job = job def p(self): print("this is father,我重写了父类的方法")class mother(gradapa): def __init__(self, money, job): super().__init__(money) self.job = job def p(self): print("this is mother,我重写了父类的方法") return 100# 定义一个函数,函数调用类中的p()方法def fc(obj): return obj.p()gradapa1 = gradapa(3000)father1 = father(2000, "工人")mother1 = mother(1000, "老师")fc(gradapa1) # 这里的多态性体现是向同一个函数,传递不同参数后,可以实现不同功能.fc(father1)print(fc(mother1))this is gradapathis is father,我重写了父类的方法this is mother,我重写了父类的方法100
拓展:函数中 return 的理解
def func(): print('in func') return 100def func1(x): print('in func1') x() #没有使用return x()print(func1(func)) #结果没有打印100 打印None --这是因为 func1 这个方法的内存地址里没有存储100
加上 return
def func(): print('in func') return 100def func1(x): print('in func1') return x() #加上return x()print(func1(func)) #结果了打印100 --这是因为return 后,100这个值就返回到 func1 这个方法的内存地址里 所有才可以打印出来
super()方法
su po
在单继承中,super就是找父类
在多继承中。super就是按照mro顺序,去找当前类的下一个类
class User: def __init__(self,name): self.name = nameclass VIPUser(User): def __init__(self,name,level,strat_date,end_date): # User.__init__(self,name) super().__init__(name) # 推荐的 # super(VIPUser,self).__init__(name) self.level = level self.strat_date = strat_date self.end_date = end_date太白 = VIPUser('太白',6,'2019-01-01','2020-01-01')print(太白.__dict__){'name': '太白', 'level': 6, 'strat_date': '2019-01-01', 'end_date': '2020-01-01'}Process finished with exit code 0
(3)封装1、什么是封装?
- 把 属性 和 方法 装到一个类中,然后通过 对象 直接或者 self 间接获取被封装的内容
广义 :把属性和方法装起来,外面不能直接调用了,要通过类的名字来调用狭义 :把属性和方法藏起来,外面不能调用,只能在内部偷偷调用(私有属性,私有方法)
2、私有属性和私有方法
- 对于私有属性和私有方法,只能在类的内部访问,类的外部无法访问
- 在定义属性或⽅法时,在属性名或者⽅法名前 增加两个下划线,定义的就是私有属性或方法
私有属性:
class User: def __init__(self,name,passwoed): self.name=name self.__passwoed=passwoed def get_passwoed(self): return self.__passwoed #只有在类内部才可以通过 self__passwoed 的形式访问到obj=User('mike','123345')#print(obj.__passwoed) #类的外部访问不了私有属性,报错print(obj.get_passwoed()) #类的内部可以调用私有属性,
思考:既然类的外部访问不了了,那为什么我还有在类的内部定义一个 def get_passwoed(self) 方法呢?
—-因为有时候我想让你看的私有属性 ,但是我不想让你该我的私有属性
私有方法:
import hashlibclass User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量 def __get_md5(self): # 私有的绑定方法 md5 = hashlib.md5(self.usr.encode('utf-8')) md5.update(self.__pwd.encode('utf-8')) return md5.hexdigest() def getpwd(self): return self.__get_md5() alex = User('alex','sbsbsb')print(alex.getpwd())d6170374823ac53f99e7647bab677b92Process finished with exit code 0
3、使用私有属性或/方法的三种场景?
# 使用私有的三种情况 # 不想让你看也不想让你改 # 可以让你看 但不让你改 # 可以看也可以改 但是要求你按照我的规则改
# 所有的私有化都是为了让用户不在外部调用类中的某个名字# 如果完成私有化 那么这个类的封装度就更高了 封装度越高各种属性和方法的安全性也越高 但是代码越复杂
4、加了双下划线的名字为啥不能从类的外部调用了?
class User: __Country = 'China' # 私有的类属性 __Role = '法师' def func(self): print(self.__Country) #在内的类别使用的时候,自动的吧当前这句话所在的类的名字拼在私有变量前面,来完成调用print(User.__dict__)#非要取私有属性?--可以print(User._User__Country){'_User__Country': 'China', '_User__Role': '法师'}ChinaProcess finished with exit code 0
5、私有的内容能不能被子类使用呢?
— 不能,原因看面试题2
面试题1:打印什么结果?
class Foo(object): def __init__(self): self.func() def func(self): print('in Foo')class Son(Foo): def func(self): print('in Son')Son()in SonProcess finished with exit code 0
- 图像解析:
面试题2:打印什么结果?
class Foo(object): def __init__(self): self.__func() def __func(self): print('in Foo')class Son(Foo): def __func(self): print('in Son')Son()in FooProcess finished with exit code 0
- 图片解析
面试题3:打印什么结果?
—会报错,因为找不到 _son__func() 方法
class Foo(object): def __func(self): print('in Foo')class Son(Foo): def __init__(self): self.__func()Son()AttributeError: 'Son' object has no attribute '_Son__func'Process finished with exit code 1
6、类中的三个装饰器(内置函数)6.1、@property
- pao po t
- 把一个方法伪装成一个属性,在调用这个方法的时候不需要加 () 就可以直接得到返回值
#(1)property#例子:计算圆得面积from math import piclass Circle: def __init__(self,r): self.r = r @property # 把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值 def area(self): return pi * self.r**2c1=Circle(5)print(c1.r)#print(c1.area()) #area()面积是一个方法,但是其实叫属性会好点,所以引用@property 装饰器print(c1.area)# 变量的属性和方法? # 属性 :圆形的半径\圆形的面积 # 方法 :登录 注册
用@property装饰的这个方法 不能有参数
–可以理解属性没发传参数,方法在可以传参数
import timeclass Person: def __init__(self,name,birth): self.name = name self.birth = birth @property def age(self): # 用@property装饰的这个方法 不能有参数 return time.localtime().tm_year - self.birth太白 = Person('太白',1995)print(太白.age)
- @property的第二个应用场景 : 和私有的属性合作的
# property的第二个应用场景 : 和私有的属性合作的class User: def __init__(self,usr,pwd): self.usr = usr self.__pwd = pwd @property def pwd(self): return self.__pwdalex = User('alex','sbsbsb')print(alex.pwd)sbsbsbProcess finished with exit code 0
class Goods: discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__price = origin_price @property def price(self): return self.__price * self.discountapple = Goods('apple',5)#print(apple.price()) #很变扭,因为苹果的价格应该是属性,叫方法很变扭print(apple.price)
- @property进阶
# property进阶 ---想把苹果的价格该一下?怎么办?class Goods: discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__price = origin_price @property def price(self): return self.__price * self.discount @price.setter def price(self,new_value): if isinstance(new_value,int): #约束new_value的值是int类型 才执行 self.__price = new_valueapple = Goods('apple',5)print(apple.price) # 调用的是被@property装饰的priceapple.price = 10 # 调用的是被setter装饰的priceprint(apple.price)
6.2、@classmethod(类方法)
普通方法可以访问类方法/类属性,类方法不能访问普通方法,因为访问普通方法需要对象
被装饰的方法会成为一个静态方法(类方法)
- 用@classmethod 修饰的方法为类方法;
- 类方法的参数为 cls (不是self),在类方法内部通过 cls.类属性 或者 cls.类方法 来访问同一个类中的其他类属性和类方法(类方法不能访问普通方法,因为访问普通方法需要对象)
- 类方法不需要实例化就可以调用 (可以通过类名或者对象名调用),类方法只能访问同一个类中的类属性和类方法。
# classmethod 被装饰的方法会成为一个静态方法class Goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount # 想修改折扣0.6====v1.0(不用类方法) # def change_discount(self,new_discount): # #self.__discount #这个地方不能用self 这样的话 相当于在self这个空间里创建了__discount # Goods.__discount=new_discount # 想修改折扣0.6====v2.0(用类方法) @classmethod # 把一个对象绑定的方法 修改成一个 类方法 def change_discount(cls,new_discount): cls.__discount=new_discount #用Goods.__discount也可以,考虑到 类名可能会改# apple=Goods()# print(apple.price)# #想修改折扣0.6# apple.change_discount(0.6)# apple2=Goods()# print(apple2.price)Goods.change_discount(0.6)apple=Goods()print(apple.price)
什么时候用@classmethod?
# 1.定义了一个方法,默认传self,但这个self没被使用# 2.并且你在这个方法里用到了当前的类名,或者你准备使用这个类的内存空间中的名字的时候
6.3、@staticmethod
帮助我们把一个普通的函数挪到类中直接使用,制造静态方法使用
# @staticmethod 被装饰的方法会成为一个静态方法class User: pass @staticmethod def login(a,b): # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加 @staticmethod装饰器就可以了 print('登录的逻辑',a,b) # 在函数的内部既不会用到self变量,也不会用到cls类 #用类名调用User.login(1,2)#用对象名调用obj = User()obj.login(3,4)
7、反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
- 用字符串数据类型的名字 来操作这个名字对应的 函数\实例变量\绑定方法\各种方法
语法:
getatter(对象名,'属性名') ==> 对象名.属性名
7.1、反射的应用:
- 应用场景:现在让我们打开浏览器,访问一个网站,你单击登录就跳转到登录界面,你单击注册就跳转到注册界面,等等,其实你单击的其实是一个个的链接,每一个链接都会有一个函数或者方法来处理
没学反射之前的解决方式:
class User: def login(self): print('欢迎来到登录页面') def register(self): print('欢迎来到注册页面') def save(self): print('欢迎来到存储页面')while 1: choose = input('>>>').strip() if choose == 'login': obj = User() obj.login() elif choose == 'register': obj = User() obj.register() elif choose == 'save': obj = User() obj.save()
学了反射之后解决方式:
class User: def login(self): print('欢迎来到登录页面') def register(self): print('欢迎来到注册页面') def save(self): print('欢迎来到存储页面')user = User()while 1: choose = input('>>>').strip() if hasattr(user, choose): func = getattr(user, choose) func() else: print('输入错误。。。。')
8、封装总结8.1、类的成员属性的成员:
类属性:定义在类的里面,方法的外面(不用实例化就可以【类名.类属性名】直接调用)
普通属性:定义在初始化init方法中
私有属性:定义在类的里面,方法的外面,前面加上两个下划线,只能在类的内部访问,类的外部无法访问
class a: company_name = '老男孩教育' # 类属性 __iphone = '1353333xxxx' # 私有属性 def __init__(self,age): self.age=age #普通属性 def func(self,hight): self.hight =hight #普通属性 return self.hight#类属性 可以直接打印。不用实例化对象print(a.company_name)#打印普通属性mike=a(18)print(mike.age)print(mike.func(180))#打印使有属性 使用属性外部无法访问,除非用 _类名__私有属性名print(a._a__iphone)老男孩教育181801353333xxxxProcess finished with exit code 0
方法的成员:
普通方法:定义在类里的方法
私有方法:定义在类里的方法,前面加上两个下划线,只能在类的内部访问,类的外部无法访问(不能被继承)
类方法:定义在类里的方法,用@classmethod 修饰,类方法的参数为 cls (不是self),类方法不需要实例化就可以调用,类方法只能访问同一个类中的类属性和类方法(访问普通方法要先实例化)。
class b: def __init__(self): #普通方法 pass def func1(self): print("普通方法") #普通方法 #self.__func2() #普通方法中调用私有方法 #self.func3() #普通方法中调用类方法 def __func2(self): #私有方法 print('私有方法') @classmethod def func3(cls): print('类方法') #类方法 # c=b() #类方法中调用普通方法 需要实例化 # c.func1() # d=b() #类方法中调用私有方法 需要实例化 # d.__func2()#调用普通方法mike=b()mike.func1()#调用私有方法 外部不能调用,只能内部访问#调用类方法 不用实例化b.func3()普通方法类方法Process finished with exit code 0
python大作业—学生选课系统需求分析:
- 从“学生选课系统” 这几个字就可以看出来,我们最核心的功能其实只有 选课。
角色:
- 学生、管理员
功能:
- 登陆 : 管理员和学生都可以登陆,且登陆之后可以自动区分身份
- 选课 : 学生可以自由的为自己选择课程
- 创建用户 : 选课系统是面向本校学生的,因此所有的用户都应该由管理员完成
- 查看选课情况 :每个学生可以查看自己的选课情况,而管理员应该可以查看所有学生的信息
工作流程:
登陆 :用户输入用户名和密码 判断身份 :在登陆成果的时候应该可以直接判断出用户的身份 是学生还是管理员 学生用户 :对于学生用户来说,登陆之后有三个功能 1、查看所有课程 2、选择课程 3、查看所选课程 4、退出程序 管理员用户:管理员用户除了可以做一些查看功能之外,还有很多创建工作 1、创建课程 2、创建学生学生账号 3、查看所有课程 4、查看所有学生 5、查看所有学生的选课情况 6、退出程序
程序设计:
- 对于复杂的功能,我们首先就应该想到面向对象编程。而要想将面向对象的程序开发好,就应该做好类和对象的分析工作。
选课系统简单的划分其实只有两个角色:管理员和学生。 仔细思考,你会发现有很多想不通的地方,比如学生选课,课从哪里来? 这样一想就会发现,其实课程应该可以由管理员创造出来,那么课程又会有很多属性:价格、周期、课程名、授课老师等等 那么课程也应该是一个类。
综上,本程序最基础的分析已经完毕。接下来我们要把所有的类以及其中的属性、方法设计出来
(1)、课程:
属性:课程名、价格、周期、老师*课程并没有什么动作,所以我们只设计属性不设计方法。其实这里还可以设计很多属性,比如课程的开始时间、结束时间、教室等等,只要你需要,这些都可以记录下来,但是这里我们为了简化代码,就先不设计这些了
(2)、学生:
属性:姓名、所选课程 方法:查看可选课程、选择课程、查看所选课程、退出程序
(3)、管理员
属性:姓名 方法:创建课程、创建学生学生账号、查看所有课程、查看所有学生、查看所有学生的选课情况、退出程序
流程图:
采分点:
1.类的创建和规划 30分
2.登陆自动识别身份 10分
3.管理员创建各种信息 20分
4.学生选课 20分
5.将内存中的数据保存到文件中 10分
6.代码简洁、调理清晰10分
技术讲解:1、enumerate()使用介绍
- enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
语法:
enumerate(sequence, [start=0])例如: enumerate(Manager.opt_list,1)sequence – 一个序列、迭代器或其他支持迭代对象。start – 下标起始位置的值。1.enumerate()是python的内置函数2.enumerate在字典上是枚举、列举的意思3.对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值4.enumerate多用于在for循环中得到计数5.enumerate()返回的是一个enumerate对象
举例:
opt_list = [('创建课程', 'create_course'), ('创建学生', 'create_student'), ('查看课程', 'show_courses'), ('查看学生', 'show_student'), ('查看学生和已选课程', 'show_stu_course'), ('退出', 'exit')]for index,i in enumerate(opt_list,1): print(index,i)
1 ('创建课程', 'create_course')2 ('创建学生', 'create_student')3 ('查看课程', 'show_courses')4 ('查看学生', 'show_student')5 ('查看学生和已选课程', 'show_stu_course')6 ('退出', 'exit')Process finished with exit code 0