深浅copy

1对于赋值运算来说,l1与l2指向的是同一个内存地址,所以他们是完全一样的。

  1. l1 = [1,2,3,['barry','alex']]
  2. l2 = l1
  3.  
  4. l1[0] = 111
  5. print(l1,id(l1)) # [111, 2, 3, ['barry', 'alex']] 112431152
  6. print(l2,id(l2)) # [111, 2, 3, ['barry', 'alex']] 112431152
  7.  
  8. l1[3][0] = 'wusir'
  9. print(l1,id(l1)) # [1, 2, 3, ['wusir', 'alex']] 112431152
  10. print(l2,id(l2)) # [1, 2, 3, ['wusir', 'alex']] 112431152

2对于浅copy(copy)来说,只是在内存中重新创建了开辟了一个空间存放一个新列表(内存地址不同),列表中的元素的内存地址是一样的。

列表中可变的数据类型修改就会跟着修改,但是列表中的不可变的数据类型修改就不会跟着修改。

解释:copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。子对象(数组,字典等可变数据类型)修改,也会修改

  1. l1 = [1, '太白', True, (1,2,3), [22, 33]]
  2. l2 = l1.copy()
  3.  
  4. print(id(l1), id(l2)) # 112496688 112475312
  5.  
  6. print(id(l1[-2]), id(l2[-2])) #35243432 35243432
  7.  
  8. print(id(l1[-1]),id(l2[-1])) #112410096 112410096

3.对于深copy(deepcopy)来说,列表是在内存中重新创建的,是原始的对象。不会随着修改而修改。

copy.deepcopy 深拷贝 拷贝对象及其子对象(原始对象)

  1. import copy
  2. l1 = [1, 'alex', True, (1,2,3), [22, 33]]
  3. l2 = copy.deepcopy(l1)
  4. # print(id(l1), id(l2)) # 112627760 112606384
  5. print(id(l1[0]),id(l2[0])) # 1521669168 1521669168
  6. print(id(l1[-1]),id(l2[-1])) # 112999960 112999880 #元素为列表 为不可哈希(可变类型)
  7. print(id(l1[-2]),id(l2[-2])) # 6735272 6735272

例子:

  1. import copy
  2.  
  3. a = [1, 2, [3, 4], {'a': 1}] # 原始对象
  4. b = a # 赋值,传对象的引用
  5. c = copy.copy(a) # 对象拷贝,浅拷贝
  6. d = copy.deepcopy(a) # 对象拷贝,深拷贝
  7. e = a[:] # 能复制序列,浅拷贝
  8.  
  9. a.append('add1')
  10. # 修改对象a
  11. a[2].append('add2')
  12. # 修改对象a中的[3,4]数组对象
  13. a[3]['a'] = 666
  14. print('a:', a)
  15. print('b:', b)
  16. print('c:', c)
  17. print('d:', d)
  18. print('e:', e)

结果;

  1. a: [1, 2, [3, 4, 'add2'], {'a': 666}, 'add1']
  2. b: [1, 2, [3, 4, 'add2'], {'a': 666}, 'add1']
  3. c: [1, 2, [3, 4, 'add2'], {'a': 666}]
  4. d: [1, 2, [3, 4], {'a': 1}]
  5. e: [1, 2, [3, 4, 'add2'], {'a': 666}]
  1. 解释:copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。子对象(数组)修改,也会修改
  2.  copy.deepcopy 深拷贝 拷贝对象及其子对象(原始对象)

  

  

文件的操作

文件句柄 = open(‘文件路径’,‘模式’,'编码')

  1. #1. 打开文件的模式有(默认为文本模式):
  2. r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】 #注意读是读光标后的内容
  3. w,只写模式【不可读;不存在则创建;存在则清空内容】
  4. a 只追加写模式【不可读;不存在则创建;存在则只追加内容】
  5. #2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、
    图片文件的jgp格式、视频文件的avi格式)
  6. rb
  7. wb
  8. ab
  9. 注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码
  10.  
  11. #3,‘+’模式(就是增加了一个功能)
  12. r+, 读写【可读,可写】
  13. w+,写读【可写,可读】
  14. a+, 写读【可写,可读】
  15.  
  16. #4,以bytes类型操作的读写,写读,写读模式
  17. r+b 读写【可读,可写】
  18. w+b,写读【可写,可读】
  19. a+b 写读【可写,可读】
  1. #在读的时候,bytes类型在读的时候内置函数自动转变为字符串了
  2. # with open('01.txt','r',encoding='UTF-8') as f:
  3. # # f.write('曾辉123456')
  4. # print(f.read()) bytes ---->str

read:

  1. 文件打开方式为文本模式时,代表读取3个字符

  2. 文件打开方式为b模式时,代表读取3个字节

其余的文件内光标移动都是以字节为单位的如:seek,tell,truncate

注意:

  1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的

  2. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下使用。

  1. with open('log',mode='a+',encoding='utf-8') as f:
  2. # f.write('佳琪')
  3. count = f.tell() #tell 是获取光标的位置,也是以一个字节为单位的。
  4. f.seek(0) #seek 光标的移动是以一个字节为单位的
  5. print(f.read()) #读取光标后的文本
  6. f.read(2) #读取几个字符
  7. f.truncate(5) #截断原文件,对原文件进行变化,
  8. print(f.readline()) #只读取文件光标后的一行内容
  9. coun = f.readlines() #readlines读取文件的每行内容,并且把每行内容
  10. #作为List中的元素储存起来。
  11. f.writelines(['1','23','3']) #将一个字符串列表(列表中的元素为字符串)写入文件
  12.  
  13. with open('log',mode='a+',encoding='utf-8') as f,\
  14. open('log',mode='a+',encoding='utf-8') as f1,\
  15. open('log',mode='a+',encoding='utf-8') as f2: #用 with 可以同时打开多个文件,
  16. #同时不用在最后关闭文件

文件的修改,实际上文件不能直接进行修改,可以通过间接修改。修改的过程为:先打开2个文件,然后在写入新的内容放在一个文件里面,然后在删除原来的文件,在将修改内容的文件重命名为原来的文件名。

  1. #函数的文件修改
  2. def txt_func(fliename,old,new): #打开文件
  3. with open(fliename,encoding='utf-8') as f,open('%s.bak'%(fliename),'w',encoding='utf-8') as f2:
  4. for line in f:
  5. if old in line:
  6. line = line.replace(old,new)
  7. #写文件
  8. f2.write(line)
  9.  
  10. import os
  11. os.remove(fliename) #删除文件
  12. os.rename('%s.bak'%(fliename),fliename) #重命名文件
  13.  
  14. txt_func('123.txt','123','aaa')

  

 

函数的返回值

函数:可读性强  复用性强

return 关键字的作用:

1,结束函数的执行(return 下面的语句都不执行)

  2,返回值

没有返回值。

  不写return的情况下,会默认返回一个None; 

有返回值包括一个值或者是多个值。

  1. def fun(a,b):
  2. c= a+b
  3. return c,a
  4. sun,b = fun(1,1) #返回多个值,如果用一个变量去接收,变量代表一个元组;
  5. sun1 = fun(1,1)
  6. print(sun1) #用相对应的个数的变量去接收,每一个变量代表一个值.
  7. print(sun,b)

  

函数的参数

实参:调用函数传递的参数(实际的参数);     形参:定义函数时传递的参数;

站在实参的角度上:

1,按照位置传参;

2, 按照关键字传参

#位置传参必须在关键字传参之前;

形参里面的:

默认参数(不可变数据的类型);

#参数陷阱:默认参数是一个可变数据的类型;

  1. # 如果默认参数的值是一个可变数据类型,
    # 那么每一次调用函数的时候,
    # 如果不传值就公用这个数据类型的资源
  1. def defult_param(a,l = []):
  2. l.append(a)
  3. print(l)
  4.  
  5. defult_param('alex')
  6. defult_param('egon') #每次调用公用一个列表
  7.  
  8. #['alex'] #默认参数l里面只一个元素
  9. #['alex', 'egon'] #l里面变成2个元素来;

  

  

站在形参的角度上:

1,按照位置传参;

2,按照动态传参(*args)

3,按照关键字传参(默认参数);

4,**kwargs

所有参数的传参的顺序是 位置参数,*args,关键字参数,**kwargs。

  1. #参数的位置: 位置参数 ,*args, 默认参数,**kargs
  2.  
  3. #*args :接收多个位置参数,组织成一个元组
  4. #**kwargs可以接收多个关键字参数,组成成一个字典
  5. # def func(*args ,**kwargs): #站在形参的角度上(定义函数的时候),给变量加上*;**,就是组合所有传来的值.
  6. # print (args,kwargs)
  7. # # return ('{},{}'.format(name,age))
  8. # l = [1,2,3,'abc1']
  9. # # l='12331231'
  10. # a={'name':'zenghui','age':'22'}
  11. #
  12. # # func(1,2,3,name='zenghui',age = '22'
  13. # func(*l,**a) #**就是将a的字典按照位置的顺序打散;
  14. #站在实参的角度上(调用函数的时候),给一个序列加上*;**,就是将这个序列按照顺利打散.

  

函数的注释

  1. def fun(a,b):
  2. '''
  3. 函数的功能
  4. param a(参数):
  5. param b(参数):
  6. return(返回值):
  7. '''
  8. c= a+b #函数的主体
  9. return c,a

  

参数的总结:

命名空间和作用域python 

python代码运行的时候碰到函数的运行过程:

第一步是python解释器先执行,然后在内存中开辟了一个空间,每当遇到一个变量的时候,就把变量名和值之间的对应的关系记录下来。但是当遇到函数的定义的时候,解释器只是将函数名读入内存中,表示这个函数的存在。但是不关心函数内部的变量以及名字。等到执行函数调用的时候,python解释器会再开辟一块内存开存储这个函数里面的内容,这个时候才关注函数里面有哪些变量,而函数中的变量会存储在新开辟出来的内存中。函数中的变量只能在函数的内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。

我们给这个“存放名字与值的关系”的空间起了一个名字——叫做命名空间

代码在运行开始,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间

有三种命名空间;分别为内置的命名空间;全局的命名空间;局部的命名空间;

  1. #内置命名空间 —— python解释器
    就是python解释器一启动就可以使用的名字存储在内置命名空间中
    内置的名字在启动解释器的时候被加载进内存里
    #全局命名空间 —— 我们写的代码但不是函数中的代码
    是在程序从上到下被执行的过程中依次加载进内存的
    放置了我们设置的所有变量名和函数名
    #局部命名空间 —— 函数
    就是函数内部定义的名字
    当调用函数的时候 才会产生这个名称空间 随着函数执行的结束 这个命名空间就又消失了
  2.  
  3. 在局部:可以使用全局、内置命名空间中的名字
    在全局:可以使用内置命名空间中的名字,但是不能用局部中使用
    在内置:不能使用局部和全局的名字的
  1. 在正常情况下,直接使用内置的名字
    当我们在全局定义了和内置名字空间中同名的名字时,会使用全局的名字
    当我自己有的时候 我就不找我的上级要了
    如果自己没有 就找上一级要 上一级没有再找上一级 如果内置的名字空间都没有 就报错
    多个函数应该拥有多个独立的局部名字空间,不互相共享
  1.  

三种命名空间之间的加载与取值顺序:

加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

取值:

从底层开始往上寻找并且取值;

在局部调用的时候: 局部命名空间————全局命名空间————内置的命名空间

在全局调用的时间:全局命名空间————内置的命名空间

作用域

作用域就是作用范围,按照生效范围可以分为全局作用域局部作用域

全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

局部作用域:局部名称空间,只能在局部范围内生效

global 关键字:声明全局变量

  1. 对于不可变数据类型 在局部可是查看全局作用域中的变量
    但是不能直接修改
    如果想要修改,需要在程序的一开始添加global声明
    如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效
  1. a=2
  2. def fi():
  3. a = 1
  4. def f2():
  5. global a
  6. a+=1
  7. print(a)
  8. f2()
  9. # print("--a---",a)
  10. fi()
  11. print(a)

 globals和locals方法

  1. #globals 永远打印全局的名字
    #locals 输出什么 根据locals所在的位置
  1. def func():
  2. a = 12
  3. b = 20
  4. print(locals())
  5. print(globals())
  6.  
  7. func()
  8. #print(globals())打印的内置空间和局部空间的名字 #{'__name__': '__main__', '__doc__':.....
  9. #print(locals()) 打印的是本地的空间的名字(此时的本地为函数内部即局部命名空间)即{'b': 20, 'a': 12}
  10. def func():
  11. a = 12
  12. b = 20
  13. # print(locals())
  14. print(globals())
  15.  
  16. func()
  17. print(locals())
  18. #都是打印的内置空间和局部空间的名字 #{'__name__': '__main__', '__doc__':.....
  19. #因为此时locals()的本地为全局

  

  1. 函数的嵌套和作用域链
  1. #函数的嵌套定义
    #内部函数可以使用外部函数的变量
  1. def max2(x,y):
  2. m = x if x>y else y
  3. return m
  4.  
  5. def max4(a,b,c,d):
  6. res1 = max2(a,b)
  7. res2 = max2(res1,c)
  8. res3 = max2(res2,d)
  9. return res3
  10.  
  11. # max4(23,-7,31,11)
  1.  

函数的作用域链

  1. def f1():
  2. a = 1
  3. def f2():
  4. a = 2
  5. f2()
  6. print('a in f1 : ',a)
  7.  
  8. f1()

nonlocal关键字 

  1. #nonlocal 只能用于局部变量 找上层中离当前函数最近一层的局部变量
    #声明了nonlocal的内部函数的变量修改会影响到 离当前函数最近一层的局部变量
    # 对全局无效
    # 对局部 也只是对 最近的 一层 有影响
  1. a=1
  2.  
  3. def outer():
  4. a=1
  5. def inner():
  6. b=1
  7. def inner2():
  8. nonlocal a
  9. # global a
  10. a+=1
  11. # print('小小局部',a)
  12.  
  13. inner2()
  14. # print('小局部',a)
  15. inner()
  16. print('局部:',a)
  17. outer()
  18.  
  19. print("全局:",a)
  20.  
  21. #局部: 2
  22. #全局: 1

  

  1. 函数名的本质
    函数名本质上就是函数的内存地址
    1.可以被引用
    2.可以被当作容器类型(可变数据类型)的元素
    3.可以当作函数的参数和返回值
  1. def func():
  2. print(123)
  3.  
  4. # func() #函数名就是内存地址
  5. func2 = func #函数名可以赋值
  6. func2()
  7.  
  8. l = [func,func2] #函数名可以作为容器类型的元素
  9. print(l)
  10. for i in l:
  11. i()
  12. '''
  13. 123
  14. [<function func at 0x0000000002071E18>, <function func at 0x0000000002071E18>]
  15. 123
  16. 123
  17. '''
  18. def func():
  19. print(123)
  20.  
  21. def wahaha(f):
  22. f()
  23. return f #函数名可以作为函数的返回值
  24.  
  25. qqxing = wahaha(func) # 函数名可以作为函数的参数
  26. qqxing()
  27.  
  28. #123
  29. #123
  1.  

闭包:

  1. 闭包需要2个条件:嵌套函数;内部函数调用外部函数的变量
  1. def outer():
  2. a = 1
  3. def inner():
  4. print(a)
  5. inner()
  6. outer()

  

  1. 闭包常用的方法:在外面直接执行内部的函数
  1. def outer():
  2. a = 1
  3. def inner():
  4. print(a)
  5. return inner
  6.  
  7. inn = outer()
  8. inn()
  1.  

 判断闭包函数的方法__closure__ 

  1. def outer():
  2. a = 1
  3. def inner():
  4. print(a)
  5. print(inner.__closure__)
  6. inner()
  7. # print(outer.__closure__)
  8. outer()
  9.  
  10. #(<cell at 0x0000000009993C78: int object at 0x0000000060846B00>,)
  11. #输出中有<cell的就为闭包函数
  12. def outer():
  13. a = 1
  14. def inner():
  15. print(1)
  16. print(inner.__closure__)
  17. inner()
  18. outer()
  19. #不是闭包函数,就输出为None

 总结:

命名空间:

  一共有三种命名空间从大范围到小范围的顺序:内置命名空间、全局命名空间、局部命名空间

作用域(包括函数的作用域链):

  1. 小范围的可以用大范围的
    但是大范围的不能用小范围的
    范围从大到小(图)

  1. 在小范围内,如果要用一个变量,是当前这个小范围有的,就用自己的
    如果在小范围内没有,就用上一级的,上一级没有就用上上一级的,以此类推。
    如果都没有,报错

函数的嵌套:

  嵌套调用

  嵌套定义:定义在内部的函数无法直接在全局被调用

函数名的本质:

  就是一个变量,保存了函数所在的内存地址

闭包:

  内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数

装饰器函数 

  1. 装饰器形成的过程 : 最简单的装饰器 有返回值的 有一个参数 万能参数
    装饰器的作用:在不改变函数调用方式的基础上,在函数的前、后添加功能。
    原则 :开放封闭原则
    语法糖 :@
    装饰器的固定模式
  1. def timmer(f): #装饰器函数
  2. def inner():
  3. start = time.time()
  4. ret = f() #被装饰的函数
  5. end = time.time()
  6. print(end - start)
  7. return ret
  8. return inner
  9.  
  10. @timmer #语法糖 @装饰器函数名
  11. def func(): #被装饰的函数
  12. time.sleep(0.01)
  13. print('老板好同事好大家好')
  14. return '新年好'
  15. # func = timmer(func)
  16. ret = func() #inner()
  17. print(ret)
  18. # 装饰器的作用 —— 不想修改函数的调用方式 但是还想在原来的函数前后添加功能
  19. # timmer就是一个装饰器函数,只是对一个函数 有一些装饰作用
  1. 原则: 开放封闭原则
    开放 对扩展是开放的
    封闭 对修改是封闭的
  2.  
  3. 带参数的装饰器
  1. # def wrapper(f): #装饰器函数,f是被装饰的函数
  2. # def inner(*args,**kwargs):
  3. # '''在被装饰函数之前要做的事'''
  4. # ret = f(*args,**kwargs) #被装饰的函数
  5. # '''在被装饰函数之后要做的事'''
  6. # return ret
  7. # return inner
  8. #
  9. # @wrapper #语法糖 @装饰器函数名
  10. # def func(a,b): #被装饰的函数
  11. # time.sleep(0.01)
  12. # print('老板好同事好大家好',a,b)
  13. # return '新年好'

  

  1. 装饰器的固定模式
  1. def wrapper(func): #qqxing
  2. def inner(*args,**kwargs):
  3. ret = func(*args,**kwargs) #被装饰的函数
  4. return ret
  5. return inner
  6.  
  7. @wrapper #qqxing = wrapper(qqxing)
  8. def qqxing():
  9. print(123)
  10.  
  11. ret = qqxing() #inner
  1.  

  

时间功能的装饰器

  1. import time
  2.  
  3. def wrapper(func):
  4. def inner(*args,**kwargs):
  5. start=time.time()
  6. ret = func(*args,**kwargs)
  7. end = time.time()
  8. print(end - start)
  9. return ret
  10. return inner
  11.  
  12. @wrapper #qqxing = wrapper(qqxing)
  13. def qqxing(a,b):
  14. ret=a+b
  15. time.sleep(0.1)
  16. return ret
  17.  
  18. print(qqxing(1,2))

 

  1. from functools import wraps
  1. wraps(func)是python提供的给装饰器函数专门用来恢复被装饰函数性状的机制 
  1. import time
  2. def wrapper(func):
  3. # @wraps(func) #带参数的装饰器
  4. def inner(*args,**kwargs):
  5. start=time.time()
  6. ret = func(*args,**kwargs)
  7. end = time.time()
  8. print(end - start)
  9. return ret
  10. return inner
  11.  
  12. @wrapper #qqxing = wrapper(qqxing)
  13. def qqxing(a,b):
  14. '''
  15. 这是一个记录运行时间的代码
  16. :param a:
  17. :param b:
  18. :return:
  19. '''
  20. ret=a+b
  21. time.sleep(0.1)
  22. return ret
  23.  
  24. print(qqxing(1,2))
  25. print(qqxing.__name__)
  26. print(qqxing.__doc__)
  27. #
  28. #
  29. # #结果为 0.10000038146972656
  30. # #3
  31. # #inner #实际上被装饰器的函数为inner
  32. # #None
  33.  
  34. import time
  35. from functools import wraps
  36. def wrapper(func):
  37. @wraps(func) #带参数的装饰器
  38. def inner(*args,**kwargs):
  39. start=time.time()
  40. ret = func(*args,**kwargs)
  41. end = time.time()
  42. print(end - start)
  43. return ret
  44. return inner
  45.  
  46. @wrapper #qqxing = wrapper(qqxing)
  47. def qqxing(a,b):
  48. '''
  49. 这是一个记录运行时间的代码
  50. :param a:
  51. :param b:
  52. :return:
  53. '''
  54. ret=a+b
  55. time.sleep(0.1)
  56. return ret
  57.  
  58. print(qqxing(1,2))
  59. print(qqxing.__name__)
  60. print(qqxing.__doc__) #函数的注释
  61.  
  62. #结果为:0.09999990463256836
  63. #3
  64. #qqxing #在装饰器内部的函数加上了一个wraps带参数的装饰器,这样被装饰函数就是自己了
  65. '''
  66. 这是一个记录运行时间的代码
  67. :param a:
  68. :param b:
  69. :return:
  70. '''
  1. 带参数的装饰器可以方便的控制装饰器的取消.
  1. import time
  2. from functools import wraps
    flag = True
  3. def outer(flag):
  4. def wrapper(func):
  5. @wraps(func) #带参数的装饰器
  6. def inner(*args,**kwargs):
  7. if flag:
  8. start=time.time()
  9. ret = func(*args,**kwargs)
  10. end = time.time()
  11. print(end - start)
  12. return ret
  13. else:
  14. ret = func(*args, **kwargs)
  15. return ret
  16. return inner
  17. return wrapper
  18.  
  19. @outer(flag) #带参数的装饰器 #qqxing = wrapper(qqxing)
  20. def qqxing(a,b):
  21. ret=a+b
  22. time.sleep(0.1)
  23. return ret
  24.  
  25. print(qqxing(1,2))

 多个装饰器控制一个函数

 

  1.  
  1. #多个装饰器控制一个函数
    #离函数最近的装饰器函数先执行
  2. from functools import wraps
  3. flag = True
  4. def outer1(flag):
  5. def wrapper1(func): #func ---> func
  6. @wraps(func)
  7. def inner1(*args,**kwargs):
  8. if flag:
  9. print('函数之前执行w1')
  10. ret1 =func(*args,**kwargs) #func()
  11. print('函数之后结束w1')
  12. return ret1 #ret1 = ret
  13. else:
  14. ret1 = func(*args, **kwargs)
  15. return ret1
  16. return inner1
  17. return wrapper1
  18.  
  19. def outer2(flag):
  20. def wrapper2(func): #func ----> inner1
  21. @wraps(func)
  22. def inner2(*args,**kwargs):
  23. if flag:
  24. print('函数之前执行w2')
  25. ret2 =func(*args,**kwargs) #inner1()
  26. print('函数之后结束w2')
  27. return ret2 # ret2 = ret1= ret
  28. else:
  29. ret2 = func(*args, **kwargs)
  30. return ret2
  31. return inner2
  32. return wrapper2
  33.  
  34. # wrapper1 = outer1(flag)
  35. # wrapper2 = outer2(flag)
  36. #
  37. # @wrapper2 #func = w2(func) = w2(inner1) --->inner2
  38. # @wrapper1 #func = w1(func) ---> inner1
  39. # def func():
  40. # print('123')
  41. # ret ='hahha'
  42. # return ret
  43.  
  44. @outer2(flag) #func = w2(func) = w2(inner1) --->inner2
  45. @outer1(flag) #先执行 outer1装饰器函数 #func = w1(func) ---> inner1
  46. def func():
  47. print('123')
  48. ret ='hahha'
  49. return ret
  50.  
  51. ret = func() #func() ----> innner2()
  52. print(ret)
  53. print(func.__name__)
  1.  

  

  1.  

迭代器(iterator)

可以被for循环的就是可迭代的(iterable),所以说str,list,dic,tuple,set,文件句柄(f=open()),range(),enumerate都是可迭代的,同时他们中的双下方法都含有__iter__,因此只要是能被for循环的数据类型 就一定拥有__iter__方法,就一定是可迭代的。

[].__iter__()则返回的就是一个迭代器。,即可迭代的对象.__iter__就返回一个迭代器。迭代器里面重要的2个双下方法就是__iter__和__next__。所以只要有这2个内置方法就是一个迭代器。

Iterable 可迭代的 -- > __iter__   ;只要含有__iter__方法的都是可迭代的.

可迭代的对象.__iter__() 迭代器 -- > __next__ ;通过next就可以从迭代器中一个一个的取值。

只要含有__iter__方法的都是可迭代的 —— 可迭代协议

迭代器的概念:

迭代器协议 —— 内部含有__next__和__iter__方法的就是迭代器

迭代器协议和可迭代协议

可以被for循环的都是可迭代的

可迭代的内部都有__iter__方法

只要是迭代器 一定可迭代

可迭代的对象.__iter__()方法就可以得到一个迭代器。

迭代器中的__next__()方法可以一个一个的获取值;

for循环,就是在内部帮助可迭代对象变成一个迭代器,然后调用__next__方法来取值;其实就是在使用迭代器iterator可迭代对象直接给你内存地址;

只有可迭代对象才能for循环,因此在我们遇到一个新的变量的时候,不确定能不能用for循环的时候,就判断它是否可迭代.(通过dir(数据类型(变量))查看内部是否含有__iter__方法)

  1. # for i in l:
  2. # pass
  3. #iterator = l.__iter__()
  4. #iterator.__next__() 

迭代器的好处:

  1. 从容器类型中一个一个的取值,会把所有的值都取到。
    从而节省内存空间。
    迭代器并不会在内存中再占用一大块内存,
    而是随着循环,每次生成一个
    每次用next方法每次给我一个。

生成器------>迭代器(generator)

生成器表达形式:

1.生成器函数——本质上就是我们自己写的函数,只不过返回值要用yield 来返回;每__next__一次执行一次yield。

生成器函数()调用之后不执行函数里面的代码,而是返回 一个迭代器 (<generator object tail at 0x0000000009D01DB0>)

2.生成器表达式

Python中提供的生成器:

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,不会结束,以便下次重它离开的地方继续执行

  1. def func():
  2. for i in range(10):
  3. yield '娃哈哈%s'%i
  4.  
  5. a = func()
  6. print(a.__next__()) #娃哈哈0
  7. print(a.__next__()) #娃哈哈1
  8. print(a.__next__()) #娃哈哈2

2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

生成器Generator:

  本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

  特点:惰性运算,开发者自定义

生成器的2个特点:

1.一个生成器中的数据只能从头到尾读取一次,读取完了就没有了。

2.惰性运算是指不找它取值,它就不工作,而且找它要一个,它就给你一个。

生成器函数的列子(监听文件输入的列子)

  1. def tail(filename):
  2. f = open(filename,encoding='utf-8')
  3. while True:
  4. line = f.readline()
  5. if line.strip('\r\n'):
  6. yield line.strip() #把文件返回用作过滤文件的内容,不用return ,用了函数就执行结束了。
  7.  
  8. data=tail('file') #用生成器来过滤
  9. for i in data:
  10. if 'python' in i :
  11. print(i)

  

send方法

send 获取下一个值的效果和next基本一致;

只是在获取下一个值的时候,给上一yield的位置传递一个数据。

使用send的注意事项:第一次使用生成器的时候 是用next获取下一个值;最后一个yield不能接受外部的值

  1. def generator():
  2. print('123')
  3. ret = yield 1
  4. print('#####',ret)
  5. print('456')
  6. yield 2
  7. print('789')
  8. ret = yield 3
  9. print(ret)
  10. '''
  11.  
  12. '''
  13. yield
  14. g = generator()
  15. ret = g.__next__()
  16. print(ret)
  17. ret1 =g.send('marry') #send的效果和__next__效果一样,只不过send可以给上一个yield传一个值。
  18. print(ret1)
  19. ret = g.__next__()
  20. print(ret)

  

计算移动平均值的生成器,只不过每次第一步的时候要使用next方法;

  1. def average():
  2. sum = 0
  3. count = 0
  4. avg = 0
  5. while True:
  6. ret = yield avg
  7. sum += ret
  8. count += 1
  9. avg = sum/count
  10.  
  11. a = average()
  12. a.__next__()
  13. print(a.send(10))
  14. print(a.send(20))

因此有了预激生成器的装饰器,为了方便多个函数的时候,不用再外面写next方法‘

  1. def gener_waper(func):
  2. def inner(*args,**kwargs):
  3. ret = func(*args,**kwargs)
  4. ret.__next__()
  5. return ret
  6. return inner
  7.  
  8. @gener_waper
  9. def average():
  10. sum = 0
  11. count = 0
  12. avg = 0
  13. while True:
  14. ret = yield avg
  15. sum += ret
  16. count += 1
  17. avg = sum/count
  18.  
  19. a = average()
  20. print(a.send(10))
  21. print(a.send(20))

 

yield from 返回最小的字符,即拆分

  1. def generator():
  2. a = ['123456','aaaaa','aa']
  3. b = {'name':'zenghui','age':24}
  4. b = {'name': 'zenghui', 'age': 24}.items()
  5. yield from a #单个返回
  6. yield from b #若为字典,则返回k
  7.  
  8. g = generator()
  9. for i in g:
  10. print(i)

  

列表推导式和生成器表达式

1.把列表解析的[]换成()得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存,不占用内存.

  1. a = (i*i for i in range(10)) #生成器表达式
  2.  
  3. list = [ i for i in range(10)] #列表推导式
  4.  
  5. print(list) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  6. print(a) #<generator object <genexpr> at 0x0000000009D01DB0>
  7. for i in a:
  8. print(i)
  9. #1 4 9 16 25 36 49 64 81

完整的列表推导式:

1.遍历取所有的值

2.筛选功能

[   每一个元素或者是和元素的相关操作   for 元素 in  可迭代的数据类型 ]   # 遍历挨个取值

[   每一个元素或者是和元素的相关操作   for 元素 in  可迭代的数据类型  if  元素相关的条件 ]   #筛选功能

#输出30以内被3整除的数

  1. ret = [ i*i for i in range(30) if i%3==0]
  2. print(ret)

  

生成器相关的面试题:

  1. #生成器的面试题1
  2. # def genger():
  3. # for i in range(4):
  4. # yield i
  5. #
  6. # g = genger()
  7. #
  8. # g1 = ( i for i in g)
  9. # # print(list(g1))
  10. # # print(g1)
  11. # g2 = ( i for i in g1)
  12. # print(list(g2))

  

  1.  
  1. #面试题2
  2.  
  3. def add(n,i):
  4. return n+i
  5.  
  6. def gen():
  7. for i in range(4):
  8. yield i
  9.  
  10. g = gen()
  11.  
  12. for n in [1,10]:
  13. g = (add(n,i) for i in g)
  14. print(list(g))
  15.  
  16. # n = 1
  17. # g = (add(n,i) for i in gen()) #g = [0,1,2,3]
  18. # # print(list(g)) #一个生成器只能取一次,取完就没了
  19. # n= 10 # i :10,11,12,13 n :10
  20. # g = (add(n,i) for i in (add(n,i) for i in gen())) #没有取值还是一个生成器;
  21. # # for i in (0,1,2,3)
  22. # # (10,11,12,13)
  23. # # (20,21,22,23)
  24. # # print(list(g))
  25. # print(list(g)) #取值的时候才执行调用生成器 n = 10 ,g = [10,11,12,13], for i in g
  26.  
  27. for n in [1,10,5]: #碰到循环生成器的时候需要拆开分析
  28. g = (add(n, i) for i in g)
  29.  
  30. print(list(g))
  31.  
  32. # n = 1
  33. # g = (add(n, i) for i in gen())
  34. # n = 10
  35. # g = (add(n, i) for i in (add(n, i) for i in gen()))
  36. # n = 5
  37. # g = (add(n, i) for i in (add(n, i) for i in (add(n, i) for i in gen())))
  38. # # (0,1,2,3)
  39. # # (5,6,7,8)
  40. # # (10,11,12,13)
  41. # # (15,16,17,18)
  42. # print(list(g)) #取值,生成器里面的代码才执行
  1.  

  

  1.  

 

  

python 基础知识点二的更多相关文章

  1. Python 基础语法(二)

    Python 基础语法(二) --------------------------------------------接 Python 基础语法(一) ------------------------ ...

  2. python基础知识(二)

    python基础知识(二) 字符串格式化 ​ 格式: % 类型 ---- > ' %类型 ' %(数据) %s 字符串 ​ print(' %s is boy'%('tom')) ----> ...

  3. Python基础学习二

    Python基础学习二 1.编码 utf-8编码:自动将英文保存为1个字符,中文3个字符.ASCll编码被囊括在内. unicode:将所有字符保存为2给字符,容纳了世界上所有的编码. 2.字符串内置 ...

  4. 最全Python基础知识点梳理

    本文主要介绍一些平时经常会用到的python基础知识点,用于加深印象,也算是对于学习这门语言的一个总结与回顾.python的详细语法介绍可以查看官方编程手册,也有一些在线网站可以学习 python语言 ...

  5. Python之路:Python 基础(二)

    一.作用域 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. if 1==1: name = 'lenliu' print name 下面的结论对吗?(对) 外层变量,可以被 ...

  6. Python基础知识点总结

    Python基础知识与常用数据类型 一.Python概述: 1.1.Python的特点: 1.Python是一门面向对象的语言,在Python中一切皆对象 2.Python是一门解释性语言 3.Pyt ...

  7. Python基础篇(二)_基本数据类型

    Python基础篇——基本数据类型 数字类型:整数类型.浮点数类型.复数类型 整数类型:4种进制表示形式:十进制.二进制.八进制.十六进制,默认采用十进制,其他进制需要增加引导符号 进制种类 引导符号 ...

  8. Python基础知识点小结

    1.Python基础知识 在Python中的两种注释方法,分别是#注释和引号('''   ''')注释,#注释类似于C语言中的//注释,引号注释类似于C语言中的/*   */注释.接着在Python中 ...

  9. python 基础知识点整理 和详细应用

    Python教程 Python是一种简单易学,功能强大的编程语言.它包含了高效的高级数据结构和简单而有效的方法,面向对象编程.Python优雅的语法,动态类型,以及它天然的解释能力,使其成为理想的语言 ...

随机推荐

  1. mysql limit 性能问题分析

    问题重现 // todo 参考文章: MySQL 单表分页 Limit 性能优化 Scalable MySQL: Avoid offset for large tables 证明为什么用limit时, ...

  2. Java应用之shiro

    Apache Shiro是一个强大而灵活的开源安全框架,它能够干净利落地处理身份认证,授权,企业会话管理和加密. 以下是你可以用 Apache Shiro所做的事情: 1.验证用户 2. 对用户执行访 ...

  3. git链接到远程github上

    Git链接到自己的Github(1)简单的开始 好长时间没上来弄东西了,今天回来先开始弄下Git,之后再继续写uboot与kernel的编译,在版本控制下更加宏观地观察每次的变化. 1.在ubuntu ...

  4. ubuntu14.04 LTS 更新国内网易163源

    2015/10/7 更改ubuntu的默认源是linux学习中必须掌握的基础技能.在此记录,以作参考. 在ubuntu14.04 LTS默认使用的是国外源,由于网络的原因,使用apt-get安装包时异 ...

  5. js读取解析JSON类型数据【申明:来源于网络】

    js读取解析JSON类型数据[申明:来源于网络] 地址:http://blog.csdn.net/sunhuaqiang1/article/details/47026841

  6. Codeforces 670 - A/B/C/D/E/F - (Done)

    链接:https://codeforces.com/contest/670 A - Holidays - [水] AC代码: #include<bits/stdc++.h> using n ...

  7. 【摘】Fiddler工具使用介绍

    摘自:https://www.cnblogs.com/miantest/p/7289694.html Fiddler基础知识 Fiddler是强大的抓包工具,它的原理是以web代理服务器的形式进行工作 ...

  8. 阿里云api调用做简单的cmdb

    阿里云api调用做简单的cmdb 1 步骤 事实上就是调用阿里api.获取可用区,比方cn-hangzhou啊等等.然后在每一个区调用api 取ecs的状态信息,最好写到一个excel里面去.方便排序 ...

  9. ES6 字符串

    拓展的方法 子串的识别 ES6 之前判断字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的识别方法. includes():返回布尔值,判断是否找到参数字符串. startsWith( ...

  10. word2vec:将bin转换为txt

    转自:https://blog.csdn.net/u011684265/article/details/78024064 from gensim.models import word2vec mode ...