1. 程式人生 > >從 Python到Tensorflow 學習之路(二)

從 Python到Tensorflow 學習之路(二)


最近畢業設計題目是研究對抗樣本,要用tensorflow來搭建神經網路,因此python必不可少,這個不是一個傳統的Python學習教程只是把學習Python過程中遇到的問題和經驗記錄下來(基於Python3.5),如果想要一步一步學習Python建議看下面的網站。
Python學習教程


Python中的迭代

只要是可迭代物件,無論是否有下標,都可以迭代,例如dict:

# -*- coding: utf-8 -*-

dict = {'a':1, 'b':2,'c':3}
# 預設情況下dict迭代的是key, dict儲存不是按照list儲存, 所以迭代的結果很可能不一樣
for key in dict: print(key) # 迭代value for value in dict.values(): print(value) # 迭代value和key for key,value in dict.items(): print(key, value)

判斷一個型別是否可以迭代

# 判斷一個型別是否可以迭代
from collections import Iterable

print(isinstance('abc', Iterable))

print(isinstance([1,  2, 3], Iterable))

print
(isinstance(123, Iterable))

對list實現類似Java的下標迴圈,讓其變成索引元素對

l = ['a', 'b', 'd', 'e']

for index, value in enumerate(l):
    print(index, value)

列表生成式即List Comprehensions, 可以用來建立list的生成式

print (list(range(1, 11)))

print ([x*x for x in range(1, 11)])

# 生成全排列
print ([m+n for m in 'ABC' for n in
'DEF']) # 列出當前目錄下的所有檔案和目錄名 import os print([d for d in os.listdir('.')])

通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含幾百萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。從下面可以看出生成式g有點神似指標。。。

# 生成式
g = (x*x for x in range(1, 11))
print(g)

# output: <generator object <genexpr> at 0x7f2518e03910>

print(next(g))
# output: 1

print(next(g))
# output: 4

for i in g:
    print(i)
# !注意輸出為9, 16, ...

# 利用yield產生斐波那契數列
def fib(n):
    i, a, b = 0, 0, 1
    while i < n:
        yield b
        a, b = b, a+b
        i = i + 1
    return 'done'

f = fib(6)

for i in f:
    print(i)

可以直接作用於for迴圈的資料型別有以下幾種:

  • 一類是集合資料型別,如list、tuple、dict、set、str等
  • 一類是generator,包括生成器和帶yield的generator function
    這些可以直接作用於for迴圈的物件統稱為可迭代物件:Iterable
from collections import Iterable

# 使用isinstance判斷一個物件是否是Iterable物件
print(isinstance([], Iterable))
# output: True

print(isinstance((), Iterable))
# output: True

print(isinstance('abc', Iterable))
# output: True

print(isinstance((x*x for x in range(10)), Iterable))
# output: True

print(isinstance(100,Iterable))
# output: False

Iterator物件:生成器不但可以作用於for迴圈,還可以被next()函式不斷呼叫並返回下一個值,直到丟擲stopIteration錯誤表示無法繼續獲取下一個值。可以被next()函式呼叫並且不斷返回下一個值的物件叫做迭代器,可以使用isinstance()判斷一個物件是否是Iterator物件。

將list、dict、str等Iterable物件變成Iterator,使用iter()函式

print(isinstance(iter(()), Iterator))
# output:True

print(isinstance(iter([]), Iterator))
# output:True

Python中的Iterator物件表示的是一個數據流,Iterator物件可以被next()函式呼叫並不斷返回下一個資料,直到沒有資料時丟擲StopIteration錯誤。可以把這個資料流看做是一個有序序列,但是我們不能提前直到序列的長度,只能不斷通過next()函式按需計算下一個資料,所以Iterator的計算是惰性的,只有在需要返回下一個資料時它才會計算。Iterator甚至可以表示一個無限大的資料,例如全體自然數,然而list不行。

for迴圈的本質

it = iter([1, 2, 3, 4, 5])

while True:
    try:
        print(next(it))
    except StopIteration:
        break

Python中的函數語言程式設計

變數可以指向函式:函式本身也可以賦值給變數,通過變數可以呼叫函式

print(abs(-1.5))
# output: 1.5

print(abs)
# output: <built-in function abs>

f = abs
print(f(-1.5))
# output: 1.5

傳入函式:一個函式可以接收另一個函式作為引數,這種函式就稱之為高階函式

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


print(add(1, -1, abs))
# output: 2

Map、reduce函式

map函式接收兩個引數,一個是函式,一個是Iterable,map將傳入的函式依次作用到序列的每個元素,並且把結果作為新的Iterator返回。

def f(x):
    return x*x


r = map(f, [1, 2, 3, 4, 5, 6])
print(list(r))

res_str = map(str, [1, 2, 3, 4, 5, 6])
print(list(res_str))

reduce函式把一個函式作用在一個序列[x1,x2,x3,…]上,這個函式必須接收兩個引數,reduce把結果繼續和序列的下一個元素做累積計算,即:

reduce(f,[x1,x2,x3])=f(f(x1,x2),x3)

from functools import reduce


def fn(x, y):
    return x * 10 + y


print(reduce(fn, [1, 4, 3, 3]))
# output:1433


def char2num(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]


print(reduce(fn, map(char2num, '1433')))

利用lambda函式整理成一個str2int函式

from functools import reduce

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]


def str2num(s):
    return reduce(lambda x, y: 10*x+y, map(char2num, s))


print(str2num('1433'))

filter

filter()函式用於過濾序列, 和map()類似,filter()函式也接收一個函式和一個佇列。和map()不同的是,filter()把傳入的函式依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。

# 篩選奇數


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


print(list(filter(is_odd, [1, 2, 4, 3])))
# filter產生的是惰性序列即Iterator, 需要用list函式獲得所有結果, output:[1, 3]

sorted

print(sorted([1, 2, 3, -1, 2], key=abs))
# output:[1, -1, 2, 2, 3]

print(sorted(['bob', 'alice', 'John', 'hinton'], key=str.lower, reverse=True))
# output:['John','hinton','bob','alice']

返回函式

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


f = lazy_sum(1, 2, 3, 4, 5)
print(f)
# output: <function lazy_sum.<locals>.sum at 0x7f995d0fb840>
print(f())
# output: 15

f1 = lazy_sum(0, 1)
f2 = lazy_sum(1, 2)
print(f1 == f2)
# output: False

一個函式可以返回一個計算結果,也可以返回一個函式。當返回一個函式時,這個函式並未執行,返回函式中不要引用任何可能會變化的變數。

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


f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
# output:9 9 9
# 返回函式引用了變數i, 但它並非立刻被執行.等到三個函式都返回時,它們所引用的變數i已經變成了3,因此最終結果為9

修改後

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


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

匿名函式

關鍵字lambda表示匿名函式,冒號前面的表示函式引數。匿名函式可以作為返回值返回

print(list(map(lambda x: x*x, [1, 2, 3])))
# output: [1, 4, 9]

f = lambda x: x*x
print(f)
# output:<function <lambda> at 0x7f6966041f28>

def cacl(x, y):
    return lambda:x*x+y*y
print(cacl(1,3))
# output:<function cacl.<locals>.<lambda> at 0x7efc34e652f0>
print(cacl(1,3)())
# output: 10

裝飾器

函式也是一個物件,而且函式物件可以被賦值給變數,所以通過變數也能呼叫該函式。通過函式物件的一個屬性__name__,我們可以得到函式的名字。

def now():
    print('2017-12-21')


f = now
print(f.__name__)
# output:now

如果我們要增強now()函式的功能,比如在函式呼叫前後自動列印日誌,但是又不希望修改now()函式的定義,這種在程式碼執行期間動態增加功能的方式,稱之為“裝飾器(Decorator)”

def log(func):
    def warpper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return warpper


@log
def now():
    print('2017-12-21')


# output:call now()
# 2017-12-21

@log放到now()函式的定義處,相當於執行了語句now = log(now)。由於log()是一個decorator,返回一個函式,所以原來的now()函式仍然存在,只是現在同名的now變數指向了新的函式,於是呼叫now()將執行新函式,即在log()函式中返回的wrapper()函式。wrapper()函式的引數定義是(*args, **kw),因此,warpper()函式可以接受任意引數的呼叫。在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


@log('execute')
def now():
    print('2017-12-21')


now()

# output:execute now():
# 2017-12-21

print(now.__name__)
# wrapper

和兩層巢狀的decorator相比,三層巢狀的效果是這樣的:now=log('execute')(now),首先執行log('execute'),返回的是decorator函式,再呼叫返回的引數,引數是now函式,返回值最終是wrapper函式。但是函式也是物件,它有name等屬性,但上面程式碼中的經過decorator裝飾過的函式,它們的名字已經從原來的now變成了wrapper

我們需要把原始函式的各種屬性複製到wrapper()函式中,否則有些依賴函式簽名的程式碼執行就會出錯。修改後如下

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


@log('execute')
def now():
    print('2017-12-21')


now()

# output:execute now():
# 2017-12-21

print(now.__name__)
# now

偏函式

import functools

print(int('12345'))

print(int('12345', base=8))

# 如果要轉換大量的二進位制字串,每次都傳入int(x, base=2)太麻煩,容易想到的方法如下


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


print(int2('10000'))
# output:16


# 使用偏函式

int2 = functools.partial(int, base=2)
print(int2('11'))
# output:3
''' 
   int2('11')相當於kw={'base': 2} int('11', **kw)
   當傳入max2 = functools.partial(max, 10)實際上會把10作為*args的一部分自動加到左邊,也就是:
   max2(5, 6, 7)相當於args=(10, 5, 6, 7) max(*args)
'''

functools.partial的作用就是,把函式的某些引數給固定住,返回一個新的函式,呼叫這個新函式會更加簡單。建立偏函式時,實際上可以接收函式物件、*args**kw三個引數

我們下期見!~