1. 程式人生 > >Python 5.函數作用域與遞歸(基礎篇)

Python 5.函數作用域與遞歸(基礎篇)

pan 局部作用域 兩種方法 char 復制代碼 表示 基礎語法 int 算術

  • 本章大綱:

  • -變量作用域-
  • 變量由作用範圍限制
  • 兩種不同的作用域:
  • 全局(global):在函數外部定義
  • 局部(local):在函數內部定義
  • 變量的作用範圍
  • 全局變量
  • 在整個程序範圍都有效
  • 全部變量可以在局部範圍裏面使用
  • 局部變量在局部範圍內可以使用
  • 局部變量無法在全局範圍內使用
  • LEGB原則
  • L(Local)局部作用域
  • E(Enclosing function locale)外部嵌套函數作用域(命名空間)
  • G(Global module)全局變量,函數定義所在模塊的命名空間
  • B(Buildin):python內置作用域或又名 內部建模的命名空間
  • -遞歸函數-
  • 函數直接或者間接調用自身
  • 優點:簡潔,容易理解
  • 缺點:對遞歸深度有限制,過多會導致消耗資源
  • python對遞歸深度有限制(自我調用997次),超出限制自動報錯
  • eval()函數
  • exec()函數






全局變量和局部變量案例如下

glo = "我是全局"
def func():
    print(glo)  # 在函數內部調用全局變量
    print("*"*20)
    loc = "我是局部"  # 定義局部變量
    print(loc)# 在函數內部調用局部變量

func() #調用函數,查看函數內部調用內容
print("下面為全局範圍的調用")
print(glo)# 在全局範圍裏調用全局變量
print(loc)# 在全局範圍裏調用局部變量,但是無法調用,會報錯
結果如下:
我是全局
Traceback (most recent call last):
********************
我是局部
  File "D:/圖靈/2.基礎語法/測試.py", line 11, in <module>
下面為全局範圍的調用
    print(loc)# 在全局範圍裏調用局部變量,但是無法調用,會報錯
我是全局
NameError: name loc is not defined




-局部變量提升為全局變量-
  • 使用global
  • 使用格式:
  • global val (val為局部變量名字)
 案例如下
案例條件我復制了上面的例子進行修改,把loc能在全局範圍裏調用
glo = "我是全局"
def func():
    print(glo)  #在函數內部調用全局變量
    print("*"*20)
    global loc  #為loc局部變量提升為全局變量
    loc = "我是局部"  #定義局部變量
    print(loc) #在函數內部調用局部變量

func() #調用函數,查看函數內部調用內容
print("下面為全局範圍的調用")
print(glo)# 在全局範圍裏調用全局變量
print(loc) #不知道什麽原因,盡管可以調用,但是pycharm這裏還是顯示變量為紅色(報錯),但是可以運行


結果如下:
我是全局
********************
我是局部
下面為全局範圍的調用
我是全局
我是局部




-查看整個程序有多少局部或全局變量-
  1. 通過globals和locals顯示
  2. global和locals都為內建函數(系統給的)

案例如下
因為程序內部可能沒有局部函數,所以我們先定義一些
a = 1
b = 2
def func(d,e):
    c = 3
    print(locals())
    print(globals())

func(100,200)

結果如下:
# 因為全局變量太多了 我這裏只例舉一些結果就行,你們可以直接copy代碼測試一下
{d: 100, e: 200, c: 3}
{__name__: __main__, __doc__: None, __package__.........}




最終打印講解:
  1. 局部變量為 d e c,這樣我們可以發現從調用裏面傳入的參數值也是函數內部的局部變量,而a,b是我們在全局範圍裏定義的,所以並沒有定義
  2. 全局變量則一大堆,因為這些都是系統附帶的,一個程序的允許,需要一推東西,而這堆東西系統都準備好了


-eval()函數-
  • 把一個字符串當成一個表達式來執行,返回表達式執行後的結果
  • 語法:
  • eval(string_code,globals=None,locals=None)
 -exec()函數-
  • 跟eval功能相似,但是不返回結果
  • 語法:
  • exec(string_code,globals=None,locals=None)

使用字符串進行算術 案例如下
x = 199
y = 1
z1 = x + y  # 使用值來計算
z2 = eval("x+y") # 用字符串來計算

打印比較一下兩個計算是否相等
print(z1)
print(z2)
print(z1 == z2) #值為true
print(type(z2)) #返回值的類型為int

結果如下:
200
200
True
<class int>



使用exec來計算 案例如下
x = 199
y = 1
z1 = x + y
 在exec中打印結果,註意看字符串引號的用法
對比exec執行過程和執行結果
z2 = exec("print(‘x+y:‘,x+y)")
#在打印過程中,exec內部的print先打印,因為還沒經過exec所以帶有結果,我們可以看到結果為200
print(z1)
print(z2)
print(z1 == z2) #因為最終結果返回值為None,所以不相等
print(type(z2)) #因為結果是None,所以無類型

結果如下:
x+y: 200
200
None
False
<class NoneType>





-遞歸函數-
  • 函數直接或者間接調用自身
  • 優點:簡潔,容易理解
  • 缺點:對遞歸深度有限制,過多會導致消耗資源
  • python對遞歸深度有限制(自我調用997次),超出限制自動報錯

測試python遞歸深度的限制
 a = 1
 def func():
     global a
     a += 1
     print(a)
     func()

 func()

結果如下:

# 初始遞歸深度為997,太多了,我也跟上面的一樣,列舉打印,你們可以復制代碼嘗試
1
2
3
4
......
995
996
997
Traceback (most recent call last):
  File "D:/圖靈/2.基礎語法/測試.py", line 11, in <module>
    func()
  File "D:/圖靈/2.基礎語法/測試.py", line 8, in func
    func()
  File "D:/圖靈/2.基礎語法/測試.py", line 8, in func
    func()
  File "D:/圖靈/2.基礎語法/測試.py", line 8, in func
    func()
  [Previous line repeated 992 more times]
  File "D:/圖靈/2.基礎語法/測試.py", line 7, in func
    print(a)
RecursionError: maximum recursion depth exceeded while calling a Python object


所以在使用遞歸時候一定要註意結束條件,否則會崩潰+浪費資源
  • 舉一個帶有結束條件的例子
  • 斐波那契數列
  • 一列數字,第一個值是1, 第二個也是1, 從第三個開始,每一個數字的值等於前兩個數字出現的值的和
  • 數學公式為: f(1) = 1, f(2) = 1, f(n) = f(n-1) + f(n-2)
  • 例如: 1,1,2,3,5,8,13.。。。。。。。。

n表示求第n個數子的斐波那契數列的值
def func(n):
     # 進行第一和第二次值為1 2 的遞歸時如果不使用指定返回值的話,後面無法進行遞歸運算
    if n == 1:
        return 1
    if n == 2:
        return 1
    return func(n-1)+func(n-2)


如果n等於1,不加判斷使其獲得返回值時,表達式為 func(1-1)+func(1-2) 則func(0)和func(-1),慢慢變負數,無法獲得返回值,最終超出遞歸深度
簡單來說,兩個判斷句就是用來修正n不會變成負數
打印結果:
print(func(3))   #1,1,2 我求的是第三位,所以等於2

結果如下:
2

  




-遞歸修正問題-
  • 如果n一開始就為負數,如何修正
  • 修正問題:
  • 1.讓傳入的值去除負號
  • 2.讓傳入的值不再進入遞歸
  • 兩種方法都行
# 其中一種方法而已
def fab(n):
    if n >= 0:
        if n <= 2:
            return 1
        else:return fab(n-1)+fab(n-2)
print(fab(-4))

結果如下:
None


-遞歸深度限制問題-
  • 使用sys庫裏面的setrecursionlimit(val)函數可以解決
 import sys
sys.setrecursionlimit(1000000)
我這裏直接將遞歸深度的限制改為1000000次

關於遞歸修正問題,如果有博客園的大佬有其他見解,請一定加我QQ為我講解,謝謝

文筆不好,僅供參考


要有錯誤或者有其他更多的見解,歡迎大家加我QQ384435742來交流


想第一時間看更新的文章,請關註,謝謝



Python 5.函數作用域與遞歸(基礎篇)