記錄個人 python 學習歷程-Day13 匿名函數、內置函數 II、閉包

1、匿名函數

之後面試或者工做中常常用匿名函數 lambda,也叫一句話函數。python

課上練習:面試

# 正常函數:
def func(a, b):
    return a + b

print(func(4, 6))   # 10

# 匿名函數:
func = lambda a, b: a + b

print(func(11, 33)) # 44

# 寫匿名函數:接收一個可切片的數據,返回索引爲0與2的對應的元素(元組形式)。
func = lambda x: (x[0], x[2])
print(func('Dylan'))    # ('D', 'l')

# 寫匿名函數:接收兩個int參數,將較大的數據返回。
func1 = lambda a, b: a if a > b else b
print(func1(11, 88))    # 88

語法:
函數名 = lambda 參數:返回值sql

  • 此函數不是沒有名字,他是有名字的,他的名字就是你給其設置的變量,好比func.緩存

  • lambda 是定義匿名函數的關鍵字,至關於函數的def.安全

  • lambda 後面直接加形參,形參加多少均可以,只要用逗號隔開就行。網絡

    func = lambda a,b,*args,sex= 'alex',c,**kwargs: kwargs
    print(func(3, 4,c=666,name='alex'))  # {'name': 'alex'}
    # 全部類型的形參均可以加,可是通常使用匿名函數只是加位置參數,其餘的用不到。
  • 返回值在冒號以後設置,返回值和正常的函數同樣,能夠是任意數據類型。閉包

  • 匿名函數無論多複雜.只能寫一行.且邏輯結束後直接返回數據app

2、內置函數 ii函數

紅色重點講解:abs() enumerate() filter() map() max() min() open() range() print() len() list() dict() str() reversed() set() sorted() sum() tuple() type() zip() dir()源碼分析

# python 提供了68個內置函數。
# eval()    剝去字符串的外衣,運算裏面的代碼,有返回值。**
s = '1 + 3'
print(s)    # 1 + 3
print(eval(s))  # 4


s1 = "{'name': 'Dylan'}"
print(s1, type(s1))     # {'name': 'Dylan'} <class 'str'>
# print(dict(s1))     # 不能轉換成字典,報錯。
print(eval(s1), type(eval(s1))) # {'name': 'Dylan'} <class 'dict'> 轉換成字典了。
# 網絡傳輸的 str input 輸入的時候,sql 注入等,絕對不能使用 eval()。

# exec()與 eval()幾科同樣,代碼流。
msg = '''
for i in range(10):
    print(i)
'''
# print(msg)
exec(msg)

# hash() 獲取一個對象(可哈希對象:int,str,Bool,tuple)的哈希值。
print(hash('Dylan'))    # -2239819904114377323

# help() 幫助 **
s2 = 'Dylan'
print(help(str.upper))
s1 = s2.upper()
print(s1)

# callable():用於檢查一個對象是不是可調用的。***
s1 = 'Dylan'
def func():
    pass


print(callable(s1))     # False
print(callable(func))   # True

# int() 用於將一個字符串或數字轉換爲整型。
print(int())        # 0
print(int('12'))    # 12
print(int(3.6))     # 3
print(int('0100', base=2))  # 將2進制的 0100 轉化成十進制。結果爲 4

# float() 用於將整數和字符串轉換成浮點數。
print(float(3)) # 3.0
print(float())  # 0.0
print(float("12.33"))   # 12.33

# complex() 用於建立一個值爲 real + imag * j 的複數或者轉化一個字符串或數爲複數。
# 若是第一個參數爲字符串,則不須要指定第二個參數。
print(complex(1, 2))    # (1+2j)

# bin:將十進制轉換成二進制並返回。**
# oct:將十進制轉化成八進制字符串並返回。**
# hex:將十進制轉化成十六進制字符串並返回。**
print(bin(10), type(bin(10)))   # 0b1010 <class 'str'>
print(oct(10), type(oct(10)))   # 0o12 <class 'str'>
print(hex(10), type(hex(10)))   # 0xa <class 'str'>

# divmod() 計算除數與被除數的結果,返回一個包含商和餘數的元組(a // b, a % b)。**
# round() 保留浮點數的小數位數,默認保留整數。**
# pow() 求x**y次冪。(三個參數爲x**y的結果對z取餘)**
print(divmod(8, 3))     # (2, 2)
print(round(7/3, 2))    # 2.33
print(round(7/3))       # 2
print(round(3.32353, 3))    # 3.323
print(pow(2, 3))    # 兩個參數爲2**3次冪
print(pow(2, 3, 3)) # 三個參數爲2**3次冪,對3取餘。結果 2。

# bytes() 用於不一樣編碼之間的轉化。***
s = 'Dylan'
bs = s.encode('utf-8')
print(bs)   # b'Dylan'

bs = bytes(s, encoding='utf-8')
print(bs)   # b'Dylan'

# ord:輸入字符找該字符編碼的位置
# chr:輸入位置數字找出其對應的字符
print(ord('a'))     # 97
print(ord('中'))    # 20013

print(chr(97))      # a
print(chr(20013))   # 中

# repr:返回一個對象的string形式(原形畢露)。
print(repr('Dylan'))    # 'Dylan'
print('Dylan')  # Dylan

name = 'Dylan'
print('我叫%r' % name)    # 我叫'Dylan'

# all:可迭代對象中,全都是True纔是True
# any:可迭代對象中,有一個True 就是True
print(all([1, 2, True, 0])) # False
print(any([1, '', 0]))      # True

# #################### 很是重要 ######################## #
# print() 屏幕輸出。
# 源碼分析
# def print(self, *args, sep=' ', end='\n', file=None): # known special case of print
#
#     print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
#     file:  默認是輸出到屏幕,若是設置爲文件句柄,輸出到文件
#     sep:   打印多個值之間的分隔符,默認爲空格
#     end:   每一次打印的結尾,默認爲換行符
#     flush: 當即把內容輸出到流文件,不做緩存
#
print(11, 22, 33, sep='|')  # 11|22|33
print(111, end='')
print(222)  # 兩行結果爲 111222

# list() 將一個可迭代對象轉換成列表
# tuple() 將一個可迭代對象轉換成元組
# dict() 經過相應的方式建立字典。

l1 = list('Dylang')
print(l1)   # ['D', 'y', 'l', 'a', 'n', 'g']
tu = tuple('Dylan')
print(tu)   # ('D', 'y', 'l', 'a', 'n')
# dict 建立字典的幾種方式
# 直接建立
# 元組的解構
dic = dict([(1, 'one'), (2, 'two'), (3, 'three')])
print(dic)  # {1: 'one', 2: 'two', 3: 'three'}
dic1 = dict(one=1, two=2)
print(dic1) # {'one': 1, 'two': 2}
# fromkeys
# update
# 字典的推導式

# abs() 返回絕對值
i = -88
print(abs(i))   # 88

# sum() 求和
l1 = [i for i in range(11)]
s1 = '12345'
print(sum(l1))  # 55
print(sum(l1, 100))     # 設置初始數爲100 # 結果:155
# print(sum(s1))   # 報錯   TypeError: unsupported operand type(s) for +: 'int' and 'str'

# reversed() 將一個序列翻轉, 返回翻轉序列的迭代器 reversed
l = reversed('你好')  # 獲取到的是一個生成器
print(list(l))  # 將生成器中的元素轉換爲列表 ['好', '你']
ret = reversed([1, 4, 6, 5, 8, 7])  # 獲取到的是一個生成器
print(list(ret))    # [7, 8, 5, 6, 4, 1]

# zip() 拉鍊方法。函數用於將可迭代的對象做爲參數,將對象中對應的元素打包成一個個元組,
# 而後返回由這些元祖組成的內容,若是各個迭代器的元素個數不一致,則按照長度最短的返回
s = 'abcde'
tu = ('Dylan', 'xiaobai', 'niaoren')
l1 = [1, 2, 3, 4]
obj = zip(s, tu, l1)
for i in obj:
    print(i)
print(list(obj))

# ################# 如下方法最最最最最重要 ##############
# min() 求最小值
# max() 最大值與最小值用法相同。
print(min([1, 2, 3, 4]))  # 返回此序列最小值
ret = min([1, 2, -3], key=abs)  # 按照絕對值的大小,返回此序列的最小值
print(ret)
# 加 key 是能夠加函數名的,min 會自動獲取傳入函數中的參數的每一個元素,
# 而後經過你設定的返回值比較大小,返回最小的傳入的那個參數。
print(min(1, 2, -5, 6, -3, key=lambda x: abs(x)))  # 能夠設置不少參數比較大小
dic = {'a': 3, 'b': 2, 'c': 1}
print(min(dic, key=lambda x: dic[x]))
# x爲dic的key,lambda的返回值(即dic的值進行比較)返回最小的值對應的鍵
l2 = [('太白', 18), ('alex', 73), ('wusir', 35), ('口天吳', 41)]
print(min(l2))  # ('alex', 73)
print(min(l2, key=lambda x: x[1]))  # ('太白', 18)

# sorted()排序函數
# 語法:sorted(iterable,key=None,reverse=False)
# iterable : 可迭代對象
# key: 排序規則(排序函數),在sorted內部會將可迭代對象中的每個元素傳遞給這個函數的參數.根據函數運算的結果進行排序
# reverse :是不是倒序,True 倒序 False 正序
l1 = [22, 33, 1, 2, 8, 7, 6, 5]
l2 = sorted(l1)
print(l1)  # [22, 33, 1, 2, 8, 7, 6, 5]
print(l2)  # [1, 2, 5, 6, 7, 8, 22, 33]

l3 = [('大壯', 76), ('雪飛', 70), ('納欽', 94), ('張珵', 98), ('b哥', 96)]
print(sorted(l3))  # [('b哥', 96), ('大壯', 76), ('張珵', 98), ('納欽', 94), ('雪飛', 70)]
print(sorted(l3, key=lambda x: x[1]))  # 返回的是一個列表,默認從低到高。
print(sorted(l3, key=lambda x: x[1], reverse=True))  # # 返回的是一個列表,從高到低

# filter()篩選過濾
# 語法: filter(function,iterable)
# function: 用來篩選的函數,在filter中會自動的把iterable中的元素傳遞給function,
# 而後根據function返回的True或者False來判斷是否保留此項數據
# iterable:可迭代對象
l1 = [2, 3, 4, 1, 6, 7, 8]
print([i for i in l1 if i > 3])  # [4, 6, 7, 8] 返回的是列表
ret = filter(lambda x: x > 3, l1)  # 返回的是個迭代器
print(ret)  # <filter object at 0x10601db70>
print(list(ret))  # [4, 6, 7, 8]

# map() 映射函數
# 語法: map(function,iterable) 能夠對可迭代對象中的每個元素進映射,分別取執行function
# 計算列表中每一個元素的平方,返回新列表
lst = [1, 2, 3, 4, 5]


def func(s):
    return s * s


mp = map(func, lst)
print(mp)  # <map object at 0x10581dd30>
print(list(mp))  # [1, 4, 9, 16, 25]

# 改寫成lambda
print(list(map(lambda s: s * s, lst)))  # [1, 4, 9, 16, 25]

# 計算兩個列表中相同位置的數據的和
lst1 = [1, 2, 3, 4, 5]
lst2 = [2, 4, 6, 8, 10]
print(list(map(lambda x, y: x + y, lst1, lst2)))  # [3, 6, 9, 12, 15]

# reduce()
from functools import reduce


def func(x, y):
    return x + y


# reduce 的使用方式:
# reduce(函數名,可迭代對象)  # 這兩個參數必須都要有,缺一個不行
ret = reduce(func, [3, 4, 5, 6, 7])
print(ret)  # 結果 25

# reduce的做用是先把列表中的前倆個元素取出計算出一個值而後臨時保存着,
# 接下來用這個臨時保存的值和列表中第三個元素進行計算,求出一個新的值將最開始
# 臨時保存的值覆蓋掉,而後在用這個新的臨時值和列表中第四個元素計算.依次類推

# 注意:咱們放進去的可迭代對象沒有更改
# 以上這個例子咱們使用sum就能夠徹底的實現了.我如今有[1,2,3,4]想讓列表中的數變成1234,就要用到reduce了.
# 普通函數版

from functools import reduce


def func(x, y):
    return x * 10 + y
    # 第一次的時候 x是1 y是2  x乘以10就是10,而後加上y也就是2最終結果是12而後臨時存儲起來了
    # 第二次的時候x是臨時存儲的值12 x乘以10就是 120 而後加上y也就是3最終結果是123臨時存儲起來了
    # 第三次的時候x是臨時存儲的值123 x乘以10就是 1230 而後加上y也就是4最終結果是1234而後返回了


l = reduce(func, [1, 2, 3, 4])
print(l)

# 匿名函數版
l = reduce(lambda x, y: x * 10 + y, [1, 2, 3, 4])
print(l)

# 在Python2.x版本中recude是直接 import就能夠的, Python3.x版本中須要從functools這個包中導入
# 龜叔本打算將 lambda 和 reduce 都從全局名字空間都移除, 輿論說龜叔不喜歡lambda 和 reduce
# 最後lambda沒刪除是由於和一我的寫信寫了好多封,進行交流而後把lambda保住了.

3、閉包

因爲閉包這個概念比較難以理解,尤爲是初學者來講,相對難以掌握,因此咱們經過示例去理解學習閉包。

給你們提個需求,而後用函數去實現:完成一個計算不斷增長的系列值的平均值的需求。

例如:整個歷史中的某個商品的平均收盤價。什麼叫平局收盤價呢?就是從這個商品一出現開始,天天記錄當天價格,而後計算他的平均值:平均值要考慮直至目前爲止全部的價格。

好比大衆推出了一款新車:小白轎車。

第一天價格爲:100000元,平均收盤價:100000元

次日價格爲:110000元,平均收盤價:(100000 + 110000)/2 元

第三天價格爲:120000元,平均收盤價:(100000 + 110000 + 120000)/3 元

........

series = []
def make_averager(new_value):
    series.append(new_value)
    total = sum(series)
    return total / len(series)
    
print(make_averager(100000))
print(make_averager(110000))
print(make_averager(120000))

從上面的例子能夠看出,基本上完成了咱們的要求,可是這個代碼相對來講是不安全的,由於你的這個series列表是一個全局變量,只要是全局做用域的任何地方,均可能對這個列表進行改變。

series = []
def make_averager(new_value):
    series.append(new_value)
    total = sum(series)
    return total / len(series)
    
print(make_averager(100000))
print(make_averager(110000))
series.append(666)  # 若是對數據進行相應改變,那麼你的平均收盤價就會出現很大的問題。
print(make_averager(120000))
def make_averager(new_value):
    series = []
    series.append(new_value)
    total = sum(series)
    return total / len(series)


print(make_averager(100000))  # 100000.0
print(make_averager(110000))  # 110000.0
print(make_averager(120000))  # 120000.0

這樣計算的結果是不正確的,那是由於執行函數,會開啓一個臨時的名稱空間,隨着函數的結束而消失,因此你每次執行函數的時候,都是從新建立這個列表,那麼這怎麼作呢?這種狀況下,就須要用到咱們講的閉包了,咱們用閉包的思想改一下這個代碼。

def make_averager():
    
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager

avg = make_averager()
print(avg(100000))
print(avg(110000))
print(avg(120000))

你們仔細看一下這個代碼,我是在函數中嵌套了一個函數。那麼avg 這個變量接收的實際是averager函數名,也就是其對應的內存地址,我執行了三次avg 也就是執行了三次averager這個函數。那麼此時大家有什麼問題?

確定有學生就會問,那麼個人make_averager這個函數只是執行了一次,爲何series這個列表沒有消失?反而還能夠被調用三次呢?這個就是最關鍵的地方,也是閉包的精華所在。我給你們說一下這個原理,以圖爲證:

img

上面被紅色方框框起來的區域就是閉包,被藍色圈起來的那個變量應該是make_averager()函數的局部變量,它應該是隨着make_averager()函數的執行結束以後而消失。可是他沒有,是由於此區域造成了閉包,series變量就變成了一個叫自由變量的東西,averager函數的做用域會延伸到包含自由變量series的綁定。也就是說,每次我調用avg對應的averager函數 時,均可以引用到這個自用變量series,這個就是閉包。

閉包的定義:

  1. 閉包是嵌套在函數中的函數。
  2. 閉包必須是內層函數對外層函數的變量(非全局變量)的引用。

如何判斷判斷閉包?舉例讓同窗回答:

# 例一:
def wrapper():
    a = 1
    def inner():
        print(a)
    return inner
ret = wrapper()

# 例二:
a = 2
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()


# 例三:

def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)

以上三個例子,最難判斷的是第三個,其實第三個也是閉包,若是咱們每次去研究代碼判斷其是否是閉包,有一些不科學,或者過於麻煩了,那麼有一些函數的屬性是能夠獲取到此函數是否擁有自由變量的,若是此函數擁有自由變量,那麼就能夠側面證實其是不是閉包函數了(瞭解):

def make_averager():

    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager
avg = make_averager()
# 函數名.__code__.co_freevars 查看函數的自由變量
print(avg.__code__.co_freevars)  # ('series',)
固然還有一些參數,僅供瞭解:

# 函數名.__code__.co_freevars 查看函數的自由變量
print(avg.__code__.co_freevars)  # ('series',)
# 函數名.__code__.co_varnames 查看函數的局部變量
print(avg.__code__.co_varnames)  # ('new_value', 'total')
# 函數名.__closure__ 獲取具體的自由變量對象,也就是cell對象。
# (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
# cell_contents 自由變量具體的值
print(avg.__closure__[0].cell_contents)  # []

閉包的做用:保存局部信息不被銷燬,保證數據的安全性。

閉包的應用

  1. 能夠保存一些非全局變量可是不易被銷燬、改變的數據。
  2. 裝飾器。
相關文章
相關標籤/搜索