函數定義,函數結構,位置參數與關鍵字參數,函數返回值
函數定義,與函數結構
l1=[1,'s','china']
s1={'f',56}
d1={'name':'jack'}
len(s1)
#這個的作用就是讓全部結果都顯示出來。
%config ZMQInteractiveShell.ast_node_interactivity='all'
#函數定義與函數結構
def myfunc1(parm1,parm2='amazing'):
"\
here is document for this function\
multi lines \
"#__doc__ here
something=parm1
return str(something)+' is '+parm2
myfunc1
myfunc1('china')
輸出結果爲:
<function __main__.myfunc1(parm1, parm2='amazing')>
'china is amazing'
%whos
輸出結果爲:
Variable Type Data/Info
--------------------------------
d1 dict n=1
l1 list n=3
myfunc1 function <function myfunc1 at 0x0000026ABDFC8A60>
s1 set {'f', 56}
延伸閱讀:Python3新增函數特性:註釋增強/Annotations
#Python 3.X新增加了一個特性(Feature),叫作函數註釋 Function Annotations
#它的用途雖然不是語法級別的硬性要求,但是顧名思義,它可做爲函數額外的註釋來用
#Python中普通的函數定義如下:
def myfunc(parm1,parm2):
something=parm1
return str(something)+' is '+parm2
#添加了函數註釋的函數會變成如下形式:
def myfunc1(parm1:'parm user given string to output',parm2:'this parm default value is amazing, user still can replace it'='amazing')-> 'function return should be a string':
"#__doc__ here"
something=parm1
return str(something)+' is '+parm2
myfunc1.__annotations__
#註釋的一般規則是參數名後跟一個冒號,然後再跟一個expression,這個expression可以是任何形式。
#返回值的形式是用 “->” 描述
輸出結果爲:
{'parm1': 'parm user given string to output',
'parm2': 'this parm default value is amazing, user still can replace it',
'return': 'function return should be a string'}
延伸閱讀:
def:運行期指令,以代碼對象爲參數創建函數實例,並在當前上下文中與指定的名字相關聯
#def是運行期指令
import dis;
dis.dis(compile("def test():pass","","exec"))
#def的僞碼
#test=make_function('test',code)#dis操作是在函數實例創建完成後針對__code__,而非針對創建過程
#以單個代碼對象爲模板創建多個函數實例
def make_func(n):
ret=[]
for i in range(n):
def test():print('hello',n)
print(id(test),id(test.__code__))
ret.append(test)
return ret
make_func(4)#不同實例,相同代碼,也可以不同實例,不同代碼(屬性動態添加)
輸出結果爲:
4432203856 4432205264
4432206160 4432205264
4432384800 4432205264
4432385232 4432205264
[<function __main__.make_func.<locals>.test()>,
<function __main__.make_func.<locals>.test()>,
<function __main__.make_func.<locals>.test()>,
<function __main__.make_func.<locals>.test()>]
延伸閱讀:Python的函數對象PyFunctionObject:
作用:爲上下文提供調用實例,並管理所需的狀態數據。負責管理運行期狀態,如默認參數,動態添加的屬性,由MAKE_FUNCTION指令創建
#探索PyFunctionOjbect
def test1(a,b,c=1):
str1='weijichen.cn'
print(c)
return b
'co_name',test1.__code__.co_name#函數名
'co_object',test1.__code__# PyCodeObject<code object test1 at 0x0000023812E75A50, file "<ipython-input-14-1306b5bc9b20>", line 1>
'co_code',test1.__code__.co_code#函數中的代碼對象字節碼(二進制存儲)
'co_lnotab',test1.__code__.co_lnotab#'字節碼的偏移值與對應的源碼的行號的相對值'
'co_filename',test1.__code__.co_filename#函數所在文件名<ipython-input-16-fc6b304ba0cd>
'co_firstlineno',test1.__code__.co_firstlineno#函數在文件中的首行的行數1
'co_stacksize',test1.__code__.co_stacksize#佔用了幾個棧楨
擴展閱讀:棧(Stack)與棧幀(Stack Frame)
Python中棧的分類:
用戶棧:函數被調用時,會專門爲其分配用戶棧內存,用戶棧內存除用來存儲變量外,還包括字節碼參數和返回值所需空間。
系統棧:用於機器執行,用戶棧用於代碼執行狀態。
Python中棧的作用:
棧用於指令執行,與線程綁定。函數調用與執行都依賴於線程棧上存儲的上下文和執行狀態。
import dis
def test(a,b):
c=a+b
return c
dis.dis(test)
2 0 LOAD_FAST 0 (a) #從Fast讀取參數a,壓入用戶棧
2 LOAD_FAST 1 (b)#從Fast讀取參數b,壓入用戶棧
4 BINARY_ADD #系統指令從用戶棧讀取操作數,執行加法操作
6 STORE_FAST 2 (c)#結果寫回Fast
3 8 LOAD_FAST 2 (c)
10 RETURN_VALUE
棧幀(Stack Frame)
線程棧這塊內存中,每個被調用函數都分配着一塊區域
Call stack是函數調用堆棧(運行時函數的調用過程),除返回地址,還有爲函數提供參數,局部變量存儲空間等
調用函數時的內存變化:
函數每次調用,都會新建棧幀,用於局部變量和執行過程存儲。執行完成後,棧幀內存被回收,同時釋放相關對象。
def test():
return id(locals())
print(test())#Create New StackFrame
print(test())#Create Another New StackFrame
輸出結果爲:
2439859900488
2439859902432
查看棧幀對象PyFrameObject
PyFrameObject,棧幀表示程序運行時函數調用棧中的某一幀。函數沒有屬性可以獲取它,因爲它在函數調用時纔會產生
想要獲得某個函數相關的棧幀,則必須在調用這個函數且這個函數尚未返回時獲取。
sys._getframe()方法可以獲取當前棧幀
_back: 調用棧的前一幀
f_code: 棧幀對應的code對象
f_locals: 用在當前棧幀時與內建函數locals()相同,但你可以先獲取其他幀然後使用這個屬性獲取那個幀的locals()
f_globals: 用在當前棧幀時與內建函數globals()相同,但你可以先獲取其他幀……
f_builtins -> dict key: python的內置函數名
import sys
def func():
frame = sys._getframe()
print(frame.f_locals)
print(frame.f_globals)
print(frame.f_back.f_locals)
#你可以打印frame的各個域
print(s)
func()
import sys
value = 3
def A():
frame = sys._getframe()
print ('current function is : ', frame.f_code.co_name)
caller = frame.f_back
print ('caller function is : ', caller.f_code.co_name)
print ("caller's local namespace : ", caller.f_locals)
print ("caller's global namespace : ",caller.f_globals.keys())
def B():
a = 1
b = 2
print('------')
A()
print('------')
B()
cf=sys._current_frames()
import sys,dis
def A():
x='func A'
B()
def B():
C()
def C():
c_var='test'
f=sys._getframe(0)#向上2級,獲取A棧幀,1獲取B,0是當前
print('frame及遍歷---------------------------------------------------------------')
print(f.f_code.co_name,'棧幀類型和對象:',type(f),f)
# print('遍歷:',dir(f))
print('名字空間---------------------------------------------------------------')
# print('函數所在模塊的的名字空間f_globalsa或直接globals():',f.f_globals)#返回函數所在模塊的的名字空間
# print('frame f_builtins:',f.f_builtins)#A人
print(f.f_code.co_name,'名字空間f_locals(運行期):',f.f_locals)#A名字空間(運行期)
print('代碼對象---------------------------------------------------------------')
print(f.f_code.co_name,'代碼對象f_code:',f.f_code)#A代碼對象
print(f.f_code.co_name,'代碼對象f_fileno:',f.f_lineno)#所在文件行數
print(f.f_code.co_name,'最後執行指令的偏移量f_lasti:',f.f_lasti)#A最後執行指令的偏移量
print('棧幀對象---------------------------------------------------------------')
print(f.f_code.co_name,' frame f_back:',f.f_back)#前一幀對象
print(f.f_code.co_name,' frame f_trace:',f.f_trace)#trace對象
print('f_code,---------------------------------------------------------------')
print('該f_code即爲對應函數的代碼對象-------------------------')
# print('遍歷f_code:',dir(f.f_code))
print('co_stacksize',f.f_code.co_stacksize)
A()
返回所有棧幀
sys._current_frames()#返回一個dict對象
import sys
sys._current_frames()#返回一個dict
輸出結果爲:
{6792: <frame at 0x26abc39a278>,
7640: <frame at 0x26abaaa24e8>,
9380: <frame at 0x26abad67938>,
10816: <frame at 0x26abcf05cf8>,
13344: <frame at 0x26abada6008>}
函數的參數:位置參數(定位參數) 與 關鍵字參數
位置參數(定位參數)
def myadd2(a,b):
return a+b
def myadd3(a,b,c):
return a+b+c
def myadd4(a,b,c,d):
print(d)
擴展閱讀: Python可選位置參數
Python內置電池round函數的幫助中,出現了[, ndigits]這個東東。
其它的一些Python對象提供的方法中也有類似的東東出沒,比如對序列對象進行切片訪問時。
就是Python可選位置參數。
注意:這樣的參數是無法通過自定義函數來創建的。只有Python中的內置函數可以。
Help on built-in function round in module builtins:
round(...)
round(number[, ndigits]) -> number
Round a number to a given precision in decimal digits (default 0 digits).
This returns an int when called with one argument, otherwise the
same type as the number. ndigits may be negative.
關鍵字參數
#關鍵字參數,與不定長關鍵字參數
def myadd0(args,lastnum=1000):
return args+lastnum
myadd0(7)
def myadd(parm1,parm2=500):
print(parm1+parm2)
def myadd(parm1,parm2=500,parm3=5):
print(parm1+parm2)
myadd?
擴展閱讀:查看函數簽名
from inspect import signature# 注意是小寫的signature
def func(parm1,parm2='kw'):
return parm1
def func(parm1):
pass
# 獲取函數簽名
func_sig = signature(func)
# 通過函數簽名的parameters屬性,可以獲取函數參數
func_params = func_sig.parameters
func_params
#或者直接使用ipython下的?
輸出結果爲:
mappingproxy({'parm1': <Parameter "parm1">})
擴展閱讀:函數參數在__code__
對象下的反映
def test1(a,b,c=1):
str1='julyedu.com'
return b
'co_argcount',test1.__code__.co_argcount#位置參數的個數,而co_nlocals是局部變量數目,包括位置參數在內。
'co_nlocals&co_varnames',test1.__code__.co_nlocals,test1.__code__.co_varnames#多少個局部變量,以及及變量的名字。
'符號名集合co_names',test1.__code__.co_names #符號名集合
'常量集合co_const',test1.__code__.co_consts #常量集合
# 位置參數,鍵(關鍵字)參數,*args擴展位置參數[元組對象]和**kwargs擴展鍵(關鍵字)參數[字典]。
# 位置參數還能設置默認值,如果有默認值,默認值是在MAKE_FUNCTION指令賦值給`funcname.__defaults__`的。
'co_flags',test1.__code__.co_flags
# 【1】function沒有args或*kw時,`funcname.__code__.co_flags=67`;
# 【2】function有args沒有*kw時,funcname.__code__.co_flags=71;
# 【3】function沒有args有*kw時,funcname.__code__.co_flags=75;
# 【4】function既有args也有*kw時,funcname.__code__.co_flags=79;
# 【5】function是一個generator時,funcname.__code__.co_flags=99.
#對於像 def f(a, b, *lst):這樣的函數,如果調用函數時參數爲f(1,2,3,4),其實在PyCodeObject對象中的co_argcount=2, co_nlocals=3。
輸出結果爲:
('co_argcount', 3)
('co_nlocals&co_varnames', 4, ('a', 'b', 'c', 'str1'))
('符號名集合co_names', ())
('常量集合co_const', (None, 'julyedu.com'))
('co_flags', 67)
擴展閱讀:動態管理函數對象的參數的默認值與屬性
##函數對象參數的默認值存放在函數對象的,__defaults__屬性中,是一個Tuple類對象
#字節數換算爲KB,MB,GB,TB
def CaluSize(bytesize,trans='KB'):
if trans=='KB':size=1024
if trans=='MB':size=1024**2
if trans=='GB':size=1024**3
if trans=='TB':size=1024**4
return str(bytesize//size)+trans
CaluSize.__defaults__
CaluSize.__defaults__=('TB',)
CaluSize.__defaults__
CaluSize(110241550654651654544)
輸出結果爲:
('KB',)
('TB',)
'100264106TB'
##函數對象屬性
##函數對象屬性值存放在函數對象的,__dict__屬性中,是一個Dict類對象
CaluSize
CaluSize.__dict__
CaluSize.__dict__['new_atrr']='test'
CaluSize.__dict__
輸出結果爲:
<function __main__.CaluSize(bytesize, trans='TB')>
{'new_atrr': 'test'}
函數返回值
#函數返回值
#有返回值 與無返回值(即使沒有return語句,依舊會return一個NoneType類的對象None)
def myadd(a):
return a,5
def myadd1(a,b,c,d):
print(a+b+c+d)
def myadd2(a,b):
pass
不定長位置參數與不定長關鍵字參數
不定長位置參數
def myadd1(a,b,c,d,*parms):
print(type(parms))
print('parms:',parms)
print('a,b,c,d:',a,b,c,d)
return sum(parms)
myadd1(1,2,3,4,5,6)
def myadd2(arg,*args):
print(type(args))
print(args)
return sum(args)
myadd2(18,1,2,3)
def myadd3(arg,*args):
print(type(args))
print(args)
return sum(args)
myadd3(18,1,2,3)
輸出結果爲:
<class 'tuple'>
parms: (5, 6)
a,b,c,d: 1 2 3 4
11
<class 'tuple'>
(1, 2, 3)
6
<class 'tuple'>
(1, 2, 3)
6
不定長關鍵字參數
def myadd(**kwargs):
print(kwargs)
myadd(add='sh',tel=13333,name='jack',height=178)
myadd(address='sh',cellphone=13333,)
def myaddkw(**names):
print(type(names))
print(list(names.values()))
return list(names.values())
myaddkw(name='david',gender='male',age=0)
myadd0(args=5, lastnum=6)
綜合使用
l1=[1,2,3,4,5,6]
a,b,c,d,e,f=l1
f
l1=[1,2,3,4,5,6]
a,b,*c,d,e=l1
a,b,c,d,e
def myfun1(*args,**kwargs):
print(args)
for item in kwargs:
print(item)
d1={'name':'david','add':'bj','date':'2018-8-1'}
l1=[1,2,3,4,5,6,7]
myfun1(*l1,**d1)
輸出結果爲:
(1, 2, 3, 4, 5, 6, 7)
name
add
date
擴展閱讀:Python函數參數中的/
與*
sorted?
l1=['China','Japan','UK','Beijing']
#Signature: sorted(iterable, /, *, key=None, reverse=False)
#參數/是指,iterable這個參數只能以位置參數形式給,不能以關鍵字參數形式給,否則報錯
#這類參數只能在C API中被指定,def定義會報錯
#正常
sorted(l1)
#報錯
#sorted(iterable=l1)#TypeError: Function takes at least 1 positional arguments (0 given)
#參數*是指,key這個參數只能以關鍵字參數形式給,不能以位置參數形式給,以否則報錯
#這類參數可以自行定義
#正常
sorted(l1,key=lambda x:(x[-2]))
#報錯
# sorted(l1,lambda x:(x[-1]))#TypeError: must use keyword argument for key function
輸出結果爲:
['Beijing', 'China', 'Japan', 'UK']
['UK', 'Japan', 'China', 'Beijing']
偏函數
#偏函數,固定了部分參數的函數
import functools
a=0x24
a
int(str(a),base=32)
hex2int=functools.partial(int,base=16)
type(hex2int)
hex2int(str(a))
def mysal(nums,rate=6.95):
return nums*rate
mysal(5000)
mysal(12000)
import functools
rmb2eur=functools.partial(mysal,rate=1.01)
rmb2jpn=functools.partial(mysal,rate=82)
rmb2jpn(5000)
Python中的LEGB訪問規則
def a():
f=15
print(locals())
a()
f
擴展閱讀:Eval與Exec這兩個函數在名字空間
# exec?
# Signature: exec(source, globals=None, locals=None, /)
# Docstring:
# Execute the given source in the context of globals and locals.
#要exec的python源碼
ns={}#傳入的容器是空的
exec('def inside_func():pass',ns)
ns['inside_func']#返回的窗器是帶有結果的,也就是說傳入名字空間被改變了
s='''
def test():
print(hex(id(locals())),__name__)
#exec的代碼是以builtins模塊下進行運行的
test()
'''
g={'g':666}#傳給s的全局名字空間
print('入口程序模塊:',hex(id(ns)),__name__)
exec(s,g)
%whos
輸出結果爲:
入口程序模塊: 0x23813168ea0 __main__
0x238130b84c8 builtins
Variable Type Data/Info
----------------------------
g dict n=3
l dict n=2
ns dict n=2
s str \ndef test():\n print(<...>ltins模塊下進行運行的\n\ntest()\n
#eavl,顯示傳入容器對象做爲動態代碼的專用名字空間
# Signature: eval(source, globals=None, locals=None, /)
# Docstring:
# Evaluate the given source in the context of globals and locals.
g={"x":100}
l={"y":101}
eval("x+y",g,l)
輸出結果爲:
201
練習題:
實現能輸出指定長度fib數列函數
def fib(n):
a,b=0,1
for i in range(n):
a,b=b,a+b
return a
fib(3)
定義一個函數,接受任意三個數字的輸入,並按順序從小到大輸出
def sdd(a,b,c):
arr=[a,b,c]
arr.sort()# -- 排序規則,reverse = True 降序, reverse = False 升序(默認)
#arr.sort(reverse=True)
return arr
sdd(2,3,1)
自己實現一個支持可變參數的函數
def calc(*num):
return num
calc(1,2,3,4)
要求創建一個函數,它可以接收,位置參數,不定長位置參數,不定長關鍵詞參數,並按要求輸出 。
- 輸入班級名,班級特色(如’勤奮’,’顏值高’ )等等不同特色,班級不同同學的姓名與年齡。
- 要求輸出,班級名,班級特色,班級成員,班級成員的平均年齡。
def funcd(classname,*classfeatures,**classmates):
print("班級名:%s"%classname)
print("班級特色:%s"%str(classfeatures))
totalage=0
myclassmates=[]
for (key,value) in classmates.items():
totalage=totalage+value
myclassmates.append(key)
print("班級成員:%s"%str(myclassmates))
print("平均年齡:%s"%str(totalage/len(classmates)))
classmates={"zhangsan":15,"lisi":20}
classfeatures=["勤奮","顏值高"]
funcd("高三1班",*classfeatures,**classmates)
班級名:高三1班
班級特色:('勤奮', '顏值高')
班級成員:['zhangsan', 'lisi']
平均年齡:17.5
創建能根據輸入計算BMI指數的函數
身體質量指數(BMI)是根據人的體重和身高計算得出的一個數字,BMI是可靠的身體肥胖指標,其計算公式:BMI=Weight/High2,其中體重單位爲公斤,身高單位爲米。
-
計算公式爲:
-
提示用戶輸入體重(kg)和身高的數字(m)(注意單位),然後計算BMI。
-
根據BMI指數範圍,定義當前健康狀態。BMI指數在18至25之間定義爲健康的標準體重,小於該範圍定義爲偏瘦,超過該範圍定義爲偏重。
-
將BMI指數和其所代表狀態輸出
def bmicheck(height,weight):
bmi=weight/pow(height,2)
if bmi < 18:
print("BMI指數是:"+str(bmi)+",健康爲偏廋")
elif bmi < 25:
print("BMI指數是:"+str(bmi)+",健康爲標準")
else:
print("BMI指數是:"+str(bmi)+",健康爲偏重")
bmicheck(1.80,60)
BMI指數是:18.51851851851852,健康爲標準