python成长之路七-函数的进阶
1,python中,名称空间分三种:
-
全局命名空间
-
局部命名空间(临时命名空间)
-
内置名称空间
2,作用域(两种):
1,全局作用域 包含:全局名称空间 内置名称空间
2,局部作用域 包含:局部命名空间
2.1,取值顺序:
就近原则:局部名称空间 --> 全局名称空间 --> 内置名称空间 单向从小到大范围
2.2, 加载顺序:
内置名称空间 --> 全局名称空间(当程序执行时) -- > 局部名称空间(当函数调用的时候)
3,函数的嵌套:
def func1(): print(111) def func2(): print(222) func1() print(333) print(666) func2() print(555)
def func1(): print(222) def func2(): print(333) print(111) func2() print(666) func1() # 执行结果: 222 111 333 666
4,global nonlocal
局部名称空间对全局名称空间的变量可以引用,但是不能改变。
count = 1 def func1(): count = 2 print(count) func1() count = 1 def func1(): # count = 3 count = count + 1 # local variable 'count' referenced before assignment print(count) func1()
报错原因: 如果你在局部名称空间 对一个变量进行修改,那么解释器会认为你的这个变量在局部中已经定义了,但是对于上面的例题,局部中没有定义,所以他会报错,
global
1,在局部名称空间声明一个全局变量。
2,在局部名称空间声明可以对全局变量进行修改。
count = 1 def func1(): global count count = count + 1 print(count) func1() print(count)
nonlocal
1,子函数对父函数的变量进行修改。
2,此变量不能是全局变量
3,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
def func1(): count = 666 def inner(): print(count) def func2(): nonlocal count count += 1 print("func2",count) func2() print("inner",count) inner() print("funcl",count) func1() # 运行结果: 666 func2 667 inner 667 funcl 667
5,函数的应用
1,函数名就是函数的内存地址。
def func(): pass print(func) # <function func at 0x05958660>
2,函数名可以作为变量。
def func1(): print(666) f1 = func1 f2 = f1 f2()
3,函数名可以作为函数的参数。
def func(): print(666) def func1(): func() def func2(x): x() func2(func1)
4,函数名可以当作函数的返回值。
def wraaper(): def inner(): print("inner") return inner ret = wraaper() ret()
5,函数名可以当作为容器类类型的元素。
def func1(): print("in func1") def func2(): print("in func2") def func3(): print("in func3") def func4(): print("in func4") l1 = [func1,func2,func3,func4] for i in l1: i()
向上面的函数名这种,第一个类对象。
6,globals() locals()
1,globals() 返回全局变量的一个字典。
2,locals() 返回当前位置的局部变量的字典。
def funcl(): a = 2 b = 3 # print(globals()) # print(locals()) def inner(): c = 5 d = 6 print(globals()) print(locals()) inner() print(globals()) print(locals()) funcl()
7,闭包
--内层函数对外层函数的变量(非全局变量)的引用,并返回。这样就形成了闭包。
1,迭代器的条件:
1,必须要有函数嵌套。
2,内层函数必须要引用外层函数中的变量(不能是全局变量)。
3,外层函数必须返回内部中的函数名(引用)。def wraaper(): n = 1 def inner(): nonlocal n n += 1 return n # inner() return inner f = wraaper() print(f()) print(f())
2,闭包的作用:
当程序执行时,遇到了函数执行,他会在内存开辟一个空间,局部名称空间,
如果这个函数内部形成了闭包,
那么他就不会随着函数的结束而消失。
# 1,写装饰器的时候用。 # 2,写爬虫的时候用。 from urllib.request import urlopen def index(): url = "http://www.xiaohua100.cn/index.html" def get(): return urlopen(url).read() return get xiaohua = index() # get content = xiaohua() # get() content = xiaohua() # get() print(content.decode('utf-8'))
8,迭代器
1,可迭代对象
对象内部含有__iter__方法的就是可迭代对象。
可迭代对象满足可迭代协议。
2,判断是否是可迭代对象。
dic = {"name":"alex"} print('__iter__' in dir(dic))
from collections import Iterable from collections import Iterator print(isinstance("alex",Iterable)) print(isinstance('alex',Iterator)) print(isinstance("alex",str))
3,可迭代对象 vs 迭代器
可迭代对象不能取值,迭代器是可以取值的。
可迭代对象 可以转化成迭代器。
lis = [1,2,3] # 可迭代对象 itet = lis.__iter__() #迭代器 itet = iter(lis) # 迭代器 print(itet)
可迭代器如何取值?
next一次,取值一次
1,可迭代对象不能取值,迭代器是可以取值的。
2,迭代器非常节省内存。
3,迭代器每次只会取一个值。
4,迭代器是单向的从上至下地取值,一条路走到头。
1,迭代器原理
1,将可迭代对象转化成迭代器。
2,调用__next__方法取值。
3,利用异常处理停止报错。
s1 = "asdfg" iter1 = s1.__iter__() while 1: try: print(iter1.__next__()) except StopIteration: break
s1 = "asdfg" # l1 = [i for i in range(100)] # print(11) ret = (i for i in range(10000)) print(ret.__next__()) print(ret.__next__()) print(ret.__next__())
9, 生成器
--就是自己python用代码写的迭代器,生成器的本质就是迭代器。
1,构建一个生成器的两种方式:
1,通过生成器函数。
2,生成器表达式。
生成器函数:
def func1(x): x += 1 return x func1(5) # 函数的执行命令,并且接受函数的返回值 print(func1(5))
def func1(x): x += 1 print(111) print(111) print(111) yield x x += 2 print(222) yield "alex" x += 3 g_obj = func1(5) # 生成器函数对象 # print(g_obj) # print(g_obj.__next__()) print(g_obj.__next__())
一个 naxt 对应一个 yield
yield 将值返回给生成器对象 .__next__()
yield 和 return区别
return 结束函数,给函数的执行者返回值
yield 不会结束函数,一个next 对应一个yield,给生成器对.__next__()返回值。
生成器函数 和 迭代器 区别
1,自定制的区别
# li = [1,2,3,4,5] # li.__iter__() def func1(x): x += 1 yield x x += 3 yield x x += 5 yield x g1 = func1(5) print(g1.__next__()) print(g1.__next__()) print(g1.__next__())
2,内存级别的区别
迭代器是需要可迭代对象进行转化。可迭代对象非常占内存。
生成器直接创建,不需要转化,从本质就节省内存。
def func1(): for i in range(10000): yield i g1 = func1() for i in range(50): print(g1.__next__()) # ———————————————————————— def func1(): print(1) count = yield 6 print(count) print(2) count1 = yield 7 print(count1) print(3) yield 8 g = func1() next(g) g.send("alex") # g.send("太白") print(g.__next__())
send 与 naxt 区别
send与next一样,也是对生成器取值(执行一个yield)的方法。
send可以给上一个yield传值。
第一次取值永远都是next。
最后一个yield永远也得不到send传的值。
# 函数 def cloth1(n): for i in range(n+1): print("衣服%s号" % i) cloth1(1000) # -------------------------------- # 生成器函数 def cloth1(n): for i in range(1,n+1): yield ("衣服%s号" % i) g = cloth1(1000) for i in range(50): print(g.__next__()) # for i in range(50): # print(g.__next__())
列表推导式:
-- 一行代码几乎搞定你需要的任何的列表。
优点:一行解决,方便
缺点:容易着迷,不易排错,不能超过三次循环。
列表推导式不能解决所有列表问题,所以不要太刻意用。
循环模式 [变量(加工后的变量)for 变量 initerable]
l = [i for i in range(1,101)] print(l) l2 = print(["python%s期" % i for i in range(1,11)]) print([i*i for i in range(1,11)])
筛选模式 [变量(加工后的变量)in iterable if 条件]
l3 = [i for i in range(1,31) if i % 2 == 0] print(l3) print([i for i in range(1,31) if i % 3 == 0]) print([i**2 for i in range(1,31) if i % 3 == 0]) names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] # 请选出名字中带两个e的 print([j for i in names for j in i if j.count("e") == 2])
生成器表达式: 将列表推导式的 [ ] 换成 ( ) 即可。
g = (i for i in range(100000)) print(g) print(g.__next__()) print(g.__next__()) for i in range(20): # 循环取取值 print(g.__next__())
mcase = {"a":10,"b":34} mcase_frequency = {mcase[k]: k for k in mcase} # 将键值对反转 print(mcase_frequency)
squared = {x**2 for x in [1,-1,2]} # 将集合去重 print(squared)