Python學習基礎之函數

  1. 當出現函數的時候世界的曙光出現了,重複的事情變得簡單
    python提供了很多內置的函數供開發者使用,方便了管理,對於API的使用,可以使用help(命令)形同於linux下的man手冊
    def Whichbig(a,b,c):
        x=(max(a,b,c))
        return x

投機取巧打個包裝,當然做一個比較還是很簡單的沒事試試吧
對於一個空函數

def MyNull():
    pass

意味着什麼都不做,在以後的大型編程中可能是意味着在這裏留一個接口,供後續使用

    import math

是一個導包的過程形同於C的 - include又有所不同,真要說像的話是和java的packge是一樣的 道理
返回多個值,當然在外面你的類型要是一樣,要可以接受這些返回值例如元組,列表。這在爬蟲中經常使用

    def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny
  • 定義函數時,需要確定函數名和參數個數;如果有必要,可以先對參數的數據類型做檢查;函數體內部以用return隨時返回函數結果;函數執行完畢也沒有return語句時,自動return None。
  • 函數可以同時返回多個值,但其實就是一個tuple關於函數的參數,有這麼幾點注意,函數可以是多個參數,從無到無窮不等,函數也可以是默認參數,要注意的是默認參數,必須是不可變參數,也就是我們說的常量,變量有太多可變性,具有不可預知性。
def add_list(L=[]):
    L.append('ONE')
    return L

這是有錯的,會出現不可預知的後果,因爲不能總是得到你想要看到的結果,原因就是因爲參數是不可預知的
- 對於可變參數的問題,有這麼幾種解決方法,一個是給函數中傳遞list或者tuple對象,一種是函數參數對象添加*號,例如*nums,這樣的感覺就是C中的指針但是在python中是沒有指針的
這樣就解決了可變長參數的問題

def GetNums(*nums):
    print (nums[0])
GetNums(0)
  • 解釋器會自動將參數合成爲一個tuple
    當你使用關鍵字作爲參數時,傳遞會自動合成dict,例如
def MyFamilyMem(name,age,**Familynote):
    print(name,age,Familynote)
MyFamilyMem("Bob",15,sex='boy')
  • Python的函數具有非常靈活的參數形態,既可以實現簡單的調用,又可以傳入非常複雜的參數。
    默認參數一定要用不可變對象,如果是可變對象,程序運行時會有邏輯錯誤!要注意定義可變參數和關鍵字參數的語法: *args是可變參數,args接收的是一個tuple;**kw是關鍵字參數,kw接收的是一個dict。 以及調用函數時如何傳入可變參數和關鍵字參數的語法:
  • 可變參數既可以直接傳入:func(1, 2, 3),又可以先組裝list或tuple,再通過args傳入:func((1, 2, 3)); - 關鍵字參數既可以直接傳入:func(a=1, b=2),又可以先組裝dict,再通過kw傳入:func({‘a’: 1, ‘b’: 2})。 使用*args和**kw是Python的習慣寫法,當然也可以用其他參數名,但最好使用習慣用法。
    命名的關鍵字參數是爲了限制調用者可以傳入的參數名,同時可以提供默認值。

    定義命名的關鍵字參數不要忘了寫分隔符*,否則定義的將是位置參數

  • 對於遞歸函數

def fact(n):
    if n==1:
        return 1
    return n*fact(n-1)
  • 典型的 斐波那契函數,對於遞歸函數時通過不停的壓棧來運行的,棧的空間有限,不能無限遞歸。和調用函數相同又不相同解決棧溢出的方法是爲遞歸
  • 尾遞歸就相當於將其變成循環函數,調用一次釋放一次,所以不會出現棧溢出的情況
def factextern(n):
    return fact_iter(n,1)
def fact_iter(num,prodect):
    if num==1:
        return prodect
    return fact_iter(num-1,num*prodect)
  • 在返回的時候減少了調用棧的過程
  • 尾遞歸調用時,如果做了優化,棧不會增長,因此,無論多少次調用也不會導致棧溢出。
  • 遺憾的是,大多數編程語言沒有針對尾遞歸做優化,Python解釋器也沒有做優化,所以,即使把上面的fact(n)函數改成尾遞歸方式,也會導致棧溢出
  • 使用遞歸函數的優點是邏輯簡單清晰,缺點是過深的調用會導致棧溢出。
  • 針對尾遞歸優化的語言可以通過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能通過尾遞歸實現循環。
  • Python標準的解釋器沒有針對尾遞歸做優化,任何遞歸函數都存在棧溢出的問題
  • python崇尚的極簡原則。高級特性
  • 1、切片。對於一個列表中有多個元素,我們想提取其中第n到m個怎麼辦?for循環遍歷算一種,一個一個輸出,也算提取,但是這樣是否麻煩呢?
  • 去一兩個元素,你說一下兩下的提取確實很方便,要是有50多,100多呢甚至更多。要是列表中有1000個呢for循環也浪費了那麼多次輪詢,當然額,可以使用條件判斷終止循環。但是明明可以一行代碼解決,浪費那麼多體力幹什麼,哈哈,這就是切片
Listone=[]
for i in range (1000):
    Listone.append(i)
print (Listone)
Listone=list(range(100)) - 初始化的簡便方式
print(Listone[2:50]) - 這就是切片
Listone[:50] - 省略了開頭
Listone[20:] - 省略了結尾
print (Listone[20::2])
print (Listone[-10:])
tupleone=tuple(range(100))
print(tupleone[10:15]) - 對於列表和元鏃都可以使用切片技術
  • 對於可迭代對象,形同於for循環中變量的 增長依賴上一次 的結果,在數值計算這門課上有相應的介紹與學習。有興趣可以去學習
for x,y in ((1,1),(2,4)):
    print (x,y)
  • 列表生成式
  • 一個列表想要生成1..100的x^2,for 循環依舊可以,但是還是比較繁瑣
L=[]
for i in range(100):
    L.append(i*i)
  • 也可以這樣幹
[x*x for x in range (199)]
  • 甚至可以在此基礎上篩選想要的結果
Listtwo=[i*i for i  in range(10,50,2) if i%5==0]
print (Listtwo)
  • 在此基礎上還可以套用循環,多層循環,生成表達式。但是一般不超過三層循環,這樣違背了簡單的 原則。可以將他們分開來寫。當然一行代碼能完成的事,也是很酷的
  • 我們已經知道,可以直接作用於for循環的數據類型有以下幾種:
  • 一類是集合數據類型,如list、tuple、dict、set、str等;
  • 一類是generator,包括生成器和帶yield的generator function。
  • 這些可以直接作用於for循環的對象統稱爲可迭代對象:Iterable。
  • 可以使用isinstance()判斷一個對象是否是Iterable對象
  • 對於生成器和迭代器,其實迭代器算是生成器 的一種高級抽像,因爲生成器的思想中也是迭代
def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n
  • 凡是可作用於for循環的對象都是Iterable類型;
  • 凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
  • 集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
  • Python的for循環本質上就是通過不斷調用next()函數實現
  • 高階函數的使用
  • 高階函數 的思想是,函數也可以作爲函數的變量和參數使用
intA=max(1,2,3,4,5,6)
maxA=max
intB=maxA(1,4,5,6,7,8)
print(intA,intB)
  • 結果是一樣的
def add_My(x,y,f):
    return f(x^2,y)+f(y^2,x)
print (add_My(10,2,max))
  • map()作爲高階函數,事實上它把運算規則抽象map()傳入的第一個參數是f,即函數對象本身。由於結果r是一個Iterator,Iterator是惰性序列,因此通過list()函數讓它把整個序列都計算出來並返回一個list
def f(x):
    return x*x
r=map(f,[1,20,40,5,3,2,3,42])
list (r)
 - 對於reduce
from functools import reduce
def add_reduce(x,y):
    return x+y
a=reduce(add_reduce,[1,2,7,4,3,5])
print (a)
def char2num(s):
    return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))
  • 對於filter 就相當於map加上了篩選功能
def   is_palindrom(n):
     return n == int(str(n)[::-1]) - 進行一個迴文數的判斷,就是將整數轉化爲字符串,然後進行一一判斷
  • 對於高階函數sorted ,內置函數作爲排序使用,還可以接受自定義鍵值來排序,用sorted()排序的關鍵在於實現一個映射函數。
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def sort_byname(t):
    return t[0]
def sort_byscore(t):
    return t[1]
La=sorted(L,key=sort_byname)
print (La)
Lb=sorted(L,key=sort_byscore,reverse=1)
print(Lb)
  • 閉包
  • 注意到返回的函數在其定義內部引用了局部變量args,所以,當一個函數返回了一個函數後,其內部的局部變量還被新函數引用,所以,閉包用起來簡單,實現起來可不容易。
  • 另一個需要注意的問題是,返回的函數並沒有立刻執行,而是直到調用了f()才執行。
  • 返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者後續會發生變化的變量
def count():
    def f(j):
        def g():
            return j*j
        return g
    fs=[]
    for i in range(1,4):
        fs.append(f(i))
    return  fs
  • 對於匿名函數lambda 不能有return返回值,基本上都是一個表達式
def add(x,y):
    return lambda : x^3+y^3
  • python對lambda的支持有限,相反對於C++,js之類的語言反倒是經常使用lambda已經在C++11 規範裏面添加現在是2016年,其使用方法和範圍得到了大大的擴散
  • 對於裝飾器函數u,採用了設計模式中的 裝飾模式的影響
  • 其本質上裝飾器就是一個返回函數的高階函數。在動態添加功能的時候,又不能修改原有代碼,保持原有兼容性,又要添加新功能,所以就有了裝飾器,在不修改原有的基礎上添加功能
  • 在面向對象(OOP)的設計模式中,decorator被稱爲裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。
  • Python的decorator可以用函數實現,也可以用類實現。decorator可以增強函數的功能,定義起來雖然有點複雜,但使用起來非常靈活和方便
import functools
def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
#對於functool中的偏函數模塊而言,其目的是爲了簡化函數
int2=functools.partial(int,base=2)
numone=int2('1001')
print(numone)

基本上函數的部分就到這裏了

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