python的爬蟲都有什么內置函數?
函數
一.引言
a) 概念:函數是完成指定或者特定任務的一組代碼。在面向對象編程的類中函數通常被稱為方法。不同的函數在程序中會扮演不同的角色完成不同的功能。
b) 作用:
i. 函數的使用可以提高代碼的復用性,省去重復代碼的編寫,提升程序代碼重復利用率。
ii. 函數能封裝內部的實現,保護內部的數據。通常,我們可以將函數看做成一個“黑盒子”。往往函數的使用者并不是函數的編寫者,函數的使用者并不需要對函數內部實現和行為進行考慮,可以將精力更多投入到自身業務邏輯的設計中。只有函數的編寫者才需要考慮函數內部的實現細節,如何暴露對外的接口,返回什么樣的數據值,也就是API的設計。
iii. 提高程序的可讀性。使得程序模塊化。從一盤散散沙變成整齊隊列。
iv. 提高程序的可維護性。案例:打印一個不同字符組成的分隔符。
二.函數基礎
a) 可以將函數抽象成現實生活中的工具。工具需要先制作出來才可以使用。函數也是一樣的需要先進行函數的定義,然后方可進行函數調用。
b) 函數定義語法結構:
def 函數名(參數):
# 內部代碼
return 表達式
c) Return:return不是必須要寫的,如果需要則寫,不需要可不寫。Return后面什么都不跟表示return None。一旦函數執行過程中遇到了return語句,那么函數中return后面的所有語句將不會執行,函數執行結束。
d) 函數調用:
i. 語法結構:函數名(參數值)
ii. 注意事項:
1. 參數之前使用逗號隔開
2. 由于python動態語言的特點,函數調用時填寫的參數,python不會對參數類型進行檢查,如果函數調用中的參數不符合函數內部運行機制的話,會報錯。
e) 參數:現實函數和調用者之間的交互
i. 函數-》工具:使用ATM取錢時需要傳入密碼數據。
ii. 實參和形參的概念。
iii. 函數調用時,實參會傳值給形參
1. 注意:
a) Python的函數參數傳遞實際上傳遞的是實參的地址
b) Python中的參數類型分為可變數據類型和不可變數據類型。
2. Test:使用可變數據類型和不可變數據類型的數據作為參數
a = 1
def func(a):
print("在函數內部修改之前,變量a的內存地址為: %s" % id(a))
a = 2
print("在函數內部修改之后,變量a的內存地址為: %s" % id(a))
print("函數內部的a為: %s" % a)
print("調用函數之前,變量a的內存地址為: %s" % id(a))
func(a)
print("函數外部的a為:%s" % a)
打印結果為:
調用函數之前,變量a的內存地址為: 1401140288
在函數內部修改之前,變量a的內存地址為: 1401140288
在函數內部修改之后,變量a的內存地址為: 1401140320
函數內部的a為: 2
函數外部的a為:1
解釋:作為參數,a被傳入函數時,將數字對象1的地址傳遞給了函數內部的a。執行第一句內部代碼時,此時內部的a和外面的a其實是一個東西,因此打印出了同樣的內存地址。而當a=2被執行后,由于整數是不可變的數據類型,所以創建了一個新的內部變量a,并賦值2,將數字對象2的內存地址賦給變量a。我們知道,首先,賦值語句具有創建新變量的功能。
剛才說的是不可變類型參數,如果是可變類型的,比如列表呢?
a = [1, 2, 3]
def func(b):
print("在函數內部修改之前,變量b的內存地址為: %s" % id(b))
b.append(4)
print("在函數內部修改之后,變量b的內存地址為: %s" % id(b))
print("函數內部的b為: %s" % b)
print("調用函數之前,變量a的內存地址為: %s" % id(a))
func(a)
print("函數外部的a為:%s" % a)
執行結果是:
調用函數之前,變量a的內存地址為: 34875720
在函數內部修改之前,變量b的內存地址為: 34875720
在函數內部修改之后,變量b的內存地址為: 34875720
函數內部的b為: [1, 2, 3, 4]
函數外部的a為:[1, 2, 3, 4]
三.參數類型:python函數的參數定義靈活度很大,可以定義位置參數,默認參數,動態參數,關鍵字參數等。
a) 位置參數
i. 概念:也叫做必傳參數,在函數調用時必須明確提供的參數,切參數順序個數必須和形參保持一致。但是如果在函數調用的時候給實參指定參數名,那么位置參數的順序可以不同。
b) 默認參數:如果給某個參數提供一個默認值,該參數就是默認參數。在函數調用時,可以給默認參數傳遞一個值,也可以使用默認值。
i. Test:
def power(x, n = 2):
return x**n
ret1 = power(10) # 使用默認的參數值n=2
ret2 = power(10, 4) # 將4傳給n,實際計算10**4的值
ii. 使用默認參數的注意事項:
1. 默認參數必須在順序參數后面
2. 默認參數盡量指向不變的對象
a) Test:
下面是國內某上市互聯網公司Python面試真題:
def func(a=[]):
a.append("A")
return a
print(func())
print(func())
print(func())
不要上機測試,僅憑代碼,你能說出打印的結果嗎?
很多同學可能會說,這還不簡單,肯定是下面的結果啊:
['A']
['A']
['A']
真的是這樣嗎?錯了!真正的結果是:
['A']
['A', 'A']
['A', 'A', 'A']
Why?為什么會這樣?
因為Python函數體在被讀入內存的時候,默認參數a指向的空列表對象就會被創建,并放在內存里了。因為默認參數a本身也是一個變量,保存了指向對象[]的地址。每次調用該函數,往a指向的列表里添加一個A。a沒有變,始終保存的是指向列表的地址,變的是列表內的數據!我們可以測試一下:
def func(a=[]):
print("函數內部a的地址為:%s" % id(a))
a.append("A")
return a
b = func()
print('此時b的值為:%s' % b)
print("函數外部b的地址為:%s" % id(b))
print("-------------")
c = func()
print('此時c的值為:%s' % c)
print("函數外部c的地址為:%s" % id(c))
print("-------------")
d = func()
print('此時d的值為:%s' % d)
print("函數外部d的地址為:%s" % id(d))
打印結果是:
函數內部a的地址為:39287880
此時b的值為:['A']
函數外部b的地址為:39287880
-------------
函數內部a的地址為:39287880
此時c的值為:['A', 'A']
函數外部c的地址為:39287880
-------------
函數內部a的地址為:39287880
此時d的值為:['A', 'A', 'A']
函數外部d的地址為:39287880
那么如何避免這個問題呢?
使用不可變的數據類型作為默認值!
def func(a=None):
# 注意下面的if語句
if a is None:
a = []
a.append("A")
return a
print(func())
print(func())
print(func())
將默認參數a設置為一個類似None,數字或字符串之類的不可變對象。在函數內部,將它轉換為可變的類型,比如空列表。這樣一來,不管調用多少次,運行結果都是['A']了。
iii. 動態參數
1. 概念:函數調用時傳入的參數可以是動態數量的,可以為任意個,甚至是0個。
2. 分類:重點是*的個數,*后的名字任意。動態參數必須放在所有位置參數和動態參數后面。
a) *args:可以接受任意個參數。調用時,會將實參打包成一個元組傳給形參。如果參數是一個列表,則會將整個列表當做一個參數傳入。
i. Test
def func(*args):
for arg in args:
print(arg)
func('a', 'b', 'c')
li = [1, 2, 3]
func(li)
ii. 上述案例中,我們本意是將li這個列表中的元素作為參數進行傳值,但是實際卻是將列表本身作為一個整體進行了傳遞。如果想要將一個序列類型的對象(元組,列表,字符串,字典)的元素依次作為參數進行傳遞,則可以在序列對象前加一個*即可。如果參數是字典,則會將字典所有的key逐一傳遞進去。
1. Test:
def func(*args):
for arg in args:
print(arg)
li = [1, 2, 3]
func(*li)
b) **kwargs:表示接受鍵值對的動態參數,數量任意。調用時,會將參數打包成一個字典。
i. Test:
def func(**kwargs):
for kwg in kwargs:
print(kwg, kwargs[kwg])
print(type(kwg))
func(k1='v1', k2=[0, 1, 2])
運行結果是:
k1 v1
k2 [0, 1, 2]
ii. 而如果我們這樣傳遞一個字典dic呢?我們希望字典內的鍵值對能夠像上面一樣被逐一傳入。
1. Test:
def func(**kwargs):
for kwg in kwargs:
print(kwg, kwargs[kwg])
dic = {
'k1': 'v1',
'k2': 'v2'
}
func(**dic)
iv. 關鍵字參數:關鍵字參數前面需要一個特殊分隔符*和位置參數及默認參數分隔開來。*后面的參數被視為關鍵字參數。在函數調用時,關鍵字參數必須傳入參數名。
1. Test:
def student(name, age, *, sex):
pass
student(name="jack", age=18, sex='male')
Test2:如果函數定義中已經有了一個*args參數,后面跟著的命名關鍵字參數就不再需要一個特殊分隔符*了。
def student(name, age=10, *args, sex, classroom, **kwargs):
pass
student(name="jack", age=18, sex='male', classroom="202", k1="v1")
總結:參數定義順序:位置參數,動態參數,關鍵字參數,動態參數