Python全棧之作用域和閉包

1. return返回值

# ### return 自定義函數的返回值
"""
概念:return 把函數內部的數據返回到函數的外面,返回到函數的調用處
1.return + 六大標準數據類型 , 除此之外還可以返回函數 或者 是類對象
2.return 在執行時,意味著終止函數,後面的代碼不執行.
3.如果不定義return返回值,默認返回None
"""
# (1) return + 六大標準數據類型
def func():
	# return 111
	# return 6.89
	# return "你好帥啊,我愛死你樂"
	# return [1,2,3]
	# return {"a":1,"b":2}
	return 1,2,3 # 返回元組
res = func()
print(res)
# (2) return 在執行時,意味著終止函數,後面的代碼不執行.
def func():
	print(1)
	print(2)
	return 3
	print(4)
res = func()
print(res)
def func():
	for i in range(5):
		if i == 3:
			return 4
		print(i)
res = func()
print(res)
# (3) 如果不定義return返回值,默認返回None
def func():
	pass
res = func()
print(res) # None
# 註意點 打印的數據和返回的數據不是等價的,返回的數據是可以自定義的;
res = print(1234)
print(res)  # None

# 模擬+-*/計算器
"""
功能:   完成計算
參數:   2個數字和運算符
返回值: 計算後的結果
"""
def calc(num1,num2,sign):
	if sign == "+":
		return num1 + num2
	elif sign == "-":
		return num1 - num2
	elif sign == "*":
		return num1 * num2
	elif sign == "/":
		if num2 == 0:
			return "除數不能為零"
		return num1 / num2
	else:
		return "抱歉,超出瞭我的運算范圍."
res = calc(3,5,"+")
res = calc(3,5,"-")
res = calc(3,5,"*")
res = calc(3,0,"/")
res = calc(3,0,"&")
print(res)

2. 全局變量_局部變量

# ### 全局變量和局部變量
"""
1.概念
	局部變量:在函數內部定義的變量就是局部變量
	全局變量:在函數外部定義的變量或者在函數內部使用global關鍵字聲明是全局變量
2.作用域:
	局部變量的作用范圍僅僅在函數的內部
	全局變量的作用范圍橫跨整個文件
3.生命周期:該變量的作用時長
	內置命名空間 -> 全局命名空間  -> 局部命名空間 (開辟空間順序)
	內置屬性 > 全局屬性 > 局部屬性 (作用時長:長->短)
"""
# 1 局部變量
def func():
	# 定義一個局部變量
	a = 1
	# 獲取當前的局部變量
	print(a)
	# 修改一個局部變量
	a = 2
	print(a)
func()
# print(a) error
# 2.全局變量
# 定義一個全局變量
b = 10
# 獲取當前的全局變量
print(b)
# 修改一個全局變量
b = 20
print(b)
def func():
	print(b)
func()
# 3.函數內部定義全局變量
def func():
	global c
	c =30
func()
print(c)
# 4.函數內部修改全局變量
d = 50
def func():
	global d
	d = 51
func()
print(d)
"""
總結:global的使用
如果當前不存在全局變量,可以在函數內部通過global關鍵字來定義全局變量
如果當前存在全局變量,可以在函數內部通過global關鍵字來修改全局變量
"""

請添加圖片描述

3. 函數名的使用

# ### 函數名的使用
"""
# python中的函數可以像變量一樣,動態創建,銷毀,當參數傳遞,作為值返回,叫第一類對象.其他語言功能有限
"""
def func():
	print( "我是func函數")
# (1)動態創建
a = 1
print(a)
a = func
a()
# (2)動態銷毀
del a
# a()
# func()
# (3)當參數傳遞
def func2():
	return "我是func2函數"
def func1(f):
	return f() # "我是func2函數"
res = func1(func2)
print(res)
# (4)作為值返回
def func3():
	print( "我是func3函數" )
def func4(f):
	return f
res = func4(func3)	
print(res)
res()
print("<===>")
# (5)函數名可以作為容器類型數據的元素
lst = [func,func3]
for i in lst:
	i()
print("<=========>")
# ### __doc__ 或者help查看文檔
def big_chang_cishen(something):
	"""
	功能: 教你怎麼吃大腸
	參數: 吃的內容
	返回值: 是否滿意
	"""
	print("把{}洗一洗".format(something))
	print("直接找腸子頭,放嘴裡,吸一下")
	print("擦擦嘴,滿意的放下腸子頭")
	return "吃完瞭,真好吃~"
big_chang_cishen("生腸子")
# 方法一
res = big_chang_cishen.__doc__
print(res)
# 方法二
help(big_chang_cishen)

4. 函數的嵌套

4.1 函數的嵌套

# ### 函數的嵌套
"""
互相嵌套的兩個函數![請添加圖片描述](https://img-blog.csdnimg.cn/f3ab3fd8502e43eebd473306c0e28633.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA54as5aSc5rOh5p645p2e,size_20,color_FFFFFF,t_70,g_se,x_16)
:
	包裹在外層的叫做外函數,內層的就是內函數
"""
def outer():
	# inner()
	def inner():
		print("我是inner函數")
""""""
# (1)內部函數可以直接在函數外部調用麼 不行
# inner()
# (2)調用外部函數後,內部函數可以在函數外部調用嗎 不行
# outer()
# inner()
# (3)內部函數可以在函數內部調用嗎 可以
outer()
# (4)內部函數在函數內部調用時,是否有先後順序 有的
# 先定義在調用
# 在其他語言中有預加載的機制,提前把函數駐留到內存中,然後再去編譯腳本內容
# python沒有預加載函數的機制,隻能先定義在調用;

# 外函數是outer  中間函數是inner  最裡層是smaller ,調用smaller函數
def outer():
	def inner():
		def smaller():
			print("我是smaller函數")
		smaller()
	inner()
outer()

# LEGB 原則
def outer():	
	def inner():
		def smaller():	
			print(a)
		smaller()
	inner()
outer()
"""
LEGB原則(就近找變量原則)
#找尋變量的調用順序采用LEGB原則(即就近原則)
B —— Builtin(Python);Python內置模塊的命名空間      (內建作用域)
G —— Global(module); 函數外部所在的命名空間        (全局作用域)
E —— Enclosing function locals;外部嵌套函數的作用域(嵌套作用域)
L —— Local(function);當前函數內的作用域            (局部作用域)
依據就近原則,從下往上 從裡向外 依次尋找
"""

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

4.2 nonlocal的使用

# ### nonlocal的使用 (用來修改局部變量)
"""
nonlocal遵循LEGB原則
(1) 它會找當前空間上一層的變量進行修改
(2) 如果上一層空間沒有,繼續向上尋找
(3) 如果最後找不到,直接報錯
"""
# (1)它會找當前空間上一層的變量進行修改
def outer():
	a = 10
	def inner():
		nonlocal a
		a = 20
		print(a)
	inner()
	print(a)
outer()
# (2)如果上一層空間沒有,繼續向上尋找
def outer():
	a = 20
	def inner():
		a = 15
		def smaller():
			nonlocal a
			a = 30
			print(a)
		smaller()
		print(a)
	inner()
	print(a)
outer()
# (3)如果最後找不到,直接報錯
"""nonlocal 隻能修改局部變量,"""
"""
a = 20
def outer():	
	def inner():
		def smaller():
			nonlocal a
			a = 30
			print(a)
		smaller()
		print(a)
	inner()
	print(a)
outer()
error
"""

# (4) 不通過nonlocal 是否可以修改局部變量呢?ok
def outer():
	lst = [1,2,3]
	def inner():
		lst[-1] = 3000
	inner()
	print(lst)
outer()

5. 閉包函數的定義

# ### 閉包函數
"""
互相嵌套的兩個函數,如果內函數使用瞭外函數的局部變量
並且外函數把內函數返回出來的過程,叫做閉包
裡面的內函數叫做閉包函數
是不是閉包?
	1.內函數用瞭外函數的那個局部變量
	2.外函數返回內函數
"""
# 1.基本語法形式
def zhaoshenyang_family():
	father = "馬雲"
	def hobby():
		print("我對錢沒有一絲絲的興趣,我不看重錢,這是我爸爸{}說的".format(father))
	return hobby
func = zhaoshenyang_family()
func()
print("<==1==>")
tup = func.__closure__
print(tup[0].cell_contents) # 馬雲
print(tup)
print("<==2==>")
# 2.閉包的復雜形式
def zhaowanli_family():
	gege = "王思聰"
	didi = "鞋王,高振寧"
	money = 1000
	def gege_hobby():
		nonlocal money
		money -= 500
		print("我交朋友不在乎他有沒有錢,反正都沒有我有錢.我就喜歡交女朋友... 錢物還剩下{}".format(money))
	def didi_hobby():
		nonlocal money
		money -= 400
		print("傢裡有鞋櫃,各式各樣的奢侈鞋,一雙大概20~30萬,錢物還剩下{}".format(money))
	def big_master():
		return [gege_hobby,didi_hobby]
	return big_master
func = zhaowanli_family()
print(func)
lst = func()
print(lst)
# 獲取哥哥函數
gege = lst[0]
gege()
# 獲取弟弟函數
didi = lst[1]
didi()
# 3.使用 __closure__ , cell_contents 判定閉包
"""如果返回的元組中有數據說明是閉包,誰的生命周期被延長就打印誰"""
tup = func.__closure__
print(tup)
# 先獲取第一個單元格  cell_contents獲取對象中的內容
func1 = tup[0].cell_contents
print("<11111>")
"""打印閉包函數didi_hobby中,生命周期被延長的屬性"""
print(func1.__closure__[0].cell_contents)
func1()
# 在獲取第二個單元格  cell_contents獲取對象中的內容
func2 = tup[1].cell_contents
print("<22222>")
"""打印閉包函數gege_hobby中,生命周期被延長的屬性"""
print(func2.__closure__[0].cell_contents)
func2()

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

6. 閉包的特點_意義

# ### 閉包特點
"""
特點:在閉包函數中,內函數使用瞭外函數的局部變量,
該變量會與內函數發生綁定,延長該變量的生命周期,
持續到腳本執行結束.
"""
def outer(val):
	def inner(num):
		return val + num
	return inner
func = outer(10)
res = func(15)
print(res)

# ### 閉包的意義
"""全局變量的作用域大,容易被篡改"""
num = 0
def click_num():
	global num
	num += 1 # num = num + 1
	print(num)
click_num()
click_num()
click_num()
num = 100
click_num()
click_num()
# 改造,用閉包來實現
"""
閉包的意義:
	閉包可以優先使用外函數中的變量,並對閉包中的值起到瞭封裝保護的作用.外部無法訪問.
"""
def outer():
	x = 0
	def click_num():
		nonlocal x
		x += 1
		print(x)
	return click_num
click_num = outer()
click_num()
click_num()
click_num()
x = 100
click_num()
click_num()

小提示:

def outer():
      a = 10
      def inner():
            a = 20
            print(a)
       inner()
       print(a)
outer()
這裡的輸出結果是20 10,嵌套裡的兩個a變量互不幹擾

列表可以直接可以在內外函數直接傳遞值,修改列表
的時候不需要使用nolocal來修改變量的值。
閉包可以延長局部變量的周期  
沒辦法在函數的內部去修改一個全局變量的,必須通過一個global(跟nonlocal一個道理的)

7. 小練習

# # 1.定義函數:接收任意個參數,打印其中的最小值
def func(*args):
	lst = []
	for i in args:
		if isinstance(i , (float,int)):
			lst.append(i)
	print(lst)
	return lst[0]
res = func(-100,1,2,423,"sdf")
print(res)
# 2.定義函數:傳入一個參數n,返回n的階乘(5! = 5*4*3*2*1)
def func(n):
	total = 1
	for i in range(n,0,-1):
		total *= i
	return total
print(func(5))
# 3.寫函數,傳入函數中多個實參(均為可迭代對象如字符串,列表,元祖,集合等)
# # 將容器中的每個元素依次添加到新的列表中返回
#例:傳入函數兩個參數[1,2,3] (22,33)最終args為(1,2,3,22,33)
# 方法一
def func(*args):
	lst =[]
	for i in args:
		for j in i:
			lst.append(j)
	return lst
res = func([1,2,3],(5,6,7),"abc")
print(res)
# 方法二
def func(*args):
	return list(args)
res = func(*[1,2,3],*(5,6,7),*"abc")
print(res)

# 4.寫函數,用戶傳入要修改的文件名,與要修改的內容,執行函數,修改操作
# 方法一
def func(filename,str1,str2):
	with open(filename,mode="r+",encoding="utf-8") as fp:
		strvar = fp.read()
		print(strvar)
		res = strvar.replace(str1,str2)
	with open(filename,mode="w+",encoding="utf-8") as fp:
		fp.write(res)
func("ceshi2.py","內置函數","外置函數")
# 方法二
def func(filename,str1,str2):
	with open(filename,mode="r+",encoding="utf-8") as fp:
		strvar = fp.read()
		res = strvar.replace(str1,str2)
		# print(fp.tell())
		fp.seek(0)
		# 清空
		fp.truncate()
		fp.write(res)
func("ceshi2.py","外置函數","內置函數")

# 5.寫函數,計算傳入字符串中【數字】、【字母】、【空格] 以及 【其他】的個數
# 方法一
def func(strvar):
	dic = {"num":0,"word":0,"space":0,"other":0}
	for i in strvar:
		if i.isdecimal():
			dic["num"] += 1 # dic["num"] = dic["num"] + 1
		elif i.encode().isalpha():
			dic["word"] += 1
		elif i.isspace():
			dic["space"] += 1
		else:
			dic["other"] += 1
	return dic

# strvar = input("請輸入字符串")
# print(func(strvar))
"""
print("你".isalpha())
# 中文 => False
print("你".encode().isalpha())
# 字母 => True
print("a".encode().isalpha())
"""
# 方法二
def func(strvar):
	dic = {"num":0,"word":0,"space":0,"other":0}
	lst = []
	for i in range(97,123):
		lst.append(chr(i))
		lst.append(chr(i-32))
	for i in strvar:
		if i in "0123456789":
			dic["num"] += 1
		elif i in lst:
			dic["word"] += 1
		elif i == " ":
			dic["space"] += 1
		else :
			dic["other"] += 1
	return dic
# strvar = input("請輸入字符串")
# print(func(strvar))
# 6.寫函數,檢查字典的每一個value的長度,如果大於2,那麼僅保留前兩個長度的內容,返回處理後的結果.
	#例:參數為:dic = {"k1": "v1v1", "k2": [11,22,33,44]}
def func(dic):
	if isinstance(dic,dict):
		for k,v in dic.items():
			print(k,v)
			dic[k] = v[:2]
		return dic
	else:
		return "不是字典"
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
print(func(dic))
print(func([1,23,42,34,23,4234]))

# 7傳入多個容器類型數據,計算所有元素的個數
def func(*args):
	total = 0 
	for i in args:
		print(i)
		total += len(i)
	return total
res = func("123",[5,6,7],("你好","123423"))
print(res)
# 改造,不去判斷字符串本身的長度
def func(*args):
	total = 0 
	for i in args:
		print(i)
		if isinstance(i,str):
			total += 1
		elif isinstance(i,(tuple,list,set,dict)):
			total += len(i)
	return total
res = func("123",[5,6,7],("你好","123423"))
print(res)

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: