當前位置:首頁 » 編程語言 » python函數套函數

python函數套函數

發布時間: 2023-05-27 04:33:10

① 核心解密python函數在(類與函數之間)和(類與類之間)互相調用

首先來看一個函數間的調用

類方法:

執行結果:

metaclass能有什麼用處,先來個感性的認識:

1.1 在wiki上面,metaclass是這樣定義的:In object-oriented programming,

a metaclass is a class whose instances are classes.

Just as an ordinary class defines the behavior of certain objects,

a metaclass defines the behavior of certain classes and their instances.

也就是說metaclass的實例化結果是類,而class實例化的結果是instance。我是這么理解的:

metaclass是類似創建類的模板,所有的類都是通過他來create的(調用 new ),這使得你可以自由的控制

創建類的那個過程,實現你所需要的功能。

當然你也可以用函數的方式(下文會講)

4.1 用類的形式

4.1.1 類繼承於type, 例如: class Meta(type):pass

4.1.2 將需要使用metaclass來構建class的類的 metaclass 屬性(不需要顯示聲明,直接有的了)賦值為Meta(繼承於type的類)

4.2 用函數的形式

4.2.1 構建一個函數,例如叫metaclass_new, 需要3個參數:name, bases, attrs,

name: 類的名字

bases: 基類,通常是tuple類型

attrs: dict類型,就是類的屬性或者函數

4.2.2 將需要使用metaclass來構建class的類的 metaclass 屬性(不需要顯示聲明,直接有的了)賦值為函數metaclas_new

5.1 basic

metaclass的原理其實是這樣的:當定義好類之後,創建類的時候其實是調用了type的 new 方法為這個類分配內存空間,創建

好了之後再調用type的 init 方法初始化(做一些賦值等)。所以metaclass的所有magic其實就在於這個 new 方法裡面了。

說說這個方法: new (cls, name, bases, attrs)

cls: 將要創建的類,類似與self,但是self指向的是instance,而這里cls指向的是class

name: 類的名字,也就是我們通常用類名. name 獲取的。

bases: 基類

attrs: 屬性的dict。dict的內容可以是變數(類屬性),也可以是函數(類方法)。

所以在創建類的過程,我們可以在這個函數裡面修改name,bases,attrs的值來自由的達到我們的功能。這里常用的配合方法是

getattr和setattr(just an advice)

下面實現python中在一個類中調用另一個類的函數方法

或者下面來一個號理解的例子

執行結果:

先來介紹內部類與外部類是什麼?
源碼解析:

內部類調用外部類的類屬性和類方法

參考文獻1
參考文獻2
參考文獻3

② python基礎:內置函數、方法、轉義字元大全

在寫python程序時,常能用到一些函數和方法,總結一下,保存起來,方便查詢。

一、內置函數

# abs()獲取數字絕對值

# chr(i)數字轉換為字元類型

# divmod() 獲取兩個數值的商和余數

# enumerate() 將可遍歷序列組合為索引序列

# float()轉換為浮點數

# format() 格式化字元串

# int()轉換為整數 

# input() 接受用戶輸入內容

# len() 計算元素個數

# max() 返回最大值

# min() 返回最小值

# math.ceil() 返回指定數值的上舍整數

# open()打開文件並返迴文件對象

# pow() 冪運算

# print()列印輸出 

# range() 生成器

# reversed()反轉所有元素

# round()四捨五入求值

# sorted()對可迭代對象進行排序 

# str() 轉換為字元串

# sum() 求和

# set() 創建集合

# tuple() 將序列轉換為元組

# zip()將可迭代對象打包成元組

二、方法

# append() 添加列表元素

# capitalize()首字母轉換為大寫 

# count()字元出現次數

# close() 關閉文件

# decode() 解碼字元串

# dict.keys() 獲取字典所有的鍵

# find()字元串首次出現的索引

# f.read() 讀取文件內容

# dict.update()更新字典

# dict.items() 獲取字典鍵/值對

# dict.get() 返回指定鍵的值

# encode() 編碼字元串

# list.sort() 排序列表元素

# index() 元素首次出現的索引

# isdigit() 判斷字元串是否只由數字組成

# isupper() 是否所有字母都為大寫

# isnum() 判斷字元串是否由字母和數字組成

# islower() 是否所有字母都為小寫

# isdecimal() 檢查字元串是否只包含十進制字元

# isalpha() 檢測字元串是否為純字母

# random.shuffle()隨機排序

# random.sample()返回無重復隨機數列表

# random.choice() 返回一個隨機元素

# random.randint() 生成指定范圍的隨機整數

# random.randrange() 生成指定范圍的指定遞增基數隨機整數

# pop() 刪除列表中的元素

# remove()刪除列表中的指定元素

# strip()去除空格

# lstrip()去除左側空格

# rstrip() 去除右側空格

# readline() 讀取單行內容

# root.after() Tkinter中等待一段時間後再執行命令

# str.isnumeric() 驗證字元串是否為數字(適用於Unicode)

# split()分割字元串

# ord() 將字元轉換為整數

# replace() 字元串替換

# ljust() 左對齊填充

# rjust() 左對齊填充

# readlines() 讀取所有行內容

# datetime.datetime.now() 返回指定時區的本地日期時間

# datetime.datetime.today() 獲取當前本地日期的date對象

# datetime.utcnow() 返回當前UTC時間的datetime對象

# time.strptime()把時間字元串解析為元組

# time.time()返回當前時間的時間戳

# time.sleep()暫停指定秒數

# time.strftime() 返回指定格式的日期字元串

# time.mktime() 接收時間元組並返回時間戳

# os.getcwd() 獲取當前工作目錄

# os.listdir() 獲取指定路徑下的目錄和文件列表

# os.makedirs() 遞歸創建目錄

# os.rename() 重命名目錄或文件

# os.path.exists() 判斷路徑是否存在

# upper() 全部轉換為大寫字母

# lower()  全部轉換為小寫字母

# sys.stdout.write() 標准輸出列印

# sys.stdout.flush()刷新輸出 

# shutil.() 復制單個文件到另一文件或目錄

# write() 寫入文件內容

# winsound.Beep() 打開電腦揚聲器

# zfill() 在字元串前面填充0

三、循環語句

# break終止當前循環

# continue 終止本循環進入下一次循環

# with open() as file 以with語句打開文件(數據保存)

四、轉義字元

\ 行尾續行符

\' 單引號 

\'' 雙引號

\a 響鈴

\e 轉義

\n 換行

\t 橫向製表符

\f 換頁

\xyy 十六進制yy代表的字元

\\反斜杠符號

\b 退格

\000 空

\v 縱向製表符

\r 回車

\0yy 八進制yy代表的字元

\other 其他的字元以普通格式輸出

③ python中怎麼在一個函數內調用另外一個函數,類

今天遇到同樣的問題,就來答一波吧

1,如果是在類中,那麼就很簡單了,類中的一個函數調用另一個函數,只要在那個被調用的函數前加self即可(圖如下,詳細可以參考筆者博客),

4,最後,更多關於python問題可以參考筆者的python教程筆記

④ Python的函數都有哪些

Python 函數

函數是組織好的,可重復使用的,用來實現單一,或相關聯功能的代碼段。

函數能提高應用的模塊性,和代碼的重復利用率。你已經知道Python提供了許多內建函數,比如print()。但你也可以自己創建函數,這被叫做用戶自定義函數。

定義一個函數

你可以定義一個由自己想要功能的函數,以下是簡單的規則:

  • 函數代碼塊以def關鍵詞開頭,後接函數標識符名稱和圓括弧()。

  • 任何傳入參數和自變數必須放在圓括弧中間。圓括弧之間可以用於定義參數。

  • 函數的第一行語句可以選擇性地使用文檔字元串—用於存放函數說明。

  • 函數內容以冒號起始,並且縮進。

  • return [表達式]結束函數,選擇性地返回一個值給調用方。不帶表達式的return相當於返回 None。

  • 語法

    def functionname( parameters ): "函數_文檔字元串"
    function_suite
    return [expression]

    默認情況下,參數值和參數名稱是按函數聲明中定義的順序匹配起來的。

    實例

    以下為一個簡單的Python函數,它將一個字元串作為傳入參數,再列印到標准顯示設備上。

    實例(Python 2.0+)

    def printme( str ): "列印傳入的字元串到標准顯示設備上"
    print str
    return

    函數調用

    定義一個函數只給了函數一個名稱,指定了函數里包含的參數,和代碼塊結構。

    這個函數的基本結構完成以後,你可以通過另一個函數調用執行,也可以直接從Python提示符執行。

    如下實例調用了printme()函數:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    # 定義函數def printme( str ): "列印任何傳入的字元串"
    print str
    return
    # 調用函數printme("我要調用用戶自定義函數!")printme("再次調用同一函數")

    以上實例輸出結果:

  • 我要調用用戶自定義函數!再次調用同一函數

  • 參數傳遞

    在 python 中,類型屬於對象,變數是沒有類型的:

    a=[1,2,3]
    a="Runoob"

    以上代碼中,[1,2,3]是 List 類型,"Runoob"是 String 類型,而變數 a 是沒有類型,她僅僅是一個對象的引用(一個指針),可以是 List 類型對象,也可以指向 String 類型對象。

    可更改(mutable)與不可更改(immutable)對象

    在 python 中,strings, tuples, 和 numbers 是不可更改的對象,而 list,dict 等則是可以修改的對象。

  • 不可變類型:變數賦值a=5後再賦值a=10,這里實際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,相當於新生成了a。

  • 可變類型:變數賦值la=[1,2,3,4]後再賦值la[2]=5則是將 list la 的第三個元素值更改,本身la沒有動,只是其內部的一部分值被修改了。

  • python 函數的參數傳遞:

  • 不可變類型:類似 c++ 的值傳遞,如 整數、字元串、元組。如fun(a),傳遞的只是a的值,沒有影響a對象本身。比如在 fun(a)內部修改 a 的值,只是修改另一個復制的對象,不會影響 a 本身。

  • 可變類型:類似 c++ 的引用傳遞,如 列表,字典。如 fun(la),則是將 la 真正的傳過去,修改後fun外部的la也會受影響

  • python 中一切都是對象,嚴格意義我們不能說值傳遞還是引用傳遞,我們應該說傳不可變對象和傳可變對象。

    python 傳不可變對象實例

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    def ChangeInt( a ): a = 10
    b = 2ChangeInt(b)print b # 結果是 2

    實例中有 int 對象 2,指向它的變數是 b,在傳遞給 ChangeInt 函數時,按傳值的方式復制了變數 b,a 和 b 都指向了同一個 Int 對象,在 a=10 時,則新生成一個 int 值對象 10,並讓 a 指向它。

    傳可變對象實例

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    # 可寫函數說明def changeme( mylist ): "修改傳入的列表"
    mylist.append([1,2,3,4])
    print "函數內取值: ", mylist
    return
    # 調用changeme函數mylist = [10,20,30]changeme( mylist )print "函數外取值: ", mylist

    實例中傳入函數的和在末尾添加新內容的對象用的是同一個引用,故輸出結果如下:

  • 函數內取值: [10, 20, 30, [1, 2, 3, 4]]函數外取值: [10, 20, 30, [1, 2, 3, 4]]

  • 參數

    以下是調用函數時可使用的正式參數類型:

  • 必備參數

  • 關鍵字參數

  • 默認參數

  • 不定長參數

  • 必備參數

    必備參數須以正確的順序傳入函數。調用時的數量必須和聲明時的一樣。

    調用printme()函數,你必須傳入一個參數,不然會出現語法錯誤:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    #可寫函數說明def printme( str ): "列印任何傳入的字元串"
    print str
    return
    #調用printme函數printme()

    以上實例輸出結果:

  • Traceback (most recent call last):

  • File "test.py", line 11, in <mole>

  • printme()TypeError: printme() takes exactly 1 argument (0 given)

  • 關鍵字參數

    關鍵字參數和函數調用關系緊密,函數調用使用關鍵字參數來確定傳入的參數值。

    使用關鍵字參數允許函數調用時參數的順序與聲明時不一致,因為 Python 解釋器能夠用參數名匹配參數值。

    以下實例在函數 printme() 調用時使用參數名:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    #可寫函數說明def printme( str ): "列印任何傳入的字元串"
    print str
    return
    #調用printme函數printme( str = "My string")

    以上實例輸出結果:

  • My string

  • 下例能將關鍵字參數順序不重要展示得更清楚:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    #可寫函數說明def printinfo( name, age ): "列印任何傳入的字元串"
    print "Name: ", name
    print "Age ", age
    return
    #調用printinfo函數printinfo( age=50, name="miki" )

    以上實例輸出結果:

  • Name: mikiAge 50

  • 默認參數

    調用函數時,默認參數的值如果沒有傳入,則被認為是默認值。下例會列印默認的age,如果age沒有被傳入:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    #可寫函數說明def printinfo( name, age = 35 ): "列印任何傳入的字元串"
    print "Name: ", name
    print "Age ", age
    return
    #調用printinfo函數printinfo( age=50, name="miki" )printinfo( name="miki" )

    以上實例輸出結果:

  • Name: mikiAge 50Name: mikiAge 35

  • 不定長參數

    你可能需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數,和上述2種參數不同,聲明時不會命名。基本語法如下:

    def functionname([formal_args,] *var_args_tuple ): "函數_文檔字元串"
    function_suite
    return [expression]

    加了星號(*)的變數名會存放所有未命名的變數參數。不定長參數實例如下:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    # 可寫函數說明def printinfo( arg1, *vartuple ): "列印任何傳入的參數"
    print "輸出: "
    print arg1
    for var in vartuple: print var
    return
    # 調用printinfo 函數printinfo( 10 )printinfo( 70, 60, 50 )

    以上實例輸出結果:

  • 輸出:10輸出:706050

  • 匿名函數

    python 使用 lambda 來創建匿名函數。

  • lambda只是一個表達式,函數體比def簡單很多。

  • lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。

  • lambda函數擁有自己的命名空間,且不能訪問自有參數列表之外或全局命名空間里的參數。

  • 雖然lambda函數看起來只能寫一行,卻不等同於C或C++的內聯函數,後者的目的是調用小函數時不佔用棧內存從而增加運行效率。

  • 語法

    lambda函數的語法只包含一個語句,如下:

  • lambda [arg1 [,arg2,.....argn]]:expression

  • 如下實例:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    # 可寫函數說明sum = lambda arg1, arg2: arg1 + arg2
    # 調用sum函數print "相加後的值為 : ", sum( 10, 20 )print "相加後的值為 : ", sum( 20, 20 )

    以上實例輸出結果:

  • 相加後的值為 : 30相加後的值為 : 40

  • return 語句

    return語句[表達式]退出函數,選擇性地向調用方返回一個表達式。不帶參數值的return語句返回None。之前的例子都沒有示範如何返回數值,下例便告訴你怎麼做:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    # 可寫函數說明def sum( arg1, arg2 ): # 返回2個參數的和."
    total = arg1 + arg2
    print "函數內 : ", total
    return total
    # 調用sum函數total = sum( 10, 20 )

    以上實例輸出結果:

  • 函數內 : 30

  • 變數作用域

    一個程序的所有的變數並不是在哪個位置都可以訪問的。訪問許可權決定於這個變數是在哪裡賦值的。

  • 變數的作用域決定了在哪一部分程序你可以訪問哪個特定的變數名稱。兩種最基本的變數作用域如下:
  • 全局變數

  • 局部變數

  • 全局變數和局部變數

    定義在函數內部的變數擁有一個局部作用域,定義在函數外的擁有全局作用域。

    局部變數只能在其被聲明的函數內部訪問,而全局變數可以在整個程序范圍內訪問。調用函數時,所有在函數內聲明的變數名稱都將被加入到作用域中。如下實例:

    實例(Python 2.0+)

    #!/usr/bin/python# -*- coding: UTF-8 -*-
    total = 0 # 這是一個全局變數# 可寫函數說明def sum( arg1, arg2 ): #返回2個參數的和."
    total = arg1 + arg2 # total在這里是局部變數.
    print "函數內是局部變數 : ", total
    return total
    #調用sum函數sum( 10, 20 )print "函數外是全局變數 : ", total

    以上實例輸出結果:

  • 函數內是局部變數 : 30函數外是全局變數 : 0

⑤ python中怎麼在一個函數內調用另外一個函數,類

方案如下:
將另一個py做成一個包,或者直接和調用文件放在同一個目錄下;
在調用者文件頭引入:from py名字 import *;
這樣就可以使用另一個py文件的所有函數了。

分享
本回

⑥ Python 里為什麼函數可以返回一個函數內部定義的函數

「在Python中,函數本身也是對象」
這一本質。那不妨慢慢來,從最基本的概念開始,討論一下這個問題:
1. Python中一切皆對象
這恐怕是學習Python最有用的一句話。想必你已經知道Python中的list, tuple, dict等內置數據結構,當你執行:
alist = [1, 2, 3]

時,你就創建了一個列表對象,並且用alist這個變數引用它:
當然你也可以自己定義一個類:
class House(object):
def __init__(self, area, city):
self.area = area
self.city = city

def sell(self, price):
[...] #other code
return price

然後創建一個類的對象:
house = House(200, 'Shanghai')

OK,你立馬就在上海有了一套200平米的房子,它有一些屬性(area, city),和一些方法(__init__, self):

2. 函數是第一類對象
和list, tuple, dict以及用House創建的對象一樣,當你定義一個函數時,函數也是對象:
def func(a, b):
return a+b

在全局域,函數對象被函數名引用著,它接收兩個參數a和b,計算這兩個參數的和作為返回值。
所謂第一類對象,意思是可以用標識符給對象命名,並且對象可以被當作數據處理,例如賦值、作為參數傳遞給函數,或者作為返回值return 等
因此,你完全可以用其他變數名引用這個函數對象:
add = func

這樣,你就可以像調用func(1, 2)一樣,通過新的引用調用函數了:
print func(1, 2)
print add(1, 2) #the same as func(1, 2)

或者將函數對象作為參數,傳遞給另一個函數:
def caller_func(f):
return f(1, 2)

if __name__ == "__main__":
print caller_func(func)

可以看到,

函數對象func作為參數傳遞給caller_func函數,傳參過程類似於一個賦值操作f=func;

於是func函數對象,被caller_func函數作用域中的局部變數f引用,f實際指向了函數func;cc

當執行return f(1, 2)的時候,相當於執行了return func(1, 2);

因此輸出結果為3。
3. 函數對象 vs 函數調用
無論是把函數賦值給新的標識符,還是作為參數傳遞給新的函數,針對的都是函數對象本身,而不是函數的調用。
用一個更加簡單,但從外觀上看,更容易產生混淆的例子來說明這個問題。例如定義了下面這個函數:
def func():
return "hello,world"

然後分別執行兩次賦值:
ref1 = func #將函數對象賦值給ref1
ref2 = func() #調用函數,將函數的返回值("hello,world"字元串)賦值給ref2

很多初學者會混淆這兩種賦值,通過Python內建的type函數,可以查看一下這兩次賦值的結果:
In [4]: type(ref1)
Out[4]: function

In [5]: type(ref2)
Out[5]: str

可以看到,ref1引用了函數對象本身,而ref2則引用了函數的返回值。通過內建的callable函數,可以進一步驗證ref1是可調用的,而ref2是不可調用的:
In [9]: callable(ref1)
Out[9]: True

In [10]: callable(ref2)
Out[10]: False

傳參的效果與之類似。
4. 閉包&LEGB法則
所謂閉包,就是將組成函數的語句和這些語句的執行環境打包在一起時,得到的對象
聽上去的確有些復雜,還是用一個栗子來幫助理解一下。假設我們在foo.py模塊中做了如下定義:
#foo.py
filename = "foo.py"

def call_func(f):
return f() #如前面介紹的,f引用一個函數對象,然後調用它

在另一個func.py模塊中,寫下了這樣的代碼:
#func.py
import foo #導入foo.py

filename = "func.py"
def show_filename():
return "filename: %s" % filename

if __name__ == "__main__":
print foo.call_func(show_filename) #注意:實際發生調用的位置,是在foo.call_func函數中

當我們用python func.py命令執行func.py時輸出結果為:
chiyu@chiyu-PC:~$ python func.py
filename:func.py

很顯然show_filename()函數使用的filename變數的值,是在與它相同環境(func.py模塊)中定義的那個。盡管foo.py模塊中也定義了同名的filename變數,而且實際調用show_filename的位置也是在foo.py的call_func內部。
而對於嵌套函數,這一機制則會表現的更加明顯:閉包將會捕捉內層函數執行所需的整個環境:
#enclosed.py
import foo
def wrapper():
filename = "enclosed.py"
def show_filename():
return "filename: %s" % filename
print foo.call_func(show_filename) #輸出:filename: enclosed.py

實際上,每一個函數對象,都有一個指向了該函數定義時所在全局名稱空間的__globals__屬性:
#show_filename inside wrapper
#show_filename.__globals__

{
'__builtins__': <mole '__builtin__' (built-in)>, #內建作用域環境
'__file__': 'enclosed.py',
'wrapper': <function wrapper at 0x7f84768b6578>, #直接外圍環境
'__package__': None,
'__name__': '__main__',
'foo': <mole 'foo' from '/home/chiyu/foo.pyc'>, #全局環境
'__doc__': None
}

當代碼執行到show_filename中的return "filename: %s" % filename語句時,解析器按照下面的順序查找filename變數:

Local - 本地函數(show_filename)內部,通過任何方式賦值的,而且沒有被global關鍵字聲明為全局變數的filename變數;

Enclosing - 直接外圍空間(上層函數wrapper)的本地作用域,查找filename變數(如果有多層嵌套,則由內而外逐層查找,直至最外層的函數);

Global - 全局空間(模塊enclosed.py),在模塊頂層賦值的filename變數;

Builtin - 內置模塊(__builtin__)中預定義的變數名中查找filename變數;
在任何一層先找到了符合要求的filename變數,則不再向更外層查找。如果直到Builtin層仍然沒有找到符合要求的變數,則拋出NameError異常。這就是變數名解析的:LEGB法則。
總結:

閉包最重要的使用價值在於:封存函數執行的上下文環境;

閉包在其捕捉的執行環境(def語句塊所在上下文)中,也遵循LEGB規則逐層查找,直至找到符合要求的變數,或者拋出異常。

5. 裝飾器&語法糖(syntax sugar)
那麼閉包和裝飾器又有什麼關系呢?
上文提到閉包的重要特性:封存上下文,這一特性可以巧妙的被用於現有函數的包裝,從而為現有函數更加功能。而這就是裝飾器。
還是舉個例子,代碼如下:
#alist = [1, 2, 3, ..., 100] --> 1+2+3+...+100 = 5050
def lazy_sum():
return rece(lambda x, y: x+y, alist)

我們定義了一個函數lazy_sum,作用是對alist中的所有元素求和後返回。alist假設為1到100的整數列表:
alist = range(1, 101)

但是出於某種原因,我並不想馬上返回計算結果,而是在之後的某個地方,通過顯示的調用輸出結果。於是我用一個wrapper函數對其進行包裝:
def wrapper():
alist = range(1, 101)
def lazy_sum():
return rece(lambda x, y: x+y, alist)
return lazy_sum

lazy_sum = wrapper() #wrapper() 返回的是lazy_sum函數對象

if __name__ == "__main__":
lazy_sum() #5050

這是一個典型的Lazy Evaluation的例子。我們知道,一般情況下,局部變數在函數返回時,就會被垃圾回收器回收,而不能再被使用。但是這里的alist卻沒有,它隨著lazy_sum函數對象的返回被一並返回了(這個說法不準確,實際是包含在了lazy_sum的執行環境中,通過__globals__),從而延長了生命周期。
當在if語句塊中調用lazy_sum()的時候,解析器會從上下文中(這里是Enclosing層的wrapper函數的局部作用域中)找到alist列表,計算結果,返回5050。
當你需要動態的給已定義的函數增加功能時,比如:參數檢查,類似的原理就變得很有用:
def add(a, b):
return a+b

這是很簡單的一個函數:計算a+b的和返回,但我們知道Python是 動態類型+強類型 的語言,你並不能保證用戶傳入的參數a和b一定是兩個整型,他有可能傳入了一個整型和一個字元串類型的值:
In [2]: add(1, 2)
Out[2]: 3

In [3]: add(1.2, 3.45)
Out[3]: 4.65

In [4]: add(5, 'hello')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/chiyu/<ipython-input-4-f2f9e8aa5eae> in <mole>()
----> 1 add(5, 'hello')

/home/chiyu/<ipython-input-1-02b3d3d6caec> in add(a, b)
1 def add(a, b):
----> 2 return a+b

TypeError: unsupported operand type(s) for +: 'int' and 'str'

於是,解析器無情的拋出了一個TypeError異常。
動態類型:在運行期間確定變數的類型,python確定一個變數的類型是在你第一次給他賦值的時候;
強類型:有強制的類型定義,你有一個整數,除非顯示的類型轉換,否則絕不能將它當作一個字元串(例如直接嘗試將一個整型和一個字元串做+運算);
因此,為了更加優雅的使用add函數,我們需要在執行+運算前,對a和b進行參數檢查。這時候裝飾器就顯得非常有用:
import logging

logging.basicConfig(level = logging.INFO)

def add(a, b):
return a + b

def checkParams(fn):
def wrapper(a, b):
if isinstance(a, (int, float)) and isinstance(b, (int, float)): #檢查參數a和b是否都為整型或浮點型
return fn(a, b) #是則調用fn(a, b)返回計算結果

#否則通過logging記錄錯誤信息,並友好退出
logging.warning("variable 'a' and 'b' cannot be added")
return
return wrapper #fn引用add,被封存在閉包的執行環境中返回

if __name__ == "__main__":
#將add函數對象傳入,fn指向add
#等號左側的add,指向checkParams的返回值wrapper
add = checkParams(add)
add(3, 'hello') #經過類型檢查,不會計算結果,而是記錄日誌並退出

注意checkParams函數:

首先看參數fn,當我們調用checkParams(add)的時候,它將成為函數對象add的一個本地(Local)引用;

在checkParams內部,我們定義了一個wrapper函數,添加了參數類型檢查的功能,然後調用了fn(a, b),根據LEGB法則,解釋器將搜索幾個作用域,並最終在(Enclosing層)checkParams函數的本地作用域中找到fn;

注意最後的return wrapper,這將創建一個閉包,fn變數(add函數對象的一個引用)將會封存在閉包的執行環境中,不會隨著checkParams的返回而被回收;

當調用add = checkParams(add)時,add指向了新的wrapper對象,它添加了參數檢查和記錄日誌的功能,同時又能夠通過封存的fn,繼續調用原始的add進行+運算。
因此調用add(3, 'hello')將不會返回計算結果,而是列印出日誌:
chiyu@chiyu-PC:~$ python func.py
WARNING:root:variable 'a' and 'b' cannot be added

有人覺得add = checkParams(add)這樣的寫法未免太過麻煩,於是python提供了一種更優雅的寫法,被稱為語法糖:
@checkParams
def add(a, b):
return a + b

這只是一種寫法上的優化,解釋器仍然會將它轉化為add = checkParams(add)來執行。
6. 回歸問題
def addspam(fn):
def new(*args):
print "spam,spam,spam"
return fn(*args)
return new

@addspam
def useful(a,b):
print a**2+b**2

首先看第二段代碼:

@addspam裝飾器,相當於執行了useful = addspam(useful)。在這里題主有一個理解誤區:傳遞給addspam的參數,是useful這個函數對象本身,而不是它的一個調用結果;

再回到addspam函數體:

return new 返回一個閉包,fn被封存在閉包的執行環境中,不會隨著addspam函數的返回被回收;

而fn此時是useful的一個引用,當執行return fn(*args)時,實際相當於執行了return useful(*args);

最後附上一張代碼執行過程中的引用關系圖,希望能幫助你理解:

⑦ python要連續使用2個函數時,為什麼有時的格式是f1().f2(),有時是f2(f1())

這個和函數本身的性質是有關系的,和一開始的定義相中虧關。
形式不同取決於返回值。
f1().f2() >> 這備培凳種形式感覺有點少見,如果前面是類的實例化就很常見了。仿旅
f2(f1()) >> 則是將 f1() 函數的運行結果作為f2 函數的參數繼續運行。

⑧ python中print函數再內嵌函數的語句問題

在python中,邏輯表達式輸出需要注意這幾個問題:(1)and :x and y 返回的結果是決定表達式結果的值。如果 x 為真,則 y 決定結果,返回 y ;如果 x 為假,x 決定了結果為假,返回 x。(2)or :x or y 跟 and 一樣都是返回決定表達式結果的值。(3)not : 返回表達式結果的「相反的值」。如果表達式結果為真,則返回false;如果表達式結果為假,則返回true。

⑨ python函數嵌套問題

雖然覺得這么畸形的邏輯很難有實際應用,大多數都是誤用。

還是順著去解這個試了下...

deffun1(x):
sum=x
deffun2(y):
returnsum+y
deffun3(z):
nonlocalsum
sum+=z
returnfun2
returnfun3
print(fun1(1)(2)(3))

得6

⑩ python函數嵌套內存不是釋放

內存不會釋放。python函或液畝數嵌套內存不會釋放。內存是計算機中重要的部衫森件之一,它=是與CPU進行溝通的橋梁。計算機中所有程序的運行都是在內埋型存中進行的,因此內存的性能對計算機的影響非常大。

熱點內容
內置存儲卡可以拆嗎 發布:2025-05-18 04:16:35 瀏覽:336
編譯原理課時設置 發布:2025-05-18 04:13:28 瀏覽:378
linux中進入ip地址伺服器 發布:2025-05-18 04:11:21 瀏覽:612
java用什麼軟體寫 發布:2025-05-18 03:56:19 瀏覽:32
linux配置vim編譯c 發布:2025-05-18 03:55:07 瀏覽:107
砸百鬼腳本 發布:2025-05-18 03:53:34 瀏覽:945
安卓手機如何拍視頻和蘋果一樣 發布:2025-05-18 03:40:47 瀏覽:742
為什麼安卓手機連不上蘋果7熱點 發布:2025-05-18 03:40:13 瀏覽:803
網卡訪問 發布:2025-05-18 03:35:04 瀏覽:511
接收和發送伺服器地址 發布:2025-05-18 03:33:48 瀏覽:372