名称空间
顾名思义就是存放名字的地方,比如,若变量x=1,1存放在内存里,那x存在哪里?名称空间就是存放名字x与1绑定关系的地方
x:内存地址 1所在的内存
名称空间共3种,分别如下:
locals:是函数内的名称空间,包括局部变量和形参
globals:全局变量,函数定义所在模块的名字空间
builtins:内置模块的名字空间
作用域的查找空间
作用域的查找顺序
LEGB
L:locals :函数的名字空间,包括局部变量和形参
E:enclosing 相邻的上一级 外部嵌套函数的名字空间
G:globls 全局变量,函数定义所在的模块名字空间
B:builtins 内置模块的名字空间
闭包
举例:
def func(): n = 10 def func2(): print("n=",n) return func2 f = func() f()
从理论上说,当f = func()时候,func已经关闭,那么n = 10这个内部局部变量就应该释放了,但是f()仍能够打印出10,这是为什么呢?
这就是闭包的概念
整改代码发生的现象就是一个闭包
f2是放在f里面,原来是根本拿不到f2,如果要执行只能在f里面执行
但是现在 f返回了f2名称 相当于在外部拿到了里面的函数。
即 func如果返回了内部的一个东西,并且还在使用中,那么他的内存空间不会释放的。
我们在外部可以执行内部的函数,并且可以用内部函数作用域里面的所有值,这就是闭包
原来f2放在func里面,在外面是不知道f2存在,只能在外面执行,但是把内层函数名称返回,在外部拿到了里面的函数,那其实func返回内部东西,还在使用中,内存空间没被释放。效果:我们在外部可以执行内部函数,并且可以用他内部函数作用域里的所有的值。
装饰器
代码有两个原则
开放-封闭原则:开放 对现有功能的扩展开放 封闭:已实现的功能代码块不应该被修改
不改变调用方式
在符合开放封闭原则的情况下,给代码加新功能
# -*- coding:utf-8 -*- def login(func): def inner(): _username = '111' _password = '222' global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcom login...") user_status = True else: print("wrong!") if user_status == True: func() return inner def home(): print("---首页----") #home = login(home()) @login def america(): print("----欧美----") def japan(): print("----日本----") @login def henan(): print("----河南----") user_status = False home() america() henan()
个人理解:通过高阶嵌套函数,将原来的代码块进行包装
home = login(home()) 此home非旧home,
如果遇到传参情况,为了方便装饰器能用多个代码块上,则需要传入非固定参数 *args **args,注意返回方法同样用**
# -*- coding:utf-8 -*- def login(func): def inner(*args,**kwargs): _username = '111' _password = '222' global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcom login...") user_status = True else: print("wrong!") if user_status == True: func(*args,**kwargs) return inner def home(): print("---首页----") #home = login(home()) @login def america(): print("----欧美----") def japan(): print("----日本----") @login def henan(style): print("----河南----") user_status = False home() america() henan('3p')
如果装饰器带参数,那么需要再套一层函数
# -*- coding:utf-8 -*- def login(auth_type): def outer(func): def inner(*args,**kwargs): _username = '111' _password = '222' global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcom login...") user_status = True else: print("wrong!") if user_status == True: func(*args,**kwargs) return inner return outer def home(): print("---首页----") #home = login(home()) def america(): print("----欧美----") def japan(): print("----日本----") @login('qq') def henan(style): print("----河南----") user_status = False home() america() henan('3p')
银角大王的解释我觉得很容易懂
@login def henan(): pass #在这里 @ 相当于把下面的函数henan当做一个参数,传给了login这个方法,那么 @login('qq') def henan(): pass #这里 就相当于将函数henan当做参数,传给了login('qq')这个执行后的return的函数
装饰器需要重点复习
列表生成式
a = [2,3,4,5,6,7,8,9] b = a.copy() c=a.copy() for i in range(len(a)): a[i]+=1 print(a) #列表生成式 b = [i+1 for i in b ] print(b) #高级一点例子 c = [i if i<6 else i*i for i in a] print(c)
继续用银角大王的总结:
for i in a 做一个循环,前面相当于循环缩进下面得每一个i 每循环一次前面都会执行一次,结果会当做元素挨个存放,这时候,a 就可以循环任何东西,列表元组字典字符串等等。
这个只能写到列表里和元组里
生成器
假设一个场景,我需要从一个列表中取数据,而这个列表可能长度是10或者100万,这样的话难道要预先生成一个100万长度的列表吗?不会的,因为非常占用内存空间
解决办法:生成器
如果我知道一个列表的规律,那么我就可以制定一个规则,当我需要什么值时候,我再通过规则去生成这个值,这样就避免了空间浪费。
a = (i for i in range(5)) >>> < generator object<genexpr> at 0xkasjdfljals> #这就是一个生成器generato #如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值: next(a) #1.只能往后输出,不能回头 #2.超出边界,报错 #生成器保存的是一个算法,超出边界就会跑出stopIteration错误
生成器调用方式
用next()一个一个调很不现实,一般用for循环调
a = (i for i in range(5)) for i in a3: print(i)
与next区别,超过范围不报错,直接跳出循环
这个会报错
while True: next(a3)
range(100) <<底层就是生成器 python2里打印,python3里就是> range(0,100) python2里替代range的:xrange()
斐波那契数列
小注意点:
a = 1,b = 2
a,b= b,a+b
此时a和b是多少?答案是 2,和3
为什么呢 ?不知道
函数调用
def fib(max): n,a,b = 0,0,1 while n < max: yield b #通过函数生成了一个生成器 这个语法相当于 :出现了yield,就相当于将这个函数变成了生成器 函数执行到这里就返回了(冻结,函数没有结束) 通过next(f) 函数才能继续往下走。。把函数的执行过程冻结在这一步,并且把b的值返回给外面的next() a,b = b,a+b n = n+1 return 'done' print(fib(15)) f = fib(15) >><generator ...>
变成了一个生成器
print(next(f)) ... 0 0 1 2 3 5..
刚开始执行时候,f = fib(15) python将他变成一个生成器
只有next(f) 从函数开始,到yield结束
下一次next(f) ,则从上一次yield后面开始,执行到这一次yield之前。
好处是,函数执行-等结果
如果这样 函数执行一小会儿,把函数执行过程中的某个状态,返回出来。原来只能到结束,现在可以函数执行过程中返回到外部,多次
总结:
1。函数内加了yield 函数名加括号,函数根本不执行,只是生成了一个生成器对象
2.yield把函数的执行过程冻结在这一步(没有结束)
3。并且把b的值返回给外面的next()
for i in a:
循环这个生成器,不用next方法,不会报错
while...
用next方法,会报错
range(10) python2 :[0,1,2,3,4,5,6,7,8,9]
python3:range(0,10)
python2中有个方法跟python3中range一样:xrange
生成器的创建方式
1.类似列表生成式 []>>>() 最复杂的方式只能写一个三元运算
2.函数生成器
def range2(n): count = 0 while count < n: print(count) count += 1 range2(10)
next(f) 就等于 f.__next__()
如果这样
def range2(n): count =0 while count<n count+=1 yield count print(----) return x >>>>>会报错
总结:
python2
range = list
xrange = 生成器generator
python3
range = 生成器generator
xrange 没有
yield vs return
return 返回并终止function
yield 返回数据,并冻结当前的执行过程
next 唤醒冻结的函数执行过程,继续执行,直到遇到下一个yield
1.函数有了yield之后,函数名加()就得到了一个生成器,生成器就必须next(开始执行,如果终结了还能唤醒
2 return在生成器里代表生成器的终止,直接报错
如果需要中断,range.send("xxx") 传给yield
sign = yield asef
通过判断sign 可以break可以return退出
迭代器
理解:迭代一次=循环一次
可以直接作用于for循环的数据类型
一类是集合类型:list tuple dict set str
一类是生成器,包裹开yield的generator function
可以直接作用于for循环的对象统称为可迭代对象 Iterable()注意这是可迭代对象 Able
可以使用.isinstance()判断一个对象是否是Iterable对象 isinstance('abc',Iterable)>>>>True
一切皆对象
:::可以被next()调用并且返回下一个值的对象成为迭代器Iterator 这个是迭代器 Tor
(生成器 是 迭代器的一种)
迭代器都是可迭代对象
可迭代对象不都是迭代器
集合类就不是
如何将可迭代对象转化为迭代器?
通过iter方法
isinstance('abc',Iterator)>>>>False
str = iter('abc')
这样 str 就是迭代器,可以使用next()方法,
str.__next__()>>
'a'
'b'
'c'
traceback....
迭代器对象是一个数据流,迭代器对象可以被next()函数调用并不断返回下一个数据,知道没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序的序列,但我们却不知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以迭代器的对象都是惰性的,只有在需要返回下一个数据时他才会计算
迭代器甚至可以表示一个无限大的数据流,列入全体自然数,而list永远不可能存储所有自然数。。
总结:迭代器和可迭代
总结:
凡是可以for循环的 都是可迭代对象 iterable
凡是可作用于next()的 都是Iterator对象 迭代器对象
集合数据类型 list tuple dict str 是可迭代对象,但不是迭代器,可以通过iter()获得一个迭代器对象
Python3的for循环本质上就是通过不断调用next()函数来实现的