python學習筆記(二)---高級特性

依據廖雪峯官方網站的python教程整理

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

r'''
    高階知識
    lrn_higher_list: 切片、迭代、列表生成式、生成器、迭代器
    lrn_higher_func: 函數式編程,修飾器、匿名函數、篩選器等
'''

__author__ = 'Kingrumn'

'''
    第1行和第2行是標準註釋,第1行註釋可以讓這個.py文件直接在Unix/Linux/Mac上運行,第2行註釋表示.py文件本身使用標準UTF-8編碼;

    第4行是一個字符串,表示模塊的文檔註釋,任何模塊代碼的第一個字符串都被視爲模塊的文檔註釋;

    第10行使用__author__變量把作者寫進去,這樣當你公開源代碼後別人就可以瞻仰你的大名;
'''


from collections import Iterable
from collections import Iterator
from functools import reduce
import functools


# 高級特性--切片、迭代、列表生成式、生成器、迭代器
def lrn_higher_list():
    # 切片Slice
    lst = list(range(100))

    # 取前N個元素,也就是索引爲0-(N-1)的元素
    # list[a:b:c]: c>0, a<b; c<0, a>b, >指位置靠後
    print(lst[0:3])      # [0, 1, 2]
    print(lst[:3])      # [0, 1, 2]

    # 後N個元素
    print(lst[-2:])     # [98, 99]

    # 帶步長取
    print(lst[10:20:2])     # [10, 12, 14, 16, 18]
    print(lst[::15])     # [0, 15, 30, 45, 60, 75, 90]

    # 逆序取列表
    print(lst[20:10:-2])    # [20, 18, 16, 14, 12]
    print(lst[-10:80:-2])    # [20, 18, 16, 14, 12]

    # 迭代
    # dict迭代方法
    d = {"a": 1, "b": 2, "c": 3}
    # 按key迭代
    for key in d:
        print(d[key])
    # 按value迭代
    for value in d.values():
        print(value)
    # 同時迭代
    for k, v in d.items():
        print(k, v)
    # 判斷對象是否可迭代
    # from collections import Iterable
    print(isinstance('abc', Iterable))
    # enumerate可將list變爲索引-元素對
    for k, v in enumerate(['a', 'b', 'c']):
        print(k, v)

    # 列表生成式List Comprehensions
    # range:    range(start, stop[, step])
    print(list(range(1, 11, 2)))            # [1, 3, 5, 7, 9]
    print([x * x for x in range(1, 5)])     # [1, 4, 9, 16]
    print([x * x for x in range(1, 11) if x % 2 == 0])  # [4, 16, 36, 64, 100]
    print([m + n for m in 'ABC' for n in 'XYZ'])        # ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
    d = {'x': '1', 'y': '2'}
    print([k + '=' + v for k, v in d.items()])      # ['x=1', 'y=2']
    lst = ['A', 'B', 'C']
    print([s.lower() for s in lst])           # ['a', 'b', 'c']

    # 生成器
    # 在循環的過程中不斷推算出後續的元素,不必創建完整的list
    # 一邊循環一邊計算的機制,稱爲生成器:generator
    # 方法一:列表生成式的[]改成()
    lst = (x * x for x in range(10))
    print(lst)          # <generator object lrn_higher.<locals>.<genexpr> at 0x0322C8B0>
    print(next(lst))    # 0
    print(next(lst))    # 1
    print(next(lst))    # 4
    # 沒有更多的元素時,拋出StopIteration的錯誤

    # generator也是可迭代對象, 用for循環纔是正確的做法
    for n in lst:
        print(n)

    # 方法二:利用yield 構造;
    # 一個函數定義中包含yield關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator
    # 函數是順序執行,遇到return語句或者最後一行函數語句就返回;
    # 而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行;
    # 用for循環調用generator時,發現拿不到generator的return語句的返回值,
    # 如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中
    # 斐波那契數列
    def fib(m):
        n, a, b = 0, 0, 1
        while n < m:
            yield b
            a, b = b, a + b
            n = n + 1
        return 'done'
    next(fib(10))

    # 楊輝三角
    print(u"楊輝三角")

    def yt():
        a = [1]
        yield a
        b = [1, 1]
        yield b
        while True:
            index = 0
            a = b
            b = [1, 1]
            while index < len(a) - 1:
                at = a[index] + a[index+1]
                b.insert(-1, at)
                index += 1
            yield b

    n = 0
    results = []
    for t in yt():
        print(t)
        results.append(t)
        n = n + 1
        if n == 10:
            break

    # 迭代器
    # 可以被next()函數調用並不斷返回下一個值的對象稱爲迭代器:Iterator
    # 可以使用isinstance()判斷一個對象是否是Iterator對象
    # 生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator
    # 可以使用iter()函數把list、dict、str等Iterable變成Iterator
    print(isinstance((x for x in range(10)), Iterator))     # True
    print(isinstance(iter([]), Iterator))               # True
    print(isinstance([], Iterator))             # False


# 函數式編程
def lrn_higher_func():

    def p(x):
        return x

    def pa(x):
        return -x
    # 變量可以指向函數
    f = p(-10)
    print(f)            # -10
    f = p
    print(f)            # <function lrn_function_higher.<locals>.p at 0x036348E8>

    # 函數名也是變量,可以進行賦值,這麼做是危險的
    print(pa(-10))      # 10
    pa = p
    print(pa(-10))     # -10

    # 高階函數
    # 一個函數就可以接收另一個函數作爲參數,這種函數就稱之爲高階函數
    def add(x, y, func):
        return func(x) + func(y)

    print(add(-5, 4, abs))  # 9

    # map
    # map()函數接收兩個參數,一個是函數,一個是Iterable
    # map將傳入的函數依次作用到序列的每個元素,並把結果作爲新的Iterator返回
    def f(x):
        return x * x
    r = map(f, [1, 2, 3, 4, 5])
    print(list(r))      # [1, 4, 9, 16, 25]
    print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))      # ['1', '2', '3', '4', '5', '6', '7', '8', '9']

    # reduce
    # reduce把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算
    # from functools import reduce
    def add(a, b):
        return a * 10 + b

    print(reduce(add, [1, 3, 5, 7, 9]))     # 13579

    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

    def char2num(s):
        return digits[s]

    # str2int 函數: ’13579‘  --->   13579
    def str2int(s):
        def fn(x, y):
            return x * 10 + y

        return reduce(fn, map(char2num, s))
    print(str2int('13579'))     # 13579

    # 用lambda函數簡化寫法
    def str2int(s):
        return reduce(lambda x, y: x * 10 + y, map(char2num, s))

    print(str2int('13579'))     # 13579

    # str2float
    def str2float(s):
        index = s.find('.')
        return reduce(lambda x, y: x * 10 + y, map(char2num, s[:index])) \
               + reduce(lambda x, y: x * 0.1 + y, map(char2num, s[-1:index:-1]+'0'))
    print(str2float('0.123'))

    # filter
    # filter()也接收一個函數和一個序列
    # filter()把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素
    # filter()函數返回的是一個Iterator
    def is_odd(x):
        return x % 2 == 1

    print(list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])))     # [1, 5, 9, 15]

    # 利用埃氏篩法求所有素數
    # 依次從第一個篩掉所有能被整除的數
    def odd_iter():
        n = 1
        while True:
            n += 2
            yield n

    def not_divisible(n):
        return lambda x: x % n > 0  # 此處返回的是個函數

    def primes():
        yield 2
        it = odd_iter()  # 初始序列
        while True:
            n = next(it)  # 返回序列的第一個數
            yield n
            it = filter(not_divisible(n), it)  # 構造新序列
    mn = 0
    ml = []
    for i in primes():
        ml.append(i)
        mn += 1
        if mn > 10:
            break
    print(ml)

    # sorted
    # 可以對list進行排序
    # sorted()函數也是一個高階函數,它還可以接收一個key函數來實現自定義的排序
    '''
         sorted(iterable[, cmp[, key[, reverse]]]),後面的可選參數爲命名關鍵字參數
         iterable -- 可迭代對象。
         cmp -- 比較的函數,這個具有兩個參數,參數的值都是從可迭代對象中取出,此函數必須遵守的規則爲,大於則返回1,小於則返回-1,等於則返回0
         key - - 主要是用來進行比較的元素,只有一個參數,具體的函數的參數就是取自於可迭代對象中,指定可迭代對象中的一個元素來進行排序。
         reverse -- 排序規則,reverse = True 降序 , reverse = False 升序(默認)。
         返回重新排序的列表, 保持原列表不變
    '''
    print(sorted([36, 5, -12, 9, -21], key=abs))

    # 函數作爲返回值
    def lazy_sum(*args):
        def s():
            ax = 0
            for n in args:
                ax = ax + n
            return ax

        return s

    f1 = lazy_sum(1, 3, 5, 7, 9)
    f2 = lazy_sum(1, 3, 5, 7, 9)
    print(f1 == f2)     # 此時才真正進行計算

    # 閉包
    # 返回函數不要引用任何循環變量,或者後續會發生變化的變量。
    def count():
        def func(j):
            def g():
                return j * j

            return g

        fs = []
        for i in range(1, 4):
            fs.append(func(i))  # func(i)立刻被執行,因此i的當前值被傳入func()
        return fs

    f1, f2, f3 = count()
    print(f1())         # 1
    print(f2())         # 4
    print(f3())         # 9

    # 匿名函數
    # 函數沒有名字,不必擔心函數名衝突
    # 也可以把匿名函數賦值給一個變量,再利用變量來調用該函數
    def build(x, y):
        return lambda: x * x + y * y

    print(build(1,2)())

    # 裝飾器
    # 函數有個內置的__name__屬性,可以拿到函數的名字
    # decorator就是一個返回函數的高階函數
    def log(func):
        @functools.wraps(func)              # functools模塊,用於修改wrapper的__name__爲func.__name__
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__, end=' ')    # 增加end=''防止換行
            return func(*args, **kw)

        return wrapper

    @log                # 相當於執行了now = log(now)
    def now():
        print('2015-3-25')

    now()       # call now(): 2015-3-25

    # 偏函數
    # 把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單
    # import functools
    # functools.partial
    int2 = functools.partial(int, base=2)
    print(int2('1010101'))         # 85


# main
'''
    當我們在命令行運行hello模塊文件時,Python解釋器把一個特殊變量__name__置爲__main__,
    而如果在其他地方導入該hello模塊時,if判斷將失敗,
    因此,這種if測試可以讓一個模塊通過命令行運行時執行一些額外的代碼,最常見的就是運行測試
'''
if __name__ == "__main__":
    lrn_higher_list()
    lrn_higher_func()

更多更及時的博客更新請戳—> KingRumn

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