1. 程式人生 > 資訊 >賈躍亭微博高調宣傳 FF91:超級汽車機器人,高出特斯拉一個檔次

賈躍亭微博高調宣傳 FF91:超級汽車機器人,高出特斯拉一個檔次

一、函式概述

  • 定義:函式是一堆程式碼的集合體,用於完成某種功能,相當於工具。
  • 原則:先定義,後呼叫。
def foo():
	bar()
	print('from foo')

def bar():
	print('from bar')

foo()

# 輸出結果:
from bar
from foo

# 注意:此處並不會報錯,因為函式在定義階段只檢查語法錯誤,當呼叫foo函式的時候,bar函式已經載入到了記憶體中,所以即使bar的定義在foo後面,呼叫的時候也能找到bar的函式程式碼,因此能正常執行。

二、函式應用場景

  • 程式碼過長時,組織結構不清晰,可讀性差,可以通過函式分塊;
  • 程式碼冗餘,對於重複性程式碼,一次定義,多次呼叫;
  • 使用函式可增強程式碼的可維護性和擴充套件性。

三、函式的定義

  • 先定義(大致有三種方式)
    1. 無參函式
    def 函式名():
    	'''函式功能註釋'''
    	
    	# 註釋
    	函式內程式碼塊
    	
    	return  返回值  # 非必須
    
    1. 有參函式
    def 函式名(引數1, 引數2, ...):
    	'''函式功能註釋'''
    	
    	# 註釋
    	函式內程式碼塊
    	
    	return  返回值  # 非必須
    
    1. 空函式(一般用於前期搭建框架使用)
    def 函式名():
    	pass
    
  • 後呼叫
    【呼叫函式發生的事情】通過函式名找到函式的記憶體地址,然後加括號就是在觸發函式體程式碼的執行。
    函式名()  # 方式一:無參函式呼叫
    函式名(引數1,引數2, ...)   # 方式二:有參函式呼叫
    
  • 應用場景
    1. 直接呼叫;
    2. 作為表示式的一部分;
    3. 作為返回值;
    4. 作為可變型別的元素;
    5. 作為函式的引數。

四、函式的分類

  • 內建函式:python直譯器內已經定義好的函式,可以直接呼叫;
  • 自定義函式:自己定義的函式,需要自己實現函式的所有功能,才能呼叫;

五、函式名

  1. 函式名的命名規則跟變數相同;
  2. 函式名可以看做是一個變數,可以被賦值,則該函式將指向新賦值的記憶體地址,不再指向函式程式碼塊記憶體地址,因此定義函式時注意函式名不能使用內建函式名,否則內建函式會被覆蓋;
# 示例:

# len內建函式用於計算值得長度
v1 = len("武沛齊")
print(v1) # 3

# len重新定義成另外一個函式
def len(a1,a2):
    return a1 + a2

# 以後執行len函式,只能按照重新定義的來使用
v3 = len(1,2)
print(v3)

  1. 函式名可以賦值給其他變數,則被賦值變數會指向函式程式碼塊記憶體地址,該變數也可代指該函式,通過變數名加括號同樣可以執行該函式;
# 示例1:
def func(a1,a2):
    print(a1,a2)
    
func_list = [func,func,func]

func(11,22)
func_list[0](11,22)
func_list[1](33,44)
func_list[2](55,66)

# ***********************

# 示例2:
def func(a1,a2):
    print(a1,a2)

# 執行func函式
func(11,22)

# func重新賦值成一個字串
func = "武沛齊"

print(func)
  1. 函式名可以作為函式的引數、返回值;
# 示例1:做引數

def plus(num):
    return num + 100

def handler(func):
    res = func(10) # 110
    msg = "執行func,並獲取到的結果為:{}".format(res)
    print(msg) # 執行func,並獲取到的結果為:110
   
# 執行handler函式,將plus作為引數傳遞給handler的形式引數func
handler(plus)

# ***********************************************

# 示例2:做返回值

def plus(num):
    return num + 100

def handler():
	print("執行handler函式")
    return plus
    
result = handler()
data = result(20) # 120
print(data)
  1. 函式可被雜湊,所以函式名也可以當做集合的元素、字典的鍵,也可以作為字典、元組、列表、集合的元素。
# 示例

def send_msg(mobile, content):
    """傳送簡訊"""
    pass

def send_email(to_email, subject, content):
    """傳送圖片"""
    pass

def send_wechat(user_id, content):
    """傳送微信"""
    pass

func_list = [
    {"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短訊息"}},
    {"name": send_email, "params": {'to_email': "[email protected]", "subject": "報警訊息", "content": "硬碟容量不夠用了"}},
    {"name": send_wechat, "params": {'user_id': 1, 'content': "約嗎"}},
]

#  {"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短訊息"}},
for item in func_list:
    func = item['name'] # send_msg
    param_dict = item['params'] # {'mobile': "15131255089", "content": "你有新短訊息"}
    func(**param_dict) # send_msg(**{'mobile': "15131255089", "content": "你有新短訊息"})

六、函式的返回值

  1. return:函式結束的標誌,即函式體執行到return時會立刻終止函式的執行。
  2. 返回值分類:
    • return:返回None
    • return None : 返回None
    • 不寫return:返回None
    • return a :返回變數a的記憶體地址。(a可以是變數、具體的數值、函式名等任意型別)
    • return a, b : 返回元組(a, b)

七、函式的引數

1. 函式的引數分為形參和實參:

  • 形參:函式定義時在函式名後面的括號內定義的變數;
  • 實參:函式被呼叫時傳入的變數或值。

2. 形參的型別:

  • 普通形參:函式定義中只是定義一個變數名,呼叫函式時必須傳值;
  • 預設值形參:函式定義時不但定義了變數名,同時還賦值,呼叫函式時可以不傳該引數;
  • 動態形參:
    • *args:只接受任意多的位置實參,將傳入的所有位置實參組成一個元組,傳遞給args,因此args的值必定是一個空值或元組;
    • **kwargs:只接受任意多的關鍵字實參,將傳入的關鍵字實參以字典的形式傳遞給kwargs,因此kwargs必定是一個空值或字典。

3. 實參的型別:

  • 位置引數:對應普通形參和預設值形參,順序必須一一對應;
  • 關鍵字引數:位置可以不固定。
def func(*arg, **kwargs):
	print(arg, kwargs)

func(3,4,5,x=66,y=77)

# 輸出結果:
>>>  (3, 4, 5) {'x':66, 'y':77}

【注意】函式呼叫時,傳參也可以使用 * 和 ** 。 * 後面可以是列表、元組或集合,傳值時會將其解壓成位置實參傳遞,** 後面為字典,傳值時會將其解壓成關鍵字實參傳遞。

def func(a, b, c, x, y):
	print(a, b, c, x, y)
	
func(*(1,2,3), **{'x':4, 'y':5})

# 輸出結果:
>>>  1, 2, 3, 4, 5

4. 形參的順序:

  • 普通引數必須放在最前面;
  • **kwargs必須放在最後面;
  • 動態引數必須在普通引數後面;
  • **kwargs必須在*args後面;
  • 預設值引數與*args的位置可以互換。
    實參的順序:
    形參的順序確定後,實參的順序跟形參保持一致。
def func(a, b, *args, c=9, **kwargs):
    print(a, b, c, args, kwargs)

func(1, 2, 99, 77, x = 4, y = 5)

#輸出結果:
>>>  1 2 9 (99, 77) {'x': 4, 'y': 5}
def func(a, b, c=9, *args, **kwargs):
    print(a, b, c, args, kwargs)

func(1, 2, 99, 77, x = 4, y = 5)

#輸出結果:
>>>  1 2 99 (77,) {'x': 4, 'y': 5}

5. 傳參

函式執行傳參時,傳遞的是記憶體地址。優點:

  • 節省記憶體。不用將實參複製一份再傳值;
  • 對於可變型別(列表、字典、集合)並且函式中修改元素的內容,所有的使用該變數的地方都會修改;如果函式內重新賦值,則不影響外面的可變型別的值;【參看後面坑1】
  • 對於不可變型別(例如字串),無法修改內部元素,只能重新賦值;

八、名稱空間和作用域

名稱空間

  1. 名稱空間:存放名字的地方,是對棧區的劃分。
    • 分類:
      • 內建名稱空間:存放python直譯器內建的名字,直譯器啟動時產生,關閉則銷燬;
      • 全域性名稱空間:只要不是函式內定義的、也不是內建的,剩下的都是全域性名稱空間的名字,因此,for、while、if語句塊中定義的變數都屬於全域性名稱空間,全域性有效,語句塊執行完畢後,變數不會被銷燬;python檔案執行則產生,執行完畢後銷燬;
      • 區域性名稱空間:函式內的名字,呼叫函式時存活,函式呼叫完畢後則銷燬(函式巢狀時有不銷燬的特殊情況)。
    • 載入順序:
      內建 --> 全域性 --> 區域性
    • 銷燬順序
      區域性 --> 全域性 --> 內建
    • 查詢順序:從當前所在的位置向外層一層一層查詢
      查詢優先順序:
      區域性 --> 全域性 --> 內建
      名稱空間的“巢狀”關係是以函式定義階段為準,與呼叫位置無關。
      示例:

作用域

  1. 作用域:名字的作用範圍。python是以函式為作用域,所以在函式內建立的所有資料,可以此函式中被使用,無法在其他函式中被使用。
    • 變數作用域:
      • 全域性變數:全域性作用域中建立的變數,可全域性使用,也可以在區域性作用域中被使用,一般都使用大寫字母命名;
      • 區域性變數:在函式內建立的變數稱為區域性變數,區域性變數只能在區域性作用域中被使用。
    • 區域性作用域中使用全域性作用域的變數:區域性作用域中只能對全域性變數進行讀取和修改內部元素(可變型別)的操作,無法對全域性變數進行重新賦值,如果非要重新賦值,需要在區域性作用域用global關鍵字修飾該變數,才能重新賦值。示例如下:
      COUNTRY = "中國"
      CITY_LIST = ["北京","上海","深圳"]
      
      def download():
      	url = "http://www.xxx.com"
      
      	global CITY_LIST
      	CITY_LIST =  ["河北","河南","山西"]
      	print(CITY_LIST)
      
      	global COUNTRY
      	COUNTRY = "中華人民共和國"
      	print(COUNTRY)
      
      def upload():
      	file_name = "rose.zip"
      	print(COUNTRY)
      	print(CITY_LIST)
      
      download()
      upload()
      

九、函式巢狀

  • 定義:函式可以定義在區域性作用域,這樣函式可以被區域性作用域和其子作用域中呼叫。
# 示例:
def func():
    print("沙河高曉鬆")
    
def handler():
    print("昌平吳彥祖")
    def inner():
        print("朝陽大媽")
	inner()
    func()
    print("海淀網友")

handler()

  • 應用場景
    其實,大多數情況下我們都會將函式定義在全域性,不會巢狀著定義函式。不過,當我們定義一個函式去實現某功能,想要將內部功能拆分成N個函式,又擔心這個N個函式放在全域性會與其他函式名衝突時(尤其多人協同開發)可以選擇使用函式的巢狀。
  • 函式巢狀作用域分析方法
    • 優先在自己的作用域找,自己沒有就去上級作用域。
    • 在作用域中尋找值時,要確保此次此刻值是什麼。
    • 分析函式的執行,並確定函式作用域鏈。(函式巢狀)

閉包

  • 閉包
    • 定義:閉包,簡而言之就是將資料封裝在一個包(區域)中,使用時再去裡面取。(本質上 閉包是基於函式巢狀搞出來一箇中特殊巢狀)
    • 應用場景
      • 場景一:封裝資料防止汙染全域性。
        def func(age):
        	name = "武沛齊"
        
        	def f1():
        		print(name, age)
        
        	def f2():
        		print(name, age)
        
        	def f3():
        		print(name, age)
        
        	f1()
        	f2()
        	f3()
        
        func(123)
        
      • 場景二:封裝資料封到一個包裡,使用時再取。
        案例如下:
        def task(arg):
        	def inner():
        		print(arg)
        	return inner
        
        inner_func_list = []
        for val in [11,22,33]:
        	inner_func_list.append( task(val) )
        
        inner_func_list[0]() # 11
        inner_func_list[1]() # 22
        inner_func_list[2]() # 33
        

裝飾器

  • 裝飾器
    • 定義:裝飾器,在不修改原函式內容的前提下,通過@函式可以實現在函式前後自定義執行一些功能(批量操作會更有意義)。
    • 實現原理:基於@語法和函式閉包,將原函式封裝在閉包中,然後將函式賦值為一個新的函式(內層函式),執行函式時再在內層函式中執行閉包中的原函式。
    • 實現效果:可以在不改變原函式內部程式碼 和 呼叫方式的前提下,實現在函式執行和執行擴充套件功能。
    • 適用場景:多個函式統一在執行前後自定義一些功能。
    • 示例:基於裝飾器實現的虛擬碼:
      from flask import Flask
      
      app = Flask(__name__)
      
      def auth(func):
      	def inner(*args, **kwargs):
      	# 在此處,判斷如果使用者是否已經登入,已登入則繼續往下,未登入則自動跳轉到登入頁面。
      		return func(*args, **kwargs)
      
      	return inner
      
      
      @auth
      def index():
      return "首頁"
      
      
      @auth
      def info():
      return "使用者中心"
      
      
      @auth
      def order():
      return "訂單中心"
      
      
      def login():
      return "登入頁面"
      
      
      app.add_url_rule("/index/", view_func=index, endpoint='index')
      app.add_url_rule("/info/", view_func=info, endpoint='info')
      app.add_url_rule("/order/", view_func=order, endpoint='order')
      app.add_url_rule("/login/", view_func=login, endpoint='login')
      
      app.run()
      
      
    • 重要補充:functools:其實,一般情況下大家不用functools也可以實現裝飾器的基本功能,但後期在專案開發時,不加functools會出錯(內部會讀取__name__,且__name__重名的話就報錯),所以在此大家就要規範起來自己的寫法。
    • 【裝飾器模板】 實際使用時套用該格式即可:
      import functools
      
      
      def auth(func):
      	@functools.wraps(func)
      	def inner(*args, **kwargs):
      		"""原函式執行前需要執行的程式碼"""
      		res = func(*args, **kwargs)  # 執行原函式
      		"""原函式執行後需要執行的程式碼"""
      		return res
      
      	return inner
      

十、匿名函式和三元運算

  • 定義:匿名函式,則是基於lambda表示式實現定義一個可以沒有名字的函式。
  • 格式:lambda 引數:函式體
    1. 引數,支援任意引數。
      lambda x: 函式體
      lambda x1,x2: 函式體
      lambda *args, **kwargs: 函式體
      
    2. 函式體,只能支援單行的程式碼。
    3. 返回值,預設將函式體單行程式碼執行的結果返回給函式的執行這。
      func = lambda x: x + 100
      
      v1 = func(10)
      print(v1) # 110
      
  • 簡單的函式,可以基於lambda表示式實現。
    def func(data):
    	return data.replace("蒼老師","***")
    
    func= lambda data: data.replace("蒼老師","***")
    
  • 【擴充套件】三元運算
    • 格式:結果 = 條件成立時 if 條件 else 不成立
    • 示例:匿名函式和三元運算的綜合運用:
    func = lambda x: "大了" if x > 66 else "小了"
    
    v1 = func(1)
    print(v1) # "小了"
    
    v2 = func(100)
    print(v2) # "大了"
    

十一、生成器函式

  • 定義:生成器是由函式+yield關鍵字創造出來的寫法,在特定情況下,用他可以幫助我們節省記憶體。

    • 生成器函式,但函式中有yield存在時,這個函式就是生產生成器函式。
    • 生成器物件,執行生成器函式時,會返回一個生成器物件。
    • 生成器的特點是,記錄在函式中的執行位置,下次執行next時,會從上一次的位置基礎上再繼續向下執行。
    def func():
    	print(111)
    	yield 1
    
    	print(222)
    	yield 2
    
    	print(333)
    	yield 3
    
    	print(444)
    
    data = func()
    
    # 執行生成器函式func,返回的是生成器物件。
    # 注意:執行生成器函式時,函式內部程式碼不會立刻執行。
    
  • 生成器物件的呼叫

    • 使用 next() 函式
    def func():
    	print(111)
    	yield 1
    
    	print(222)
    	yield 2
    
    	print(333)
    	yield 3
    
    	print(444)
    
    data = func()
    
    v1 = next(data)
    print(v1)
    
    v2 = next(data)
    print(v2)
    
    v3 = next(data)
    print(v3)
    
    v4 = next(data)
    print(v4)  # 結束或中途遇到return,程式爆:StopIteration 錯誤
    
    • 使用 for 迴圈
     data = func()
    
     for item in data:
       print(item)
    
    • 使用生成器物件的send()函式【幾乎用不到】
    def func():
       print(111)
       v1 = yield 1
       print(v1)
    
       print(222)
       v2 = yield 2
       print(v2)
    
       print(333)
       v3 = yield 3
       print(v3)
    
       print(444)
    
    
    data = func()
    
    n1 = data.send(None)
    print(n1)
    
    n2 = data.send(666)
    print(n2)
    
    n3 = data.send(777)
    print(n3)
    
    n4 = data.send(888)
    print(n4)
    
  • 應用場景舉例:

    • 假設要讓你生成 300w個隨機的4位數,並打印出來。

      • 在記憶體中一次性建立300w個
      • 動態建立,用一個建立一個。
    import random
    
    val = random.randint(1000, 9999)
    print(val)
    
    import random
    
    data_list = []
    for i in range(300000000):
        val = random.randint(1000, 9999)
    	data_list.append(val)
        
    # 再使用時,去 data_list 中獲取即可。
    # ...
    
    import random
    
    
    def gen_random_num(max_count):
        counter = 0
        while counter < max_count:
            yield random.randint(1000, 9999)
            counter += 1
    
    
    data_list = gen_random_num(3000000)
    # 再使用時,去 data_list 中獲取即可。
    
  • 總結:所以,當以後需要我們在記憶體中建立很多資料時,可以想著用基於生成器來實現一點一點生成(用一點生產一點),以節省記憶體的開銷。

十二、函式相關的坑

函式定義中存在預設值引數時,python在建立函式(未執行)時,如果發現函式有預設值引數時,則會在函式內部建立一塊區域並維護這個預設值(使用a代指)。

函式被呼叫過程中:

  • 對於預設值為不可變型別,呼叫過程不會修改這個維護的預設值;
  • 對於預設值為可變型別,如果呼叫未給該預設值引數傳值,則使用a,如果函式內部修改這個值而非重新賦值的情況下,則這個值會被修改,下次不傳參呼叫函式時,使用的是修改後的a,因此,後面每次這樣的呼叫都可能修改a的值,需特別注意。如果呼叫時給預設值引數傳值,則不會影響 a 的值。
    示例:
 # 記憶體中建立空間儲存 [1, 2] ,假設記憶體地址為:10000001
 def func(a1, a2=[1, 2]):
     a2.append(a1)
     return a2
 
 # 未給預設值引數傳值,a1=10
 # a2 -> 10000001
 # v1 -> 10000001
 # 執行完成後,10000001地址的值為 [1, 2, 10]
 v1 = func(10)
 
 
 # 未給預設值引數傳值,a1=20
 # a2 -> 10000001
 # v2 -> 10000001
 # 執行完成後,10000001地址的值為 [1, 2, 10, 20]
 v2 = func(20)
 
 # 給預設值引數傳值[11, 22],假設記憶體地址為11111110,a1=30
 # a2 -> 11111110   [11,22,30]
 # v3 -> 11111110
 # 執行完成後,11111110 值為[11, 22, 30] , 10000001 值仍為[1, 2, 10, 20]
 v3 = func(30, [11, 22])
 
 # 未給預設值引數傳值,a1=40
 # a2 -> 10000001
 # v4 -> 10000001
 # 執行完成後,10000001 值為[1, 2, 10, 20, 40]
 v4 = func(40)
 
 # 執行到此處,v1、v2、v4都指向10000001的記憶體地址,因此值都為 
 # [1, 2, 10, 20, 40],v3指向新開闢的記憶體地址 11111110,因此值為 [11,22,30]。
 print(v1) # [1, 2, 10, 20, 40]
 print(v2) # [1, 2, 10, 20, 40]
 print(v3) # [11,22,30]
 print(v4) # [1, 2, 10, 20, 40] 

python函式思維導圖:
清晰版請下載:python基礎-思維導圖-函式

本文來自部落格園,作者:#緣起,轉載請註明原文連結:https://www.cnblogs.com/yuan-qi/p/15065030.html