python函數(二)

来源:http://www.cnblogs.com/mingxiazhichan/archive/2017/05/22/6891590.html
-Advertisement-
Play Games

今天繼續函數的講解: 目錄: 1.函數對象 2.函數嵌套 3.名稱空間和作用域 4.閉包 5.裝飾器 6.迭代器 7.生成器 8.內置函數 第一部分:函數對象 在python中,一切皆對象,想int,str,list,dict,tuple等等,所以函數也不例外,對象都具有屬性。作為對象,它可以賦值給 ...


    今天繼續函數的講解:

    目錄:

    1.函數對象

    2.函數嵌套

    3.名稱空間和作用域

    4.閉包

    5.裝飾器

    6.迭代器

    7.生成器

    8.內置函數

 

第一部分:函數對象

    在python中,一切皆對象,想int,str,list,dict,tuple等等,所以函數也不例外,對象都具有屬性。作為對象,它可以賦值給其他函數名,也可以當作參數傳遞給其他函數。下麵給一個例子:

 1 def foo():
 2     print('hello,world')
 3 
 4 #foo   這隻是一個函數名字,該名字指向foo()函數的記憶體地址
 5 
 6 bar=foo    #將foo()函數的地址賦值給bar,則bar也指向foo()函數的地址
 7 
 8 bar()
 9 
10 結果:
11 hello,world

 再看一個例子,這個例子將函數作為參數傳遞給其他函數

 1 def test(func):     
 2     func()      #執行func函數。
 3 
 4 def hello():
 5     print('hello,world')
 6 
 7 test(hello)     #將函數作為參數傳遞個test函數,
 8 
 9 結果:
10 hello,world

第二部分:函數嵌套:

    函數的嵌套就是在一個函數內部再定義一個或多個函數,併在調用外部函數的時候,外部函數接著調用內部的函數。類似下麵的樣子:

 1 def fun1():      #定義一個函數
 2     a='txt'
 3     def fun2():   #在原來的函數內再定義一個函數,
 4         print(a)
 5     fun2()
 6 
 7 fun1()
 8 
 9 
10 結果:
11 txt

 先瞭解上面的定義方式,下麵會用到函數嵌套的知識。

第三部分:名稱空間和作用域

    1.名稱空間(namespace)

   名稱空間就是變數名字與值的綁定關係。python中有三類名稱空間:局部名稱空間/全局名稱空間/內置名稱空間,下麵對他們進行解釋:

   局部名稱空間:每個函數或類都有的自己的名稱空間,這個自己的名稱空間就叫局部名稱空間,它隨著函數的執行和類的實例的調用而產生,在函數結束或類實例消亡後被刪除。它包括了函數的參數和定義的變數。

   全局名稱空間:每個模塊擁有自己的名稱空間,這個叫做全局名稱空間,它記錄了模塊的變數,包括函數/類/其他導入的模塊/模塊級別的變數和常量

   內置名稱空間:催著python解釋器運行而創建的產生的名稱空間,所有模塊都可以訪問,它存放內置的函數和一些異常。

  

 1 x=5
 2 y=10
 3 def change_x():
 4     x=10             #在局部名稱空間創建變數x
 5     def print_xy():
 6         print(x,y)  #首先在print_x的局部名稱空間找x,找不到的話從上層名稱空間查找.從局部名稱空間找y,找不到到全局名稱空間找,
 7     print_xy()
 8 
 9 change_x()
10 print(x,y)    #列印全局名稱空間的x,從這裡看出局部名稱空間的x並沒有覆蓋全局名稱空間中的x
11 
12 #結果
13 10,10
14 5,10

 

從上面的例子,我們也可以得到在名稱空間中查找變數的順序:

1.現在局部名稱空間查找變數,若找到則使用該變數,停止搜索;若未找到,則到上一層的局部名稱空間查找。

2.在局部名稱空間找不到的情況下,到全局名稱空間查找,如果找到則使用,放棄搜索;若未找到,則到內置命名空間查找

    2.作用域:

   作用域分兩類:

    全局作用域:對應內置名稱空間和全局名稱空間

    局部作用域:對應局部名稱空間

上面已經說過,變數名的查找順序為:局部名稱空間->全局名稱空間->內置名稱空間

我們可以通過兩個內置函數查看作用域的變數:globals() 和locals(),其中globals()可以查看全局作用域中的變數,locals()可以查看局部作用域中的變數。下麵一個例子:

 1 x=1000
 2 print(globals())
 3 
 4 結果:
 5 {'__file__': 'D:/PycharmProjects/untitled8/user.py',
 6  '__cached__': None,
 7  '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000000009D6CC0>,
 8  '__doc__': None,
 9 '__name__': '__main__',
10 '__package__': None, 
11 '__spec__': None, 
12 '__builtins__': <module 'builtins' (built-in)>,     #這裡就是內置名稱空間的變數的引用
13 'x': 1000                   #我們在全局名稱空間聲明的變數x也可以看到
14 }

 

下麵看一個locals()的例子:

1 x=1000
2 def foo():
3     x=100
4     print(locals())
5 
6 foo()
7 
8 #結果
9 {'x': 100}     #這裡只有一個局部名稱空間中聲明的變數

 

    在全局作用域聲明的變數在全局有效,在任何位置都可以訪問到,除非使用del刪除,否則一直存活,知道文件執行完畢即python解釋器退出;

    在局部作用域聲明的變數在局部有效,只能在局部範圍內訪問,只在函數調時的後有效,函數調用結束後失效

第四部分:閉包

    閉包的定義:內部函數包含外部作用域而非對全局作用域的引用,該內部函數就是閉包:

 1 x=1000            #聲明一個全局變數
 2 def f1():
 3     x=10           #聲明一個局部變數
 4     def f2():     #定義一個閉包函數
 5         print(x)
 6     return f2      #返回f2函數的記憶體地址
 7 
 8 f=f1()             #將f1函數的執行結果賦值給f.即f和f1都指向同一個記憶體地址
 9 print(f)
10 f()                 #調用函數f,間接調用函數f2()
11 
12 #結果
13 <function f1.<locals>.f2 at 0x00000000006CE1E0>    #從此處可以看到f1的內部函數f2的記憶體地址,也是f指向的地址
14 10

 

包含__closure__方法的對象就成為閉包對象,我們通過下麵的例子認識下:

 1 x=2
 2 def f1():
 3     y=2
 4     def f2():
 5         print(x,y)
 6     return f2
 7 f=f1()
 8 f()
 9 print(f)                      #返回f指向的內容,即f2()函數的記憶體地址
10 print(f.__closure__)
11 print(dir(f.__closure__[0]))    #__closure__ 返回閉包的指針
12 print(f.__closure__[0].cell_contents)  #cell_contents 返回閉包的結果
13 
14 #結果
15 2 2
16 <function f1.<locals>.f2 at 0x00000000007DE1E0>
17 (<cell at 0x00000000007A5D38: int object at 0x0000000052F301F0>,)
18 ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__','__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
20  '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
22 '__subclasshook__', 'cell_contents'] 23 2

 

第五部分:裝飾器

    裝飾器的概念:裝飾其他的函數,修飾添加功能。裝飾器本身可以是任何可調用的對象,被裝飾的對象是任何可調用的對象。

    看下麵的例子,我們已經開發了一個函數,現在想添加一個功能,在調用函數的時候,列印函數運行的時間。完成的前提是不能修改我們之前寫好的函數,如下:

 1 import time
 2 def timer(fun):                 #將一個函數作為參數傳遞進來
 3     def wrapper():              #閉包函數wrapper
 4         start_time=time.time()
 5         res=fun()               #調用函數,並獲取返回值
 6         end_time=time.time()
 7         print('%s' % (end_time-start_time))
 8         return res               #將返回值返回
 9     return wrapper             
10 
11 @timer                            # 這句的功能類似這樣 index=wrapper(index)
12 def index():
13     time.sleep(3)
14     print('welcome')
15     return 1
16   
17 res=index()
18 print(res)
19 
20 
21 結果:
22 welcome
23 3.010805368423462
24 1

 

    上面是一個沒有參數的裝飾器的應用,在原函數 ”index()“ 代碼沒有變動的情況下,實現了計算時間的功能,並且不影響對 index() 的函數的調用,非常方便。看了上面的例子,我們可以得出為什麼會使用裝飾器的原因:1,開放封閉原則:對程式的修改是封閉的,對程式的擴展是開放的;2,裝飾器就是為了在不修改被裝飾對象的源代碼以及調用當時的前提下,為其添加新功能。

    再來看下麵的一個例子有參數的裝飾器,該例子為原來的函數添加認證的功能:

 1 def check_auth(args,kwargs):          #從db文件中獲取用戶名/密碼,如果正確,則返回True,否則返回False
 2     res=[]
 3     with open('db','r',encoding='utf-8') as f:
 4         res=f.read().split()
 5     for i in res:
 6         if args[0]==i.split(',')[0] and args[1] == i.split(',')[1]:
 7             return True
 8     else:
 9         return False
10 
11 
12 def check_driver(driver='file'):                      #裝飾器成了三層,因為在調用裝飾器函數的時候有傳遞參數,所以需要在原來兩層裝飾器的基礎上在加一層
13     def auth(func):                                   #閉包函數
14         def wrapper(*args,**kwargs):                  #閉包函數,如果原來函數接受參數,則將參數傳遞到閉包函數中,*args,**kwargs會從全局變數中找。
15             if driver =='file':
16                 result=check_auth(args,kwargs)
17                 if result:                            #如果驗證通過,則放行用戶
18                     func(*args,**kwargs)               #調用原來的函數。實現頁面的顯示
19             elif driver == 'mysql':
20                 print('=========mysql==========')
21             elif driver == 'ldap':                    #根據裝飾器獲得的參數進行不同的操作。這裡簡寫了
22                 print('==========ldap==========')
23         return wrapper
24     return auth
25 
26 @check_driver('file')                    #這裡的裝飾器添加了參數。
27 def index():
28     print('welcome %s your password is %s' % (name,password))
29 
30 
31 name=input('Input username: ').strip()
32 password=input('Input password:').strip()
33 index(name,password)                       #函數調用處沒有改變

 

 

第六部分:迭代器

    迭代器的概念:重覆的過程稱為迭代,且每次迭代的結果作為下次的初始值。重點在重覆,且本次迭代的結果作為下次迭代的初始值。

    我們先來看一個便利數組的例子:

 1 a=['a','b','c','d','e']
 2 
 3 for i in a:
 4     print(i)
 5 
 6 結果:
 7 a
 8 b
 9 c
10 d
11 e

 

    上面就是一個迭代的例子,重覆讀取列表a的內容,並且下次從本次讀取的索引的下一個進行取數,並列印。

    下麵介紹兩個概念:可迭代對象,迭代器。

        可迭代對象:實現了__iter__()方法的對象成為可迭代對象,向list,tuple,dict

        迭代器:實現了__iter__()方法,並且實現了__next__()方法的對象就是迭代器。如file

    這也就是說迭代器肯定是可迭代對象,而迭代對象不一定是迭代器。

    既然已經有可迭代對象用來迭代數據了,那麼為什麼要有迭代器呢?答案是對於沒有索引的數據類型,必須提供一種不依賴於索引的迭代方式,即下麵講到的next()方法調用。

    迭代對象通過執行__iter__()方法,就會獲得一個迭代器:

1 a=list([1,2,3,4,5])
2 b=a.__iter__()
3 print(b.__next__())   #1
4 print(b.__next__())   #2
5 print(b.__next__())   #3
6 print(b.__next__())   #4
7 print(b.__next__())   #5
8 print(b.__next__())   #StopIteration

 

    如上:對list執行__iter__()方法,生成一個迭代器,迭代器使用__next__()方法獲取下一個值,當迭代器中的值取完後,繼續取值會觸發一個StopIteration的異常。

    那麼有沒有現成的函數說明一個對象是可迭代對象還是一個迭代器,有 collections模塊的 Iterable,Iterator 方法:

1 from collections import Iterable,Iterator
2 a=list([1,2,3,4,5])           #a是一個列表
3 print(isinstance(a,Iterable))                     #True
4 print(isinstance(a,Iterator))                      #False
5 f=open('db',mode='r',encoding='utf-8')   #f是一個文件對象
6 print(isinstance(f,Iterator))                    #True
7 print(isinstance(f,Iterable))                    #True
8 f.close()

 

其實,迭代器有一些優點和缺點,如下:

    優點:1.提供一種不依賴下標的迭代方式

             2.就迭代器本身來說,更節省記憶體,因為一次迭代一個值,不會因為原對象有大量的值而占用大量記憶體

   缺點:1.對於有下標的對象來說(如list,tuple),迭代器只能取一次下標的值,不能取多次同一下標的值,不如序列類型對象取值靈活

            2.迭代器無法獲取迭代器對象長度

 第七部分:生成器

     概念:只要函數體包含yield關鍵字,則該函數就是生成器函數。下麵舉一個例子: 

 1 def foo():
 2     print('one')
 3     yield 1
 4     print('two')
 5     yield 2
 6     print('three')
 7     yield 3
 8     print('for')
 9     yield 4
10 
11 g=foo()
12 next(g)                      #輸出one,卡在 yield 1執行後就是說,列印’one',返回1,然後停止執行
13 print(next(g))              #輸出two ,輸出返回值2,再次停止

 

 

 

生成器很普通函數的最大區別就是普通函數執行return後函數立即終止了,下次調用函數還是從函數起始部分開始調用,而生成器在執行到yield那裡會返回數值,下載再次調用函數,函數從上次停止的地方繼續執行。

在看一個好玩的例子:

    

 1 #輸出9x9乘法表
 2 def get_num(li):
 3     for i in li:
 4         for j in li:
 5             if i!=j:
 6                 yield i,j
 7 def shiqi():
 8     a=['1','2','3','4','5','6','7','8']
 9     new_num=[]
10 
11     for i,j in get_num(a):
12         new_num.append(i+j)
13     print(new_num)
14 shiqi()

 

 

 

    綜上:

    yield的功能:

    1.為函數封裝好__inter__和__next__方法

    2.return只能返回一次值,函數就終止了;而生成器(yield)能返回多次值,每次返回將函數暫停,下次__next__會從上次暫停位置繼續執行

第八部分:內置函數

python的內置函數如下:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上周在安裝搜索引擎Elasticsearch時,要求安裝比較新的java 版本,我選擇了java 1.8.0,安裝java 成功後使用java -version 發現使用的版本仍舊是1.6.0, 查詢了一些資料,發現可以使用Linux的alternatives命令替換選擇軟體的版本。 說明:alte ...
  • 伺服器端多線程類: ...
  • dplyr專註處理dataframe對象, 並提供更穩健的與其它資料庫對象間的介面。 一、5個關鍵的數據處理函數: select() 返回列的子集 filter() 返回行的子集 arrange() 根據一個或多個變數對行排序。 mutate() 使用已有數據創建新的列 summarise() 對各 ...
  • querySet.distinct() 去重覆__exact 精確等於 like 'aaa' __iexact 精確等於 忽略大小寫 ilike 'aaa' __contains 包含 like '%aaa%' __icontains 包含 忽略大小寫 ilike '%aaa%',但是對於sqlit ...
  • Given a list, rotate the list to the right by k places, where k is non-negative. ...
  • 一:參考官方文檔 1. Elasticsearch 5.4.0英文手冊 https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-post-filter.html 2. 《Elasticsearch權威指南》 ...
  • 在這裡我們要說的拓撲排序是有前提的 我們在這裡說的拓撲排序是基於有向無環圖的!!!。 (⊙o⊙)…我所說的有向無環圖都知道是什麼東西吧。。 如果不知道,我們下麵先來來說說什麼是有向無環圖。 所謂有向無環圖,顧名思義是不存在環的有向圖(至於有向圖是什麼不知道的在前面我們有一個圖論講解上都有)。 點的入 ...
  • ★☆ 輸入文件:2015message.in 輸出文件:2015message.out 簡單對比 時間限制:1 s 記憶體限制:256 MB 【題目描述】 有n個同學(編號為1到n)正在玩一個信息傳遞的游戲。在游戲里每人都有一個固定的信息傳遞對象,其中,編號為i的同學的信息傳遞對象是編號為Ti同學。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...