我的Python學習之路04-附練習題

函數定義,函數結構,位置參數與關鍵字參數,函數返回值

函數定義,與函數結構

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,其中體重單位爲公斤,身高單位爲米。

  • 計算公式爲:BMI=kg÷2mBMI=體重(kg)÷身高^2(m)

  • 提示用戶輸入體重(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,健康爲標準
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章