1. 程式人生 > 其它 >空間名稱與作用域

空間名稱與作用域

今日內容

01.函式物件

#1. 函式可以被引用
def add(x,y):
    return x+y
>>> func = add  # >>>指向的是函式add的記憶體地址
>>> func(1,2)
	3
#2. 函式可以作為容器型別的元素
>>> dic = {'add':add,'max':max}
>>> dic
{'add': <function add at 0x000002CCBF4CF0D0>, 'max': <built-in function max>}
>>> dic['add'](1,2) # 字典key'add'指向函式add的記憶體地址
    3
    
 # 3.函式可以作為引數傳入另外一個函式
>>> def foo(x,y,func):
    return func(x,y) 

>>> foo(1,2,add)
   3
    
 # 4.函式的返回值可以是一個函式
 >>> def bar():
 >>>   return add
 >>> func = bar()
     func(1,2)
        3

02.函式巢狀

# 1 定義: 在一個函式體內,用def重新定義新的函式
# 在定義階段,是封閉的,在呼叫的時候是拆解,在呼叫時,內部又呼叫其他的函式,大功能拆解成小功能
>>>def outer():
       def inner():
           print('innner')
       print('outer')
   	   inner()
  outer()
  >>outer 
  >>inner
  # inner() 會報錯,因為內部函式不可見
  # 內部函式不能被外部直接使用,會拋NameError異常

# 函式巢狀小案例:
"""需求求圓的面積與周長,要求在一個函式內實現"""
from math import pi  #匯入模組,pi


def circle(radius, mode=0):
    
    def perimeter(radius):   # 定義周長函式
        return 2*pi*radius
    
    def area(radius):       # 定義面積函式
        return pi*(radius ** 2)
    
    if mode == 0:           # 模式選擇 0.則返回值為周長函式
        return perimeter(radius)
   	elif mode == 1:         # 模式選擇 1.則返回值為面積函式
        return area(radius)

    
   res = circle(30,1)
   print(res)  ##2827.4333882308138

03.名稱空間

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!


Namespaces are one honking great idea  # 由此可見名稱空間的重要性
# 名稱空間即存放名字與物件對映/繫結關係的地方

3.1內部名稱空間(built-in names)

python自帶的名稱,比如內建函式
生命週期: 伴隨python直譯器的啟動/關閉而產生/回收
>>> abs
<built-in function abs> # build in 內建

3.2全域性名稱空間(global names)

頂級的名字(頂格寫的變數名)模組中定義的名稱,記錄了模組的變數,包括函式、類、其它匯入的模組、模組級的變數和常量。
生命週期:伴隨python檔案的開始執行/執行完畢而產生/回收

import os   # 模組名 os

x = 1   # 變數名 x

if x == 1: 
    y = 2  # 變數名y

def foo(x): # 函式名foo
    y = 1 
    def bar()
     pass
    
class Bar: # 類名Bar
    pass

3.3區域性名稱空間(local names)

函式中定義的名稱,記錄了函式的變數,包括函式的引數和區域性定義的變數。(類中定義的也是)
生命週期: 呼叫即產生,結束即退出
>>>def foo(x):
    y = 3 # 呼叫函式時,才會被執行程式碼,名字x和y都存放於該函式的區域性名稱空間中

3.4名稱空間的查詢順序

查詢順序:區域性名稱空間>>全域性名稱空間>>內建名稱空間

名稱空間巢狀關係是在函式定義時已經生成,與呼叫無關

# 例如
def foo():
    print(int)  # 在定義階段名稱空間已經生成,區域性沒有,向上查詢,全域性空間執行 int =100, 如果int沒有被定義的話,再向上查詢,內建空間,你將看到的是 <class 'int'>

def bar():
    int = 666
    foo()
    
int = 100  #  

bar()  
>>> 100

全域性變數與區域性變數

全域性變數可以在整個程式範圍內訪問,區域性變數只能在其被宣告的函式內部訪問
total = 0   # total 為全域性變數
def sun(x,y):
	total = x + y
     return total
    
當內部想要修改外部的變數時,就要使用global和nonlocal關鍵字
global 和 nonlocal 關鍵字

# global
num = 1
def func():
   	global num  # 宣告global關鍵字 修改全域性變數
    print(num)
    num = 123
    print(num)
>>>  func()
>>> print(num)
 1
 123
 123
# 可以看到通過使用global 改變了全域性變數的值



# nonlocal 

# nonlocal關鍵字: 宣告一個名字是來自於外層函式,如果外層沒有不會找到全域性,會報錯
x = 1
def f1():
    x = 2
    print(x)
   def f2():
       nonlocal x
	   x = 3
   f2()
   print(x)

f1()
print(x)
2
3
1
# nonlocal 可以看到全域性變數並沒有改變 nonlocal改變的是函式巢狀內部的值

作用域

作用域就是一個 Python 程式可以直接訪問名稱空間的正文區域。

在一個 python 程式中,直接訪問一個變數,會從內到外依次訪問所有的作用域直到找到,否則會報未定義的錯誤。

Python 中,程式的變數並不是在哪個位置都可以訪問的,訪問許可權決定於這個變數是在哪裡賦值的。

變數的作用域決定了在哪一部分程式可以訪問哪個特定的變數名稱。Python 的作用域一共有4種,分別是:

有四種作用域:

  • L(Local):最內層,包含區域性變數,比如一個函式/方法內部。
  • E(Enclosing):包含了非區域性(non-local)也非全域性(non-global)的變數。比如兩個巢狀函式,一個函式(或類) A 裡面又包含了一個函式 B ,那麼對於 B 中的名稱來說 A 中的作用域就為 nonlocal。
  • G(Global):當前指令碼的最外層,比如當前模組的全域性變數。
  • B(Built-in): 包含了內建的變數/關鍵字等,最後被搜尋。

規則順序: L –> E –> G –> B

在區域性找不到,便會去區域性外的區域性找(例如閉包),再找不到就會去全域性找,再者去內建中找。

閉包函式

# 閉包函式=函式物件+函式巢狀定義+名稱空間與作用域

# 閉函式:定義在函式內部的函式
# 包函式: 內部函式引用了一個外層函式的名字
def foo():
    x = 111
    def wrapper():
        print(x)
     return wrapper
f = foo()
f()
print(f)
>>>
111
<function foo.<locals>.wrapper at 0x00000240C9679700>  # 列印的為f的記憶體地址

### 閉包的函式的作用:閉包函式是一種為函式體傳參的方案

# 為函式體傳參方案一:直接傳參
def wrapper(x):
    print(x)
    
wrapper(1)

# 為函式體傳參方案二:閉包函式
def outter(x):
    def wrapper():
        print(x)
    return wrapper
wrapper = outter()

wrapper(111)