天天看點

Python學習記錄——7.函數

1.函數前導

函數是組織好的,可重複使用的,用來實作單一,或相關聯功能的代碼段。

函數能提高應用的子產品性,和代碼的重複使用率。我們已經知道Python提供了許多内建函數,比如print()。但也可以自己建立函數,這被叫做使用者自定義函數。

2.函數定義

你可以定義一個由自己想要功能的函數,以下是簡單的規則:

  • 函數代碼塊以def關鍵詞開頭,後接函數辨別符名稱和圓括号()。
  • 任何傳入參數和自變量必須放在圓括号中間。圓括号之間可以用于定義參數。
  • 函數的第一行語句可以選擇性地使用文檔字元串—用于存放函數說明。
  • 函數内容以冒号起始,并且縮進。
  • Return[expression]結束函數,選擇性地傳回一個值給調用方。不帶表達式的return相當于傳回 None。

文法:

def functionname( parameters ):
   "函數_文檔字元串"
   function_suite
   return [expression]      

預設情況下,參數值和參數名稱是按函數聲明中定義的的順序比對起來的。

示例如下:

def printme( str ):
   "列印傳入的字元串到标準顯示裝置上"
   print str
   return      

3.函數調用

定義一個函數隻給了函數一個名稱,指定了函數裡包含的參數,和代碼塊結構。

這個函數的基本結構完成以後,你可以通過另一個函數調用執行,也可以直接從Python提示符執行。

示例如下:

def printme( str ):
   "列印任何傳入的字元串"
   print str;
   return;
 
# Now you can call printme function
printme("我要調用使用者自定義函數!");
printme("再次調用同一函數");      

輸出結果:

我要調用使用者自定義函數!
再次調用同一函數      

4.參數傳遞

在python中,類型屬于對象,變量是沒有類型的

a=[1,2,3]
 
a="w3cschool"      

以上代碼中,[1,2,3] 是 List 類型,"w3cschool"是String類型,而變量a是沒有類型,她僅僅是一個對象的引用(一個指針),可以是List類型對象,也可以指向 String類型對象。

可更改(mutable)與不可更改(immutable)對象

在python中,strings, tuples, 和numbers是不可更改的對象,而 list,dict 等則是可以修改的對象。

  • **不可變類型:**變量指派a=5後再指派a=10,這裡實際是新生成一個int值對象10,再讓a指向它,而5被丢棄,不是改變a的值,相當于新生成了a。
  • **可變類型:**變量指派la=[1,2,3,4] 後再指派la[2]=5則是将list la的第三個元素值更改,本身la沒有動,隻是其内部的一部分值被修改了。

python 函數的參數傳遞:

  • **不可變類型:**類似c++的值傳遞,如 整數、字元串、元組。如fun(a),傳遞的隻是a的值,沒有影響a對象本身。比如在 fun(a)内部修改 a 的值,隻是修改另一個複制的對象,不會影響 a 本身。
  • **可變類型:**類似c++的引用傳遞,如 清單,字典。如fun(la),則是将la真正的傳過去,修改後fun外部的la也會受影響

python中一切都是對象,嚴格意義我們不能說值傳遞還是引用傳遞,我們應該說傳不可變對象和傳可變對象。

python 傳不可變對象執行個體

執行個體(Python 2.0+)

def ChangeInt( a ):
    a = 10
 
b = 2
ChangeInt(b)
print b # 結果是 2      

執行個體中有int對象2,指向它的變量是b,在傳遞給ChangeInt函數時,按傳值的方式複制了變量b,a和b都指向了同一個 Int 對象,在a=10時,則新生成一個int值對象 10,并讓a指向它。

按值傳遞參數和按引用傳遞參數(傳可變對象執行個體)

所有參數(自變量)在Python裡都是按引用傳遞。如果你在函數裡修改了參數,那麼在調用這個函數的函數裡,原始的參數也被改變了。例如:

# 可寫函數說明
def changeme( mylist ):
   "修改傳入的清單"
   mylist.append([1,2,3,4]);
   print "函數内取值: ", mylist
   return
 
# 調用changeme函數
mylist = [10,20,30];
changeme( mylist );
print "函數外取值: ",      

傳入函數的和在末尾添加新内容的對象用的是同一個引用。故輸出結果如下:

函數内取值:  [10, 20, 30, [1, 2, 3, 4]]
函數外取值:  [10, 20, 30, [1, 2, 3, 4]]      

5.參數

以下是調用函數時可使用的正式參數類型:

  • 必備參數

必備參數須以正确的順序傳入函數。調用時的數量必須和聲明時的一樣。

調用printme()函數,你必須傳入一個參數,不然會出現文法錯誤,示例如下:

#可寫函數說明
def printme( str ):
   "列印任何傳入的字元串"
   print str;
   return;
 
#調用printme函數
printme();      

輸出結果:

Traceback (most recent call last):
  File "test.py", line 11, in <module>
    printme();
TypeError: printme() takes exactly 1 argument (0 given)      
  • 命名參數

命名參數和函數調用關系緊密,函數調用使用關鍵字參數來确定傳入的參數值。

使用命名參數允許函數調用時參數的順序與聲明時不一緻,因為 Python 解釋器能夠用參數名比對參數值。

示例如下:

#可寫函數說明
def printinfo( name, age ):
   "列印任何傳入的字元串"
   print "Name: ", name;
   print "Age ", age;
   return;
 
#調用printinfo函數
printinfo( age=50, name="miki" );      

輸出結果:

Name:  miki
Age  50      
  • 預設參數

調用函數時,預設參數的值如果沒有傳入,則被認為是預設值。

示例如下:

#可寫函數說明
def printinfo( name, age = 35 ):
   "列印任何傳入的字元串"
   print "Name: ", name;
   print "Age ", age;
   return;
 
#調用printinfo函數
printinfo( age=50, name="miki" );
printinfo( name="miki" );      

輸出結果:

Name:  miki
Age  50
Name:  miki
Age  35      
  • 不定長參數

如果需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數,和上述3種參數不同,聲明時不會命名。基本文法如下:

def functionname([formal_args,] *var_args_tuple ):
   "函數_文檔字元串"
   function_suite
   return [expression]      

加了星号(*)的變量名會存放所有未命名的變量參數。選擇不多傳參數也可。如下執行個體:

# 可寫函數說明
def printinfo( arg1, *vartuple ):
   "列印任何傳入的參數"
   print "輸出: "
   print arg1
   for var in vartuple:
      print var
   return;
 
# 調用printinfo 函數
printinfo( 10 );
printinfo( 70, 60, 50 );      

以上執行個體輸出結果:

輸出:
10
輸出:
70
60
50      

6.匿名函數

類似Java,python 使用 lambda 來建立匿名函數。

  • lambda隻是一個表達式,函數體比def簡單很多。
  • lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
  • lambda函數擁有自己的命名空間,且不能通路自有參數清單之外或全局命名空間裡的參數。
  • 雖然lambda函數看起來隻能寫一行,卻不等同于C或C++的内聯函數,後者的目的是調用小函數時不占用棧記憶體進而增加運作效率。

lambda函數的文法隻包含一個語句,如下:

lambda [arg1 [,arg2,.....argn]]:expression      

示例如下:

#可寫函數說明
sum = lambda arg1, arg2: arg1 + arg2;
 
#調用sum函數
print "Value of total : ", sum( 10, 20 )
print "Value of total : ", sum( 20, 20 )      

輸出結果:

Value of total :  30
Value of total :  40      

7.return語句

return語句[表達式]退出函數,選擇性地向調用方傳回一個表達式。不帶參數值的return語句傳回None。之前的例子都沒有示範如何傳回數值。

示例如下:

#coding=utf-8
#!/usr/bin/python
 
# 可寫函數說明
def sum( arg1, arg2 ):
   # 傳回2個參數的和."
   total = arg1 + arg2
   print "Inside the function : ", total
   return total;
 
# 調用sum函數
total = sum( 10, 20 );
print "Outside the function : ",      

以上執行個體輸出結果:

Inside the function :  30
Outside the function :  30      

8.變量作用域

一個程式的所有的變量并不是在哪個位置都可以通路的。通路權限決定于這個變量是在哪裡指派的。

變量的作用域決定了在哪一部分程式你可以通路哪個特定的變量名稱。兩種最基本的變量作用域如下:

  • 全局變量
  • 局部變量

定義在函數内部的變量擁有一個局部作用域,定義在函數外的擁有全局作用域。

局部變量隻能在其被聲明的函數内部通路,而全局變量可以在整個程式範圍内通路。調用函數時,所有在函數内聲明的變量名稱都将被加入到作用域中。

執行個體如下:

total = 0; # This is global variable.
# 可寫函數說明
def sum( arg1, arg2 ):
   #傳回2個參數的和."
   total = arg1 + arg2; # total在這裡是局部變量.
   print "Inside the function local total : ", total
   return total;
 
#調用sum函數
sum( 10, 20 );
print "Outside the function global total : ",      

以上執行個體輸出結果:

Inside the function local total :  30
Outside the function global total :  0      

9.函數式程式設計

函數式程式設計的特點

  • 把計算視為函數而非指令
  • 純函數式程式設計,不需要變量,沒有副作用,測試簡單
  • 支援高階函數,代碼簡潔

python支援的函數式程式設計

  • 不是純函數式編碼:允許有變量
  • 支援高階函數:函數也可以作為變量傳入
  • 支援閉包:有了閉包就能傳回函數
  • 有限度地支援匿名函數

10.高階函數

高階函數:隻能接收函數作為參數的函數

  • 變量可以是指向函數
  • 函數的參數可以接收變量
  • 一個函數可以接收另一個函數作為參數
  • 能接收函數作為參數的函數就是高階函數

下面介紹幾個常見的高階函數

  1. map()函數

map()是 Python 内置的高階函數,它接收一個函數 f 和一個 list,并通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 并傳回。

例如,對于list [1, 2, 3, 4, 5, 6, 7, 8, 9] ,如果希望把list的每個元素都作平方,就可以用map()函數。 隻需要傳入函數f(x)=x*x,就可以利用map()函數完成這個計算(**注意: python3中map()傳回iterators類型,不再是list類型。進行list轉換即可 **):

示例如下:

def f(x):
    return x*x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])      

輸出結果:

[1, 4, 9, 10, 25, 36, 49, 64, 81]      

注意:map()函數不改變原有的 list,而是傳回一個新的 list

  1. reduce()函數

reduce()函數也是Python内置的一個高階函數。reduce()函數接收的參數和 map()類似,一個函數 f,一個list,但行為和 map()不同,reduce()傳入的函數 f 必須接收兩個參數,reduce()對list的每個元素反複調用函數f,并傳回最終結果值。

例如,編寫一個f函數,接收x和y,傳回x和y的和:

def f(x, y):
    return x + y      

調用 **reduce(f, [1, 3, 5, 7, 9])**時,reduce函數将做如下計算:

先計算頭兩個元素:f(1, 3),結果為4;
再把結果和第3個元素計算:f(4, 5),結果為9;
再把結果和第4個元素計算:f(9, 7),結果為16;
再把結果和第5個元素計算:f(16, 9),結果為25;
由于沒有更多的元素了,計算結束,傳回結果25。      

上述計算實際上是對 list 的所有元素求和。雖然Python内置了求和函數sum(),但是,利用reduce()求和也很簡單。

**reduce()還可以接收第3個可選參數,作為計算的初始值。**如果把初始值設為100,計算:

reduce(f, [1, 3, 5, 7, 9], 100)      

結果将變為125,因為第一輪計算是:

計算初始值和第一個元素:f(100, 1),結果為101。

示例

Python内置了求和函數sum(),但沒有求積的函數,請利用recude()來求積:

輸入:[2, 4, 5, 7, 12]

輸出:245712的結果

1 from functools import reduce
2 def prod(x, y):
3     return x*y
4 
5 print(reduce(prod,[2,4,5,7,12]))      

注意:在python3.x後的版本中,reduce函數被劃歸到的functools庫,是以使用reduce函數之前必須先引入functools庫

  1. filter()函數

filter()函數是 Python 内置的另一個有用的高階函數,filter()函數接收一個函數 f 和一個list*,這個函數 f 的作用是對每個元素進行判斷,傳回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,傳回由符合條件元素組成的新list。

例如,要從一個list [1, 4, 6, 7, 9, 12, 17]中删除偶數,保留奇數,首先,要編寫一個判斷奇數的函數:

def is_odd(x):
    return x % 2 == 1      

然後,利用filter()過濾掉偶數:

filter(is_odd, [1, 4, 6, 7, 9, 12, 17])      

結果:[1, 7, 9, 17]

利用filter(),可以完成很多有用的功能,例如,删除 None 或者空字元串:

def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])      

結果:[‘test’, ‘str’, ‘END’]

注意: s.strip(rm) 删除 s 字元串中開頭、結尾處的 rm 序列的字元。

當rm為空時,預設删除空白符(包括’\n’, ‘\r’, ‘\t’, ’ '),如下:

a = '     123'
a.strip()      

結果: ‘123’

a='\t\t123\r\n'
a.strip()      

結果:‘123’

示例

請利用filter()過濾出1~100中平方根是整數的數,即結果應該是:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

代碼

import math
def is_sqr(x):
    a = math.sqrt(x)%1
    if a ==0.0:
        return True
    else:
        return False

l = filter(is_sqr, range(1,101))
print(list(l))      
  1. sort()函數

Python内置的 sorted()函數可對list進行排序:

>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]      

但 sorted()也是一個高階函數,它可以接收一個比較函數來實作自定義排序,比較函數的定義是,傳入兩個待比較的元素 x, y,如果 x 應該排在 y 的前面,傳回 -1,如果 x 應該排在 y 的後面,傳回 1。如果 x 和 y 相等,傳回 0。

是以,如果我們要實作倒序排序,隻需要編寫一個reversed_cmp函數:

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0      

這樣,調用 sorted() 并傳入 reversed_cmp 就可以實作倒序排序:

>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]      

sorted()也可以對字元串進行排序,字元串預設按照ASCII大小來比較:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']      

'Zoo’排在’about’之前是因為’Z’的ASCII碼比’a’小。

示例

對字元串排序時,有時候忽略大小寫排序更符合習慣。請利用sorted()高階函數,實作忽略大小寫排序的算法。

輸入:[‘bob’, ‘about’, ‘Zoo’, ‘Credit’]

輸出:[‘about’, ‘bob’, ‘Credit’, ‘Zoo’]

[

Python學習記錄——7.函數

](javascript:void(0)?

#-*- coding=utf8 -*- 
#這段代碼在Python2.7可以運作,python3.x之後sorted函數不支援自定義比較函數,在python3.x中sorted函數的定義為
#sorted(iterable, /, *, key=None, reverse=False)
#可以通過将reverse參數設定為True來實作上面的功能

def cmp_ignore_case(s1, s2):
    if s1.lower()> s2.lower():
        return 1
    elif s1.lower()< s2.lower():
        return -1
    else:
        return 0

print sorted(['bob','about','Zoo','Credit'], cmp_ignore_case)      
  1. 偏函數

當一個函數有很多參數時,調用者就需要提供多個參數。如果減少參數個數,就可以簡化調用者的負擔。

比如,int()函數可以把字元串轉換為整數,當僅傳入字元串時,int()函數預設按十進制轉換:

>>> int('12345')
12345      

但int()函數還提供額外的base參數,預設值為10。如果傳入base參數,就可以做 N 進制的轉換:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565      

假設要轉換大量的二進制字元串,每次都傳入int(x, base=2)非常麻煩,于是,我們想到,可以定義一個int2()的函數,預設把base=2傳進去:

def int2(x, base=2):
    return int(x, base)      

這樣,我們轉換二進制就非常友善了:

>>> int2('1000000')
64
>>> int2('1010101')
85      
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85