從 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三個參數

我們下期見!~


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