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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...