1. 程式人生 > >python全棧開發-前方高能-函式進階 python_day_10

python全棧開發-前方高能-函式進階 python_day_10

一.今日主要內容 1. 動態引數 位置引數的動態引數: *args 關鍵字引數的動態引數 : **kwargs 順序: 位置,*args,預設值,**kwargs 在形參上*聚合, **聚合 在實參上*打散, **打散 2. 名稱空間和作用域 名稱空間: 1. 內建名稱空間: python解釋內部執行時的變數函式 2. 全域性名稱空間: 我們在py檔案中直接宣告出來的變數,函式 3. 區域性名稱空間: 在函式內部宣告的變數和函式. 作用域: 1.全域性作用域:內建+全域性 2.區域性作用域:區域性 globals() 檢視全域性作用域中的名字 locals() 檢視當前作用域中的名字

3. global和nonlocal global: 把全域性變數拿到區域性來用. nonlocal: 把離他最近的一層的變數拿過來.不會找全域性

10. 前⽅⾼能-函式的進階

本節主要內容:

  1. 函式引數--動態傳參
  2. 名稱空間, 區域性名稱空間, 全域性名稱空間, 作⽤域, 載入順序.
  3. 函式的巢狀
  4. gloabal, nonlocal關鍵字

⼀. 函式引數--動態傳參

 之前我們說過了傳參, 如果我們需要給⼀個函式傳參, ⽽引數⼜是不確定的. 或者我給⼀個

函式傳很多引數, 我的形參就要寫很多, 很⿇煩, 怎麼辦呢. 我們可以考慮使⽤動態引數.

 形參的第三種: 動態引數

 動態引數分成兩種:

1. 動態接收位置引數

 ⾸先我們先回顧⼀下位置引數, 位置引數, 按照位置進⾏傳參

def chi(quality_food, junk_food):

   print("我要吃", quality_food, junk_food)

chi("⼤⽶飯", "⼩⽶飯") # "⼤⽶飯"傳遞給quality_food "⼩⽶飯"傳遞給junk_food,按照位置傳

 現在問題來了. 我想吃任意的食物. 數量是任意的, 食物也是任意的. 這時我們就要⽤到

動態引數了.

在引數位置編寫*表⽰接收任意內容

def chi(*food):

   print("我要吃", food)

chi("⼤⽶飯", "⼩⽶飯")

結果:

我要吃 ('⼤⽶飯', '⼩⽶飯') # 多個引數傳遞進去. 收到的內容是元組tuple

 動態接收引數的時候要注意: 動態引數必須在位置引數後⾯

def chi(*food, a, b):

   print("我要吃", food, a, b)

chi("⼤⽶飯", "⼩⽶飯", "⻩⽠", "茄⼦")

 這時程式運⾏會報錯. 因為前⾯傳遞進去的所有位置引數都被*food接收了. a和b永遠接收

不到引數

Traceback (most recent call last):

 File "/Users/sylar/PycharmProjects/oldboy/fun.py", line 95, in <module>

 chi("⼤⽶飯", "⼩⽶飯", "⻩⽠", "茄⼦")

TypeError: chi() missing 2 required keyword-only arguments: 'a' and 'b'

 所以必須改寫成以下程式碼:

def chi(*food, a, b):

   print("我要吃", food, a, b)

chi("⼤⽶飯", "⼩⽶飯", a="⻩⽠", b="茄⼦") # 必須⽤關鍵字引數來指定

 這個時候a和b就有值了, 但是這樣寫呢位置引數就不能⽤了. 所以. 我們要先寫位置引數,

然後再⽤動態引數

def chi(a, b, *food):

   print("我要吃", a, b, food)

chi("⼤⽶飯", "⼩⽶飯", "饅頭", "⾯條") # 前兩個引數⽤位置引數來接收, 後⾯的引數⽤

動態引數接收

 那預設值引數呢?

def chi(a, b, c='饅頭', *food):

   print(a, b, c, food)

chi("⾹蕉", "菠蘿") # ⾹蕉 菠蘿 饅頭 (). 預設值⽣效

chi("⾹蕉", "菠蘿", "葫蘆娃") # ⾹蕉 菠蘿 葫蘆娃 () 預設值不⽣效

chi("⾹蕉", "菠蘿", "葫蘆娃", "⼝罩") # ⾹蕉 菠蘿 葫蘆娃 ('⼝罩',) 預設值不⽣效

 我們發現預設值引數寫在動態引數前⾯. 預設值只有⼀種情況可能會⽣效.

def chi(a, b, *food, c="娃哈哈"):

   print(a, b, food, c)
  
chi("⾹蕉", "菠蘿") # ⾹蕉 菠蘿 () 娃哈哈 預設值⽣效

chi("⾹蕉", "菠蘿", "葫蘆娃") # ⾹蕉 菠蘿 ('葫蘆娃',) 娃哈哈 預設值⽣效

chi("⾹蕉", "菠蘿", "葫蘆娃", "⼝罩") # ⾹蕉 菠蘿 ('葫蘆娃', '⼝罩') 娃哈哈 默

認值⽣效

 這個時候我們發現所有的預設值都⽣效了. 這個時候如果不給出關鍵字傳參. 那麼你的默

認值是永遠都⽣效的.

順序: 位置引數, 動態引數*, 預設值引數

2. 動態接收關鍵字引數

 在python中可以動態的位置引數, 但是*這種情況只能接收位置引數⽆法接收關鍵字引數.

在python中使⽤**來接收動態關鍵字引數

def func(**kwargs):

   print(kwargs)

func(a=1, b=2, c=3)

func(a=1, b=2)

結果:

{'a': 1, 'b': 2, 'c': 3}

{'a': 1, 'b': 2}

 這個時候接收的是⼀個dict

 順序的問題, 在函式調⽤的時候, 如果先給出關鍵字引數, 則整個引數列表會報錯.

def func(a, b, c, d):

   print(a, b, c, d)

# 關鍵字引數必須在位置引數後⾯, 否則引數會混亂

func(1, 2, c=3, 4)

 所以關鍵字引數必須在位置引數後⾯. 由於實參是這個順序. 所以形參接收的時候也是這

個順序. 也就是說位置引數必須在關鍵字引數前⾯. 動態接收關鍵字引數也要在後⾯

 最終順序(*):

 位置引數 > *args > 預設值引數 > **kwargs

 這四種引數可以任意的進⾏使⽤.

如果想接收所有的引數:

def func(*args, **kwargs):

   print(args, kwargs)

func("麻花藤","⻢暈",wtf="胡辣湯")

 動態引數的另⼀種傳參⽅式:

def fun(*args):

   print(args)

lst = [1, 4, 7]

fun(lst[0], lst[1], lst[2])

fun(*lst) # 可以使⽤*把⼀個列表按順序打散

s = "⾂妾做不到"

fun(*s) # 字串也可以打散, (可迭代物件)

 在實參位置上給⼀個序列,列表,可迭代物件前⾯加個*表⽰把這個序列按順序打散. 

 在形參的位置上的* 表⽰把接收到的引數組合成⼀個元組

 如果是⼀個字典, 那麼也可以打散. 不過需要⽤兩個*

def fun(**kwargs):

   print(kwargs)

dic = {'a':1, 'b':2}

fun(**dic)

 函式的註釋:

def chi(food, drink):

   """

   這⾥是函式的註釋, 先寫⼀下當前這個函式是⼲什麼的, ⽐如我這個函式就是⼀個吃

   :param :param food: 引數food是什麼意思

   :param :param drink: 引數drink是什麼意思

   :return :return: 返回的是什麼東東

   """

   print(food, drink)

   return "very good"

⼆. 名稱空間

 在python直譯器開始執⾏之後, 就會在記憶體中開闢⼀個空間, 每當遇到⼀個變數的時候, 就

把變數名和值之間的關係記錄下來, 但是當遇到函式定義的時候, 直譯器只是把函式名讀入內

存, 表⽰這個函式存在了, ⾄於函式內部的變數和邏輯, 直譯器是不關⼼的. 也就是說⼀開始

的時候函式只是載入進來, 僅此⽽已, 只有當函式被調⽤和訪問的時候, 直譯器才會根據函式

內部宣告的變數來進⾏開闢變數的內部空間. 隨著函式執⾏完畢, 這些函式內部變數佔⽤的空

間也會隨著函式執⾏完畢⽽被清空.

def fun():

   a = 10

   print(a)

fun()

print(a) # a不存在了已經..

 我們給存放名字和值的關係的空間起⼀個名字叫: 名稱空間. 我們的變數在儲存的時候就

是儲存在這片空間中的.

 名稱空間分類:

 1. 全域性名稱空間--> 我們直接在py⽂件中, 函式外宣告的變數都屬於全域性名稱空間

 2. 區域性名稱空間--> 在函式中宣告的變數會放在區域性名稱空間

 3. 內建名稱空間--> 存放python直譯器為我們提供的名字, list, tuple, str, int這些都是內

置名稱空間

 載入順序:

 1. 內建名稱空間

 2. 全域性名稱空間

 3. 區域性名稱空間(函式被執⾏的時候)

 取值順序:

 1. 區域性名稱空間

 2. 全域性名稱空間

 3. 內建名稱空間

a = 10

def func():

   a = 20

   print(a)

func() # 20

 作⽤域: 作⽤域就是作⽤範圍, 按照⽣效範圍來看分為 全域性作⽤域和區域性作⽤域

 全域性作⽤域: 包含內建名稱空間和全域性名稱空間. 在整個⽂件的任何位置都可以使⽤(遵循

從上到下逐⾏執⾏). 區域性作⽤域: 在函式內部可以使⽤.

 作⽤域名稱空間:

 1. 全域性作⽤域: 全域性名稱空間 + 內建名稱空間

 2. 區域性作⽤域: 區域性名稱空間

 我們可以通過globals()函式來檢視全域性作⽤域中的內容, 也可以通過locals()來檢視區域性作

⽤域中的變數和函式資訊

a = 10

def func():

   a = 40

   b = 20

   def abc():

     print("哈哈")

   print(a, b) # 這⾥使⽤的是區域性作⽤域

   print(globals()) # 列印全域性作⽤域中的內容

   print(locals()) # 列印區域性作⽤域中的內容

func()

三. 函式的巢狀

1. 只要遇⻅了()就是函式的調⽤. 如果沒有()就不是函式的調⽤

2. 函式的執⾏順序

四. 關鍵字global和nonlocal

 ⾸先我們寫這樣⼀個程式碼, ⾸先在全域性宣告⼀個變數, 然後再區域性調⽤這個變數, 並改變這

個變數的值

a = 100

def func():

     global a # 加了個global表示不再區域性建立這個變量了. ⽽是直接使⽤全域性的a

     a = 28

     print(a)

func()

print(a)

 global表⽰. 不再使⽤區域性作⽤域中的內容了. ⽽改⽤全域性作⽤域中的變數

lst = ["麻花藤", "劉嘉玲", "詹姆斯"]

def func():

     lst.append("⻢云云") # 對於可變資料型別可以直接進⾏訪問. 但是不能改地址. 說⽩

了. 不能賦值

     print(lst)

func()

print(lst)

nonlocal 表⽰在區域性作⽤域中, 調⽤⽗級名稱空間中的變數.

 再看, 如果嵌套了很多層, 會是⼀種什麼效果:

這樣的程式如果能分析明⽩. 那麼作⽤域, global, nonlocal就沒問題了

=============================7.20更新 =============================

作業:

二,寫函式,接收n個數字,求這些引數數字的和。(動態傳參)
def s(*num):
    sum = 0
    for i in num:
        sum += i
    return sum
print(s(1,2,3,4,5))
三,讀程式碼,回答:程式碼中,打印出來的值a,b,c分別是什麼?為什麼?
a=10
b=20
def test5(a,b):
    print(a,b)
c = test5(b,a)
print(c)

a =20
b =10
c =None
四,讀程式碼,回答:程式碼中,打印出來的值a,b,c分別是什麼?為什麼?
a=10
b=20
def test5(a,b):
    a=3
    b=5
print(a,b)
c = test5(b,a)
print(c)

a = 10
b = 20
c = None
五.寫函式,傳入函式中多個實參(均為可迭代物件如字串,列表,元祖,集合等),
將每個實參的每個元素依次新增到函式的動態引數args裡面.
例如 傳入函式兩個引數[1,2,3] (22,33)最終args為(1,2,3,22,33)
def func(*args):
    return args

a = [1,2,3]
b = (22,33)
print(func(*a,*b))
六,寫函式,傳入函式中多個實參(實參均為字典),將每個實參的鍵值對依次新增到函式的動態引數kwargs裡面.
例如 傳入函式兩個引數{‘name’:’alex’} {‘age’:1000}最終kwargs為{‘name’:’alex’ ,‘age’:1000}
def func(**kwargs):
    return kwargs

a = {'name':'alex'}
b = {'age':1000}
print(func(**a,**b))
7, 下面程式碼成立麼?如果不成立為什麼報錯?怎麼解決?
#7.1
    a = 2
    def wrapper():
            print(a)
    wrapper()
#成立
# 7.2
a = 2
def wrapper():
    a += 1
    print(a)
wrapper()
#不成立,在賦值前引用全域性變數a
# 7.3
def wrapper():
    a = 1
    def inner():
        print(a)
    inner()
wrapper()
#成立
# 7.4
def wrapper():
    a = 1
    def inner():
        a += 1
        print(a)
    inner()
wrapper()
# 不成立,在賦值前引用區域性變數a
八,寫函式,接收兩個數字引數,將較小的數字返回.
def func(a,b):
    if a < b:
        return a
    else:
        return b
x = func(12,23)
print(x)
九,寫函式,接收一個引數(此引數型別必須是可迭代物件),將可迭代物件的每個元素
以’_’相連線,形成新的字串,並返回.
例如 傳入的可迭代物件為[1,'老男孩','武sir']返回的結果為’1_老男孩_武sir’
def func(lst):
    s = ""
    for i in lst:
        s += str(i) + "_"
    print(s.strip("_"),end="")
lst = [1,'老男孩','武sir']
func(lst)
十,寫函式,傳入n個數,返回字典{‘max’:最大值,’min’:最小值}
例如:min_max(2,5,7,8,4) 返回:{‘max’:8,’min’:2}(此題用到max(),min()內建函式)
def func(*num):
    dic = {}
    dic["max"] = max(num)
    dic["min"] = min(num)
    return dic
a = func(1,23,3,4,44,4,5,)
print(a)
11.,寫函式,傳入一個引數n,返回n的階乘
例如:cal(7)  計算7*6*5*4*3*2*1
def func(n):
    if n == 0:
        return True
    return n * func(n - 1)
a = func(3)
print(a)
12.寫函式,返回一個撲克牌列表,裡面有52項,每一項是一個元組
例如:[(‘紅心’,2),(‘草花’,2), …(‘黑桃’,‘A’)]
def func():
    l = ['紅心','黑桃','方片','梅花']
    l1 = ["A",2,3,4,5,6,7,8,9,10,'J','Q','K']
    l2 = []
    for i in l:
        for j in l1:
            l2.append((i,j))
    return  l2

print(func())
13 有如下函式:
def wrapper():
       def inner():
           print(666)
   wrapper()

你可以任意新增程式碼,用兩種或以上的方法,執行inner函式.
# 第一種:
def wrapper():
    def inner():
        print(666)
    inner()
wrapper()
#第二種:
def wrapper():
    def inner():
        print(666)
    return inner
wrapper()()
14相關面試題(先從紙上寫好答案,然後在執行):
1,有函式定義如下:
def calc(a,b,c,d=1,e=2):
    return (a+b)*(c-d)+e
請分別寫出下列標號程式碼的輸出結果,如果出錯請寫出Error。
print(calc(1,2,3,4,5))#__2___
print(calc(1,2))#__Error__
print(calc(e=4,c=5,a=2,b=3))#_24__
print(calc(1,2,3))#__8___
print(calc(1,2,3,e=4))#__10__
print(calc(1,2,3,d=5,4))#__Error___
2,(此題有坑)下面程式碼列印的結果分別是____[10,'a]_____,____[123]____,___[10'a_']____.
def extendList(val,list=[]):
    list.append(val)
    return list
list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print('list1=%s'%list1)
print('list2=%s'%list2)
print('list3=%s'%list3)
3, 寫程式碼完成99乘法表.(升級題)
1 * 1 = 1
2 * 1 = 2 2 * 2 = 4
3 * 1 = 3 3 * 2 = 6 3 * 3 = 9
......
9 * 1 = 9 9 * 2 = 18 9 * 3 = 27 9 * 4 = 36 9 * 5 = 45 9 * 6 = 54 9 * 7 = 63 9 * 8 = 72 9 * 9 = 81
def func(a):
    for i in range(1,a+1):
        for j in range(1,i+1):
            r = i * j
            print("%s * %s = %s" % (i,j,r),'    ',end="")
        print(end = "\n")
#
func(9)