1. 程式人生 > >python簡單筆記

python簡單筆記

針對 是的 機制 函數作為參數 編寫 usr 之間 r語 alt

叠代器

叠代是Python最強大的功能之一,是訪問集合元素的一種方式。

叠代器是一個可以記住遍歷的位置的對象。

叠代器對象從集合等第一個元素開始訪問,直到所有的元素被訪問結束,叠代器只能往前不會後退。

叠代器有兩個基本的方法:iter()next()

字符串,列表或元組對象都可以用於創建叠代器。

叠代器的一大優點是不要求事先準備好整個叠代過程中所有的元素。叠代器僅僅在叠代到某個元素時才計算該元素,而在這之前或之後,元素可以不存在或者被銷毀。這個特點使得它特別適合用於遍歷一些巨大的或是無限的集合,比如幾個G的文件

特點:

  1. 訪問者不需要關心叠代器內部的結構,僅需通過next()方法不斷去取下一個內容
  2. 不能隨機訪問集合中的某個值 ,只能從頭到尾依次訪問
  3. 訪問到一半時不能往回退
  4. 便於循環比較大的數據集合,節省內存
>>> list = [1,2,3,4]
>>> it = iter(list)  # 創建叠代器對象
>>> print(next(it))  # 輸出叠代器的下一個元素
1
>>> print(next(it))
2

叠代器對象可以使用常規for語句進行遍歷:

list=[1,2,3,4]
it = iter(list)    # 創建叠代器對象
for x in it:
    print (x)

執行以上程序,輸出結果如下:

1
2
3
4

也可以使用 next() 函數:

技術分享
import sys         # 引入 sys 模塊

list=[1,2,3,4]
it = iter(list)    # 創建叠代器對象

while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()
技術分享

執行以上程序,輸出結果如下:

1
2
3
4

凡是能夠用for循環遍歷的數據結構和對象統統可以稱為可叠代的,即Iterable。我們現在接觸到Iterable數據結構有list、tuple、dict、string、set,還有生成器generator(下面將講到)。

但是,他們之間有什麽區別呢?generator不但可以使用for循環,還可以被next()函數不斷調用並返回下一個值,直到返回StopIteration錯誤。

能夠被next()函數不斷調用並返回下一個值的對象稱為叠代器Iterator。generator是Iterable,同時也是一個Iterator;而list、tuple、str、dict不是Iterator。

當然,可以通過isinstance()判斷一個對象是否為Iterator。

技術分享
>>> from collections import Iterator
>>> isinstance((x*x for x in range(10)), Iterator)
True
>>> isinstance([],Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance("ABC",Iterator)
False
技術分享

如果想讓list、tuple、str、dict等Iterable對象轉變成Iterator,用iter()函數能夠讓Iterable對象變為Iterator

技術分享
>>> isinstance(iter("ABC"),Iterator)
True
>>> i = iter("ABC")
>>> next(i)
‘A‘
>>> next(i)
‘B‘
>>> next(i)
‘C‘
技術分享

Iterable和Iterator對象到底有什麽區別呢?

實際上,python中Iterator對象表示的是一個數據流,Iterator對象可以被next()函數不斷調用返回下一個值,直到數據全部被返回。Iterator對象數據流可以被看作是一個序列,但是這個序列的長度是不可提前預知的,因為只能通過next()或者for循環按照需要調用下一個數據,所以Iterator對象的計算是一種惰性計算。

正是因為這樣的特性,Iterator可以表示一個無限大的數據流,譬如全體自然數,而list是無法完成這樣的任務的。

列表生成式

在講到生成器之前我們先來講一下列表生成式

列表生成式,顧名思義,就是用來生成列表的。以前我們生成列表的時候,一般都是先定義一個list = [] ,再用for循環將元素一個一個append到list中。

簡單列表的生成,可以使用list(range(1,100)),那麽如果需要生成[1*1,2*2,3*3....]呢?

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

同時,還可以添加一些更多的要素,判斷使用符合要求的x參與計算。

>>> [x*x for x in range(10) if x%2==0]
[0, 4, 16, 36, 64]

使用多重循環還可以生成全排列

>>> [x+y for x in ‘ABC‘ for y in ‘LMN‘]
[‘AL‘, ‘AM‘, ‘AN‘, ‘BL‘, ‘BM‘, ‘BN‘, ‘CL‘, ‘CM‘, ‘CN‘]
>>>
>>> [x+y+z for x in ‘ABC‘ for y in ‘LMN‘ for z in ‘OPQ‘ if z==‘Q‘]
[‘ALQ‘, ‘AMQ‘, ‘ANQ‘, ‘BLQ‘, ‘BMQ‘, ‘BNQ‘, ‘CLQ‘, ‘CMQ‘, ‘CNQ‘]

生成器

通過列表生成器,我們可以創造一個列表。但是,受到內存容量的限制,列表的容量肯定是有限的,如若我們只需要使用列表中的一部分,就不需要一下子生成很大的列表,直接按需生成就好了,列表生成器就提供了這樣的功能。

生成器相當於是一個生成列表元素的算法,有了這個算法,就不用一下子生成很多元素了,只需要在需要的時候生成就可以了,節省了很多空間。在Python中,這種一邊循環一遍計算的機制,稱為:generator。

創建一個generator只需要把列表生成器的[]改成()就可以了。

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> (x*x for x in range(10))
<generator object <genexpr> at 0x00000259A50BD308>

獲取generator裏面的元素,可以使用next()函數生成generator的下一個元素。

技術分享
>>> g=(x*x for x in range(10))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
.
.
.
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
技術分享

一直調用next()函數就可以遍歷generator的所有元素,直到拋出StopIteration錯誤。

但是最常用的當然還是for循環,畢竟generator也是Iterator的對象。

技術分享
>>> g=(x*x for x in range(5))
>>> for element in g:
...     print(element)
...
0
1
4
9
16
技術分享

一般我們創建一個generator後,基本上不會調用next(),而是通過for循環來叠代它,並且不需要關心StopIteration的錯誤。

有時候,推算元素的算法很復雜,並不能通過列表生成器實現,而需要通過函數打印出來,譬如菲波那切數列。

技術分享
#!/usr/bin/env python

def fib(Max):
    n = 0
    a,b = 0,1
    while n < Max:
        print(b)
        a,b = b,a+b
        n = n + 1

fib(5)

結果:
1
1
2
3
5
技術分享

在Python中,使用了yield的函數被稱為生成器(generator)。

跟普通函數不同的是,生成器是一個返回叠代器的函數,只能用於叠代操作,更簡單點理解生成器就是一個叠代器。

在調用生成器運行的過程中,每次遇到yield時函數會暫停並保存當前所有燈運行信息,返回yield的值。並在下一次執行next()方法時從當前位置繼續運行。

技術分享
#!/usr/bin/env python

def fib(Max):
    n = 0
    a,b = 0,1
    while n < Max:
        yield b
        a,b = b,a+b
        n = n + 1

g = fib(5)
print(next(g))
print(next(g))
for i in g:
    print(i)

結果:
1
1
2
3
5
技術分享

在函數的執行過程中,遇到return才會返回,但是變成了generator的函數,遇到了yield就會中斷,並且返回yield的參數。

那麽,遇到了yield就會中斷並返回,我們能不能通過yield傳一些參數進去呢?

技術分享
#!/usr/bin/env python

def fib(Max):
    n = 0
    a,b = 0,1
    while n < Max:
        e = yield b
        print(e)
        a,b = b,a+b
        n = n + 1

g = fib(5)
print(next(g))
print(next(g))
print(g.send("the third element"))
print(g.send("forth"))

結果:
1
None
1
the third element
2
forth
3
技術分享

在上面的generator定義中,使用了e = yield b語句,這樣遇到yield返回b的同時,還能夠通過g.send(arg)傳入一個參數賦值給e,這樣,就可以通過generator進行參數傳遞了。

技術分享
>>> def fib(Max):
...     n = 0
...     a,b = 0,1
...     while n < Max:
...         e = yield b
...         print(e)
...         a,b = b,a+b
...         n = n + 1
...
>>> g = fib(5)
>>> g.send("the first element")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can‘t send non-None value to a just-started generator
>>> g.send(None)
1
技術分享

註意,看上面的代碼,為什麽會報錯?說明啟動一個generator的時候,只能傳入None作為參數,而generator就相當於next(g)!

2.裝飾器

原則:
1、不能修改被裝飾函數的源代碼。
2、不能修改被裝飾函數的調用方式。
3、不能改變被裝飾函數的執行結果。
裝飾器對被裝飾函數是透明的。

如何理解裝飾器
1、函數即“變量”
2、高階函數
a:把一個函數名作為實參傳遞給另外一個函數
b:返回值中包含函數名
3、嵌套函數
在一個函數體內聲明另一個函數稱為函數的嵌套

參考鏈接:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000

裝飾器(decorator)是一種高級的Python語法。裝飾器可以對一個函數,方法或者類加工。

由於函數也是一個對象,而且函數對象可以被賦值給變量,所以,通過變量也能調用該函數。

>>> def now():
...     print(‘2017-05-01‘)
...
>>> f = now
>>> f()
2017-05-01

函數對象有一個__name__屬性,可以拿到函數的名字:

>>> now.__name__
‘now‘
>>> f.__name__
‘now‘

現在,假設我們要增強now()函數的功能。比如,在函數調用前後自動打印日誌,但是又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能的方式,就稱之為:裝飾器(decorator)。

本質上,decorator就是一個返回函數的高階函數。所以,我們要定義一個能打印日誌的decorator,可以定義如下:

def log(func):
    def wrapper(*args, **kwargs):
        print("call %s():" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

觀察上面的log,因為它是一個decorator,所以接受一個函數作為參數,[email protected],吧decorator置於函數的定義處:

@log  # 把這句放在這兒就相當於執行了 now = log(now)
def now():
    print("2017-05-01")

調用now()函數,不僅會運行now()函數本身,還會運行now()函數前打印一行日誌:

>>> now()
call now():
2017-05-01

[email protected]()函數的定義處,就相當於執行了語句:

now = log( now )

由於log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,於是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。

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

技術分享
>>> def log(text):
...     def decorator(func):
...         def wrapper(*args, **kwargs):
...             print("%s %s():" % (text, func.__name__))
...             return func(*args, **kwargs)
...         return wrapper
...     return decorator
...
>>> @log("execute")
... def now():
...     print("2017-05-01")
...
>>> now()
execute now():
2017-05-01
技術分享

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

>>> now = log("execute")(now)

我們來剖析上面的語句,首先執行log(‘execute‘),返回的是的decorator函數,在調用返回的函數,參數是now函數,返回值最終是wrapper函數。

以上兩種decorator的定義都沒有問題,但還差最後一步。因為我們講了函數也是對象。它有__name__等屬性,但你去看經過decorator裝飾之後的函數,它們的__name__已經從原來的‘now‘變成了‘wrapper‘:

>>> now.__name__
‘wrapper‘

因為返回的那個wrapper()函數名字就是‘wrapper‘,所以,需要把原始函數的__name__等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執行就會出錯。

不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是幹這個事的,所以,一個完整的decorator的寫法如下:

技術分享
import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print(‘call %s():‘ % func.__name__)
        return func(*args, **kw)
    return wrapper

@log("execute")
def now():
    print("2017-05-01")

now()
技術分享

或者針對帶參數的decorator:

技術分享
import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print("%s %s():" % (text, func.__name__))
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log("execute")
def now():
    print("2017-05-01")

now()

python簡單筆記