python學習-函數

來源:互聯網
上載者:User

標籤:自己   產生   定義函數   數通   pen   代碼   win   變數範圍   global   

python學習-函數

標籤: python 函數

目錄

[toc]

一、檔案處理

python的內建函數open提供了對檔案的處理功能。open函數會調用os的系統介面,得到一個類檔案對象f,此f對象將作為對檔案操作的標識符。從行為動作劃分,檔案處理分為讀取和寫入。從操作的資料類型劃分,檔案處理分為操作字串和操作位元據。

1、讀取檔案
with open(file_path, ‘r‘, encoding=‘utf-8‘) as f:    data = f.read()

注意:
: 1、使用with來妥善處理檔案的關閉問題。
2、給定的file_path是檔案的路徑,python預設目前的目錄(相對路徑),也可以給出絕對路徑,不過在windows下的路徑要注意 \ 的問題。
3、‘r‘表示了讀模數式,如果使用‘rb‘則意味著使用讀取二進位模式,兩者的區別在於,‘r‘模式會使用encoding參數指定的字元編碼格式對檔案進行解碼,並轉換成unicode到記憶體中作為str類型存在,而‘rb‘則不會解碼,而是直接將硬碟上的位元據讀取到記憶體中作為bytes類型存在。
4、使用‘r‘更適用於需要對檔案內容執行顯式的字串操作,一般有需要展示內容的需求。使用‘rb‘更適用於對資料進行非顯式操作,如直接傳輸、壓縮等等,不需要展示內容給使用者。
5、readreadlinereadlines分別對應:讀取整個檔案作為一個字串、讀取一行作為一個字串、讀取所有行並返回一個行列表。

2、寫入檔案
with open(file_path, ‘w‘, encoding=‘utf-8‘) as f:    f.write(msg)

注意:
: 1、使用‘w‘模式將會首先尋找此檔案是否存在,如果不存在則建立,如果存在,則會清空原有檔案的所有內容。
2、使用write函數對檔案執行寫入操作,msg應該是str類型。

3、檔案操作函數
file_path = ‘text.py‘with open(file_path, ‘w+‘, encoding=‘utf-8‘) as f:    print(f.fileno())  # 擷取檔案對象在核心中的索引值    f.write(‘hello, world‘)  # 寫入資料    f.flush()  # 在不關閉檔案的情況下,強制將緩衝中的內容刷入硬碟    f.seek(0)  # 移動seek到開頭    print(‘這裡有資料‘, f.read())  # 讀取所有內容    print(f.readable())  # 是否可讀    print(f.writable())  # 是否可寫    print(f.seekable())  # 是否可移動seek    f.seek(0)    f.truncate()  # 從seek處開始截取到檔案末尾    print(‘這裡沒資料‘, f.read())  # 讀取內容    print(f.tell())  # 擷取當前seek位置
4、注意事項
  • [x] 使用+來擴充原有的檔案處理模式,如w+r+
  • [x] 檔案的操作要特別注意當前的seek位置,seek位置會隨著讀取和寫入而後移。
  • [x] 檔案對象f可以被迭代,即:for line in f,可以每次擷取一行,類似readline
  • [x] 可以使用charsetdetect函數對位元據的編碼格式進行推測。
  • [x] 使用aab模式來對檔案追加內容,使用此模式時seek將會自動移到檔案末尾。
  • [x] 使用seek(0)函數配合truncate()函數完成檔案清空操作。
  • [x] os模組提供了很多函數用於處理檔案和目錄。
  • [x] 所有直接對硬碟資料的修改都會覆蓋當前內容,無法直接移動位置,必須通過讀取到內容、修改內容、寫入硬碟的方式。
二、函數基礎

函數在python中是第一類對象,即函數可以像變數一樣被賦值。函數名是函數對象的引用,函數對象和普通變數沒有太大的區別,普通對象比如數字對象可以四則運算,集合類型對象可以迭代,函數對象則可以執行。函數對象儲存了執行代碼和執行時的上下文環境,函數每次被執行的時候都要在記憶體中開闢一個新的函數棧用於儲存函數的執行內容,然後執行函數對象中的代碼,一旦函數執行完畢就會通過return返回函數執行的結果,並銷毀此函數棧,從而結束一個函數的運行。

1、函數體

函數體用於定義函數執行時的代碼,函數體中的代碼會被函數對象所儲存並在函數被調用的時候被執行。函數被()所調用。

2、輸入

函數可以被認為是一個小型程式或者子常式,是一個電腦的微型模型,可以接受調用者提供的輸入。調用者的輸入不同,函數執行的過程和結果也不同,所以雖然函數體中的代碼已經預先定義好了,但是調用者可以通過不同的輸入來實現不同的執行效果。而函數的形參就是用於接收可變化的輸入值的變數。

3、輸出

函數一定有輸出,即使沒有顯式的聲明return語句,也會在執行代碼的最後一行添加return None。函數的輸出用於表示此函數的執行結果,不論此函數是否需要輸出結果給調用者,輸出都被要求必須提供,哪怕提供的是None。一般調用者通過函數的輸出來判斷執行的結果,並可以通過輸出來對外部函數的執行後續流程做一定的決策。

4、局部變數/變數範圍

因為函數的執行內容存在於函數棧,而函數執行完畢後將會銷毀此函數棧,故在函數運行過程中定義的所有變數都被稱為局部變數,因為這些變數都存活在此函數棧中。可以在函數運行過程中通過locals()函數來擷取當前函數的函數棧中的局部變數命名空間內容。正因為變數僅存活在函數棧中,故這些變數的範圍也就只在函數執行過程中生效。一旦函數執行完畢,將無法訪問函數棧中的變數地址,當然,除非你顯式的return這些變數地址

5、匿名函數

所謂匿名函數,即是一個沒有函數名的函數,如下兩個聲明是完全等價的:

a = 2  # 聲明對象2,名字是a2  # 聲明無名對象2#===============def f():  # 聲明有名對象函數, 名字是f    print(‘hello‘)lambda x:print(‘hello‘)  # 聲明無名對象函數  --> 匿名函數

匿名函數一般配合mapfilterreduce等高階函數使用,此時匿名函數作為其他函數的輸入。

6、函數調用

每一個函數的執行都依賴於對應的函數棧,即使是python程式沒有定義任何函數,此程式之所以會運行,也是依賴於頂層函數棧提供了全域命名空間。在函數發生調用的時候,cpu代碼執行視角將會從當前函數跳轉到另一個函數所在的記憶體位址,一旦被調函數執行完畢,cpu代碼執行視角將會回到主調函數的下一條代碼地址處,主調函數獲得被調函數的傳回值。

7、命名空間LEGB

local 當前函數所在的命名空間
enclosing 嵌套函數所在的命名空間,從兒子到父親到爺爺的尋找鏈
global 全域命名空間,即模組的命名空間
builtin 內建命名空間,即模組的上一級,定義了所有的內建函數和變數
檢測順序是:L --> E --> G --> B --> 報錯

8、函數參數解包

函數中的*args和**kw提供瞭解包,args代表元組,kw代表字典,可以這樣使用:

def show(a, b):    print(a, b)x = (1,2)show(*x)#=========def show(a=1, b=2):    print(a, b)x = {‘a‘: 10, ‘b‘: 20}show(**x)
9、遞迴

所謂的遞迴,就是函數調用過程中調用自己,這樣會產生和無限迴圈類似的無限調用自己。函數每一次調用都需要建立一個函數棧,而因為主調函數並沒有執行完畢,所以主調函數的函數棧不會銷毀,而是會等待被調函數返回結果。無限遞迴,就會無限產生新的被調函數棧,保留主調函數棧,而導致函數棧溢出python函數棧設定為最大1000層。

使用遞迴一定需要設定遞迴停止條件,就像使用迴圈一定要設定迴圈允出準則一樣。

使用遞迴可以方便的解決某些需要不斷調用自身演算法的問題,但是遞迴效率本身並不高,因為需要不停的 建立新的函數棧和銷毀函數棧。

三、內建函數
print(dir(list))  # 查看對象的所有屬性名稱列表print(sorted([233,34,54,56,56,67,67]))  # 對序列進行排序,返回新列表對象print(eval(‘3 + 5‘))  # 執行字串代碼,得到傳回值,無法執行賦值等更改程式內容的語句exec(‘a = 2‘)  # 執行多行字串,傳回值永遠是None,但是可以執行賦值等更改程式內容的語句print(a)filter(lambda x: x%2==0, [34,34,54,54,56,6,2,2,3,34])  # 執行序列過濾map(lambda x: str(x), [2334,34,4,5])  # 執行序列各元素統一處理from functools import reducereduce(lambda x, y: x+y, [23,34,34,45,5,56])  # 執行序列迴歸累計操作print(‘msg is here‘, ‘another msg‘, sep=‘!‘, end=‘\n!!!!\n‘)  # print函數的參數使用f = lambda x: 2callable(f)  # 對象是否可調用判斷print(list(zip([23,23,32,43], [4,34,43,34])))  # 執行多個序列的打包操作,會截斷序列以最短的為準,多個序列元素打包成元組
四、函數進階1、閉包

函數一旦定義完畢,就會將執行代碼和上下文環境儲存到函數對象中。上下文環境除了保留當前層次的環境之外,還會保留外層嵌套的上下文環境。一旦此函數被作為變數返回給外部函數,外部函數可以隨時調用,被調用時,內層函數即可訪問之前保留的嵌套函數中的上下文。此即:閉包,即,將執行代碼和執行環境一起包裹,一旦被執行,就可以使用嵌套的上下文環境。閉包可以用於保留嵌套函數的變數狀態。

def outer():    a = 20    def inner():        print(a)    return innerf = outer()f()  # 執行inner的時候,可以訪問到a = 20
2、裝飾器函數

正因為閉包的性質,可以對函數進行功能擴充,在不修改原有函數代碼的前提下,增加額外的功能。裝飾器也是一種高階函數,將原函數作為輸入,對原函數進行功能擴充並返回與原函數同名的新函數。新函數一旦被執行,將會把原函數功能和擴充功能一起執行。

func_map = {}def register(key):    def wrapper_outer(func):        def wrapper_inner(*args, **kw):            result = func(*args, **kw)            print(‘擴充功能‘)            return result        func_map[key] = wrapper_inner        return wrapper_outer    return wrapper_outer@register(‘show‘)def show(msg):    print(‘this is show msg:‘, msg)msg = ‘hello, world‘func_map[‘show‘](msg)
3、產生器函數

產生器函數可以被設計成永遠不會銷毀函數棧,但是卻可以和主調函數通過yield進行函數棧的切換。產生器函數最大的好處有如下幾個:

1.延遲計算,產生器需要手工執行next或者send才會執行產生器函數代碼

2.可以和主函數進行函數棧切換,效率高,串列無鎖,無變數安全問題

3.產生器函數可以通過yield傳回值,主調函數通過send發送值,兩者可以互動

4.可以用於實現協程

log_path = ‘xxx.log‘def logger_generator(log_path):    log_count = 0    with open(log_path, ‘w‘, encoding=‘utf-8‘) as f:  # 準備log檔案,預先清空內容        pass    msg = yield ‘ok‘    while True:  # 永遠執行        if msg == ‘stop‘:            return ‘stop‘  # 引發stopiteration,主調函數要try        else:            log_count += 1            msg = ‘# [{log_count}] --> {msg}\n‘.format(log_count=log_count, msg=msg)  # 準備日誌資訊            with open(log_path, ‘a‘, encoding=‘utf-8‘) as f:  # 記錄日誌到檔案                f.write(msg)            log_summary = ‘%d informations has been logged!‘ %log_count  # 準備日誌匯總資訊            msg = yield log_summary  # 返回截止目前的日誌匯總資訊,同時等待主調發送新的日誌訊息logger = logger_generator(log_path)logger.send(None)print(logger.send(‘使用者登入‘))print(logger.send(‘使用者密碼修改‘))
五、幾個技術問題1、解包
li = [1,2,3,4,5]first, *mid, last = liprint(first, mid, last)first, second, *_ = liprint(first, second, _)a, b, *c = range(5)print(a, b, c)dic1 = {‘a‘:1}dic2 = {‘b‘:2}dic3 = {**dic1, **dic2}print(dic3)dic = {    ‘name‘: ‘hz‘,    ‘age‘: 26,}msg = ‘name is: {name}, age is: {age}‘.format(**dic)print(msg)

1.使用解包可以讓代碼更簡潔

2.***分別用於解包序列和字典

3.使用*_來承載不需要的值,_變數也可以被使用

2、值交換

如下兩者是等價的:

a = 2b = 33temp = aa = bb = tempprint(a, b)#=========a = 3b = 44a, b = b, aprint(a, b)

python的值交換會自動幫你處理temp臨時變數

3、預設參數陷阱
a = 20def show(x=a):    print(x)a = 30show()show(a)print(‘#=========‘)def show(x=[]):    x.append(99)    print(x)show()show()show()

結果是:

2030#=========[99][99, 99][99, 99, 99]

1.預設參數在函數被編譯的時候就固定了引用對象

2.如果不顯式的提供show(a)就會列印原來的固定對象20

3.如果預設參數指向可變對象,因為可變對象可以修改值,會導致非期望的結果如[99,99,99]

4.預設參數一定要指向不可變對象,而且調用函數的時候盡量顯式的提供值

4、產生器深入理解

產生器的好處:

  • [x] 儲存計算規則、演算法、節省記憶體空間,隨時計算。
  • [x] 高效的函數棧切換,產生器函數還可以嵌套多層,多個yield提供更靈活的切換控制
  • [x] 儲存產生器函數中的上下文環境不會被銷毀,只要切換進去就可恢複使用這些變數值
  • [x] 可以和主調函數或者其他子層產生器函數相互協作完成程式執行
  • [x] 使用sendnextyield來提供訊息、訊號互動
  • [x] 可以實現協程,高效串列執行,無變數安全問題,不需加鎖

python學習-函數

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.