廖雪峯 Python教程(Java基礎) 筆記 二

##一、函數式編程
函數式編程接近與數學計算,抽象程度很高,相應的和計算機底層結構差異越大,效率不如C語言等高。
函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之爲沒有副作用。而允許使用變量的程序設計語言,由於函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。

Python對函數式編程提供部分支持。由於Python允許使用變量,因此,Python不是純函數式編程語言。

###1、高階函數
函數 == 對象
函數式編程中,函數本身像一個對象一樣,只是沒有內部數據狀態而,而函數名和變量名一樣,指向某個函數對象的地址。
a、如f = abs 此時f就代表abs函數,能計算絕對值了。而將abs看做一個變量,它也是可變的!! 注意賦值是不帶括號和參數的如果帶了,就是執行了。
b、如果執行abs = 10,ads將不再能計算絕對值了,它將變成10這個數字。abs函數丟失了!,此時想要重新可用,需要重啓Python交互式環境。
c、函數可以作爲參數,返回值,容器元素等,被傳遞,存儲等幾乎任何變量使用的地方。

1.1 map/reduce
map就是映射,就想數學中的映射函數一樣,是現實世界中一類十分常見的操作處理。map就是對一個序列中所有元素做某種變換,比如路徑字符串替換前面的盤符。使用方式如下,先定義一個處理的函數f,然後放到map(f, Iterable)中,最後返回一個lterator惰性計算的序列。

>>> def f(x):
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce:
reduce 和map的不同點是函數參數個數必須爲2,即f(x1, x2),且操作序列的方式不同,它會將函數前一次計算的結果作爲參數x1傳入,然後取下一個數做x2傳入,所以reduce的Iterable的元素個數至少爲2,最後計算出一個結果。即
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

1.2 filter
如名,filter對列表中的元素進行過濾,傳入一個返回boolean的函數即可。

1.3 sort
如名,對列表中的元素進行排序,使用方式
sorted([36, 5, -12, 9, -21]) 直接排序

sorted([36, 5, -12, 9, -21], key=abs) 添加排序函數,此排序函數返回一個值,然後根據返回值排序

sorted([36, 5, -12, 9, -21], key=abs, reverse=True) 反向

以上元素能方便的轉換成key的,如果有多級參數排序這種不好轉換成key的,在Python2裏面可以傳入cmp函數,但是在Python3裏面去掉了,搞了一個很複雜的操作。

2、返回函數
即在函數中創建另一個函數,然後作爲返回值返回,此函數並不會立即執行,需要在後面進行顯示的的調用之後纔會執行。此函數可以訪問外部函數的參數和局部變量,本質上是持有它們的引用, 可以訪問這個引用,以及改變它所指向的對象內部變量的值,但是不能改變這個引用所指向的地址及對象,只能外部函數改變,這和Java的函數裏面的匿名內部類相似,只是沒有想Java裏面一樣final化,Python裏面沒有這個機制,所有會有後面一個問題q:,比如

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum


f = lazy_sum(1, 3, 5, 7, 9)
print(f())

特點:
1、每次調用外部返回的函數都是不一樣的。

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

問題q:返回的函數是持有內部局部變量的引用,並不是直接拷貝一個,所以如果在下次執行之前,局部變量發生改變,函數持有的數據也是改變了的。比如下面這種:

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i  
        fs.append(f)
    return fs

f1, f2, f3 = count()

>>> f1()
9
>>> f2()
9
>>> f3()
9

#因爲在循環中定義的函數,等到後面某個時間執行時,內部函數(閉包)指向的變量的值已經改變了,變成了3。所以輸出結果都是9

對於循環內的變量,如果一定要引用,方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:

def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執行,因此i的當前值被傳入f()
return fs
再看看結果:

>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

因爲傳參時會創建一個新的引用,count函數對i的改變,不可能再影響f的j參數了。缺點是代碼較長,可利用lambda函數縮短代碼。

2、lambda表達式——匿名函數
lambda表達式的格式:lambda 參數列表:一個表達式
參數列表可以有多種參數,當然,它還可以像一個普通的函數一樣,可以賦值傳遞等。
使用lambda可以簡化函數的編寫,更加快捷
lambda表達式可用來編寫跳轉表(jump table),就是行爲的列表或字典。例如:

L = [(lambda x: x**2),  
    (lambda x: x**3),  
    (lambda x: x**4)]  

** 3、函數裝飾器 **
顧名思義,對函數加上一層包裝,增加其功能。
例如

def log(func):

	@functools.wraps(func)
    def wrapper(x):
        print('start' + func.__name__)
        ret = func(x)
        print('end' + func.__name__)
        return ret

    return wrapper

@log
def abs(x):
    if x > 0:
        return x
    else:
        return -x


print(abs(-11))

Python裏面的函數名可以通過賦值改變其指向,所以其中的@log就相當於依據 abs = log(abs) 此時abs就相當於wapper函數,可在wrapper中接收相同的參數,返回函數值等。
使用裝飾器之後,原函數的函數名會改變,Python提供了一個funtools函數進行處理 如上面的@functools.wraps(func)就是把wrapper的函數名稱變回去,有些依賴簽名的工具需要這個。

如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數,寫出來會更復雜。比如,要自定義log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

這個3層嵌套的decorator用法如下:

@log('execute')
def now():
    print('2015-3-25')

執行結果如下:

>>> now()
execute now():
2015-3-25

和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:

>>> now = log('execute')(now)
我們來剖析上面的語句,首先執行log(‘execute’),返回的是decorator函數,再調用返回的函數,參數是now函數,返回值最終是wrapper函數。這樣就能往第三層的函數裏面傳入相同的參數,並且可以使用第一層的屬於Derector的參數了。巧妙的利用內部函數可以訪問外部函數參數和局部變量的特性。Python裏面函數基本也相當於對象了,外部函數的參數、局部變量被函數對象持有就不會消失。

4、functools提供的一些功能
functools.partial(f, a); 根據已有的函數f,自動傳入部分參數,然後以此創建一個新的函數。編碼中,通常需要創建某個函數的重載函數,原來函數所有參數都需要調用時傳遞,重載的只需傳遞部分的參數,另一部分在重載函數內傳遞默認值即可。functools.partial就是用來簡化這種操作的。

##Python的模塊、包
###1、定義
1、定義:
Python是將一個代碼文件稱作模塊,也即模塊就是一個.py文件。
包的定義和其它語言中一樣,但是Python的包裏面必須放上一個__init__.py文件,不管裏面有無內容,否則不會被當做包處理。

2、模塊文件頭:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '

__author__ = 'Michael Liao'

第一、二行是標準註釋,第一行讓此文件可以直接在unix linux的機器上運行,第二行 表示使用標準的UTF-8編碼,
第四行當做模塊地註釋,任何寫在py文件開頭的字符串都當做文檔註釋。
第六行的表示作者
這些可以不寫,但一般寫了更好

###2、導入模塊

1、導入語句
import module_name #直接導入,系統的或者是同級目錄下的
from package_test import module_name #不是同級目錄下的

對導入模塊的搜索識別,默認情況下,Python解釋器會搜索當前目錄、所有已安裝的內置模塊和第三方模塊,搜索路徑存放在sys模塊的path變量中。

還可以手動指定要搜索的模塊:
第一種直接修改System.path,運行結束後失效
sys.path.append('/Users/michael/my_py_scripts')
第二種方法是設置環境變量PYTHONPATH,該環境變量的內容會被自動添加到模塊搜索路徑中。設置方式與設置Path環境變量類似。

2、作用域
def _private_function_name(xxx) 表示私有函數,a或者__a私有的變量,這樣前面加上兩條一條或兩條’'的表示私有的變量,這只是告訴調用者不要訪問這個函數或者變量,但是仍然是可以訪問的,因爲Python沒有機制保證這個變量或者方法不能被訪問。

###3、其他的:
__name__ 在Python中表示特殊變量,可以直接被引用,但是有特殊用途,代碼中一般不創建這樣的變量。
比如__doc__ 表示模塊文檔,__author__表示作者,

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