從 Python到Tensorflow 學習之路(二)
阿新 • • 發佈:2018-12-11
最近畢業設計題目是研究對抗樣本,要用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把結果繼續和序列的下一個元素做累積計算,即:
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)
'''