python函數基本知識

一,函數的內部底層結構
函數的變量名(標識符)存儲在棧內存,其內放的位於堆內存中的函數信息的地址
所以說,有如下情況:

def fun():
    print('1')
fun()
c=fun
c()
print(fun)
print(id(fun))
print(c)
print(id(c))

結果爲:

1
1
<function fun at 0x00000150CC062F78>
1446531968888
<function fun at 0x00000150CC062F78>
1446531968888

可以看出,print(fun)這條命令打印的就是對應堆內存內的地址。id(fun)同樣對應的是堆內存中地址。變量沒有地址。

二,變量的作用域
1,全局變量:在函數和類定義之外聲明的變量,作用域爲模塊,它降低函數的通用性與可讀性,儘量避免使用。函數中要改變全局變量的值,使用golbal聲明。
2,局部變量:在函數體中聲明的變量,若與全局變量重名,優先使用局部變量。
代碼如下:

a=3
def text():
    b=4
    print(b)
    #print(a) #默認局部變量a,在下面會賦值a,所以有錯誤
    global a #在函數中改變全局變量
    a=300
    print(a)
    print(locals())#打印所有局部變量
    print(globals())#打印所有全局變量
text()
print(a)

結果如下:

4
300
{'b': 4}
{'__name__': '__main__', '__doc__': '\n@author: lhy\n@file: mypy09.py\n@time: 2020/02/29\n@desc:\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020F29FE5A48>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/PycharmProjects/MyTest/mypy09.py', '__cached__': None, 'a': 300, 'text': <function text at 0x0000020F2A1E2F78>}
300

3,變量底層結構
局部變量的地址存放在棧幀中,棧幀在調用函數時創建,在調用結束後自動銷燬,空間相對獨立,所以內部可以調用全局變量,但是在函數外調用不了局部變量。棧幀中保存的是函數調用本次的信息。(從堆地址的內容中取出來?)

4,局部變量與全局變量效率測試
代碼如下:

import time
import math
start=time.time()
def text():
    for i in range(10000000):
        math.sqrt(30)
text()
end=time.time()
print('耗時爲{0}'.format(end-start))



start=time.time()
def text1():
    b=math.sqrt
    for i in range(10000000):
        b(30)
text1()
end=time.time()
print('耗時爲{0}'.format(end-start))

結果如下:

耗時爲2.0674610137939453
耗時爲1.566443920135498

三,參數的傳遞
1,可變對象的傳遞:直接將a中id傳給棧幀中形式參數m,兩者指定同一地址,由於可變對象,所以直接修改。
2,不可變對象的傳遞:首先直接將a中id傳給棧幀中形式參數m,兩者指定同一地址,但是由於對象不可變,所以要改變m的值時,只能在堆中重新創建一個對象,並將新地址傳給m。
代碼如下:

a=[10,20]
print(id(a))
def text(m):
    print(id(m))
    m.append(30)
    print(id(m))
text(a)
print(a)

#不可變參數
a=100
print(id(a))
def text(m):
    print(id(m))
    m+=300
    print(id(m))
    print(m)
text(a)
print(a)

結果如下:

2395430277576
2395430277576
2395430277576
[10, 20, 30]
140725538500080
140725538500080
2395460236880
400
100

四,淺拷貝與深拷貝
淺拷貝:只拷引用,不拷內容。只不過指向同一地址。
深拷貝:全部拷貝(拷貝引用的引用)
代碼如下:

#淺拷貝
import copy
a=[10,20,[5,6]]
b=copy.copy(a)
print(a)
print(id(a))
print(b)
print(id(b))
b.append(30)
b[1]=2
b[2].append(7)
print(a)
print(b)
print(id(a[1]))
print(id(b[1]))
print()



#深拷貝
import copy
a=[10,20,[5,6]]
b=copy.deepcopy(a)
print(a)
print(id(a))
print(b)
print(id(b))
b.append(30)
b[2].append(7)
print(a)
print(b)
print()

結果如下:

[10, 20, [5, 6]]
2707173823368
[10, 20, [5, 6]]
2707174317896
[10, 20, [5, 6, 7]]
[10, 2, [5, 6, 7], 30]
140725538497520
140725538496944

[10, 20, [5, 6]]
2430962695432
[10, 20, [5, 6]]
2433061442504
[10, 20, [5, 6]]
[10, 20, [5, 6, 7], 30]

由代碼可以看出:深拷貝兩個對象完全不相關。在淺拷貝中,b[1]=2,這條代碼,意思是:本來a【1】,b[1],指向同一個對象,但是b修改了b【1】所以,a與b不同,但改變b【2】【1】就改變的是同一個目標。

注意以下程序:

a=(10,20,[5,6])
print(id(a))

def fun(m):
    print(id(m))
    #m[0]=30  #會報錯
    m[2][1]=300
    print(id(m))
fun(a)
print(a)

結果爲:

2707205833256
2707205833256
2707205833256
(10, 20, [5, 300])

在傳遞不可變對象時,如果不可變對象裏包含可變對象,則在函數內修改這個可變對像,原對象也會改變。

五,參數的類型
1,位置參數
2,默認參數(默認參數一定要在普通參數後)
3,命名參數
4,可變參數(一個*爲元組,兩個爲字典)
5,強制命名參數(帶
的變量後加普通變量要用)
代碼如下:

#位置參數與命名參數
def fun(a,b,c,d):
    print('{0}-{1}-{2}-{3}'.format(a,b,c,d))


fun(1,2,3,4)      #位置參數
fun(d=1,c=2,b=3,a=4)      #命名參數

#默認值參數
def fun1(a,b,c=20,d=30):
    print('{0}-{1}-{2}-{3}'.format(a,b,c,d))
fun1(1,2)
fun1(1,2,3)


#可變參數
def fun2(a,b,*c,**d):
    print('{0}-{1}-{2}-{3}'.format(a,b,c,d))


fun2(1,2,3,4,5,name='lhy',age=20)

結果如下:

1-2-3-4
4-3-2-1
1-2-20-30
1-2-3-30
1-2-(3, 4, 5)-{'name': 'lhy', 'age': 20}

六,lambda表達式聲明匿名函數
lambda 變量:表達式

f=lambda a,b:print('***')
print(f)
print(f(1,2))


def test(a,b):
    print('**')
    return a+b

g=[lambda a,b:a*b ,test]
print(g[0](5,6),g[1](1,2))

結果如下:

<function <lambda> at 0x00000206FB3D6048>
***
None
**
30 3

注意,()代表引用,列表存儲函數對象的用法

七,eval()函數
作用如下:

# eval
a='print(\'abcde\')'
eval(a)

d=10
e=20
f=eval('d+e')
print(f)

dict1=dict(d=100,e=200)
print(eval('d+e'))
print(eval('d+e',dict1))

結果如下:

abcde
30
30
300

八,遞歸函數
自己調用自己,應該包括終止條件與遞歸步驟

def fun1(n):
    print('第{0}次打印'.format(n))
    if n==0:
        print('over')
    else:
        fun1(n-1)
    print('*'*(n+1))
fun1(4)

結果:

4次打印
第3次打印
第2次打印
第1次打印
第0次打印
over
*
**
***
****
*****

這裏要注意,調用後的打印順序是從0-4

練習:使用遞歸算法計算階乘

#用遞歸實現階乘算法
def fun(n):
    if n==1:
        return 1
    else:
        return n*fun(n-1)

x=int(input("請輸入計算的數字:"))
result=fun(x)
print(result)

結果爲:

請輸入計算的數字:5
120
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章