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