這是我學(xué)習(xí)python的過(guò)程中記錄的一些東西歹啼,記錄了一些python的特性以及python工程師面試過(guò)程中經(jīng)常被問(wèn)到的點(diǎn)位隶。
第一章:Python基礎(chǔ)
輸入輸出
name = input('please enter your name: ')
print('hello,', name)
變量類型
n = 123 #整數(shù)
f = 456.789 #浮點(diǎn)
a = True and False or True #布爾
b = None #空
s1 = 'Hello, world' #字符串
s3 = r'Hello, "Bart"' #r為默認(rèn)不轉(zhuǎn)義
s4 = r'''Hello,
Lisa!''' #```內(nèi)為自動(dòng)加上換行符
編碼
一般腳本前加上這兩行
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
list & tuple
classmates = ['Michael', 'Bob', 1.23, ['asp', 'php']] #list數(shù)據(jù)類型
len(classmates)
classmates[0]
classmates[-1]
classmates.append('Adam') #加在末尾
classmates.insert(1, 'Jack') #加在指定位置
classmates.pop(1) #刪除制定元素并返回矗蕊,默認(rèn)為最后一個(gè)
t = (1, 2) #tuple數(shù)據(jù)類型,一旦指定不能改變
t = ('a', 'b', ['A', 'B']) #指向一個(gè)list脯爪,就不能改成指向其他對(duì)象
t[2][0] = 'X' #但指向的這個(gè)list本身是可變的
if
age = 20 #nothing special
if age >= 6:
print('teenager')
elif age >= 18:
print('adult')
else:
print('kid')
循環(huán)
names = ['Michael', 'Bob', 'Tracy']#第一種, for只有for...in...這種寫(xiě)法
for name in names:
print(name)
while n > 0:#第二種
sum = sum + n
n = n - 2
dict & set
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} #dict數(shù)據(jù)類型
d['Michael'] = 80 #修改
'Thomas' in d #返回False
d.get('Thomas') #返回None
d.pop('Bob') #刪除元素并返回
和list比較,dict有以下幾個(gè)特點(diǎn):
- 查找和插入的速度極快副砍,不會(huì)隨著key的增加而變慢坡倔;
- 需要占用大量的內(nèi)存漂佩,內(nèi)存浪費(fèi)多
- 要保證hash的正確性,作為key的對(duì)象就不能變
- 在Python中罪塔,字符串投蝉、整數(shù)等都是不可變的,因此征堪,可以放心地作為key瘩缆。而list是可變的,就不能作為key
還有一種數(shù)據(jù)類型set佃蚜,類似于數(shù)學(xué)中的集合庸娱,反正我沒(méi)用過(guò)
函數(shù)
- 一些內(nèi)置函數(shù)
abs(-20)
int('123')
str(1.23)
a = abs # 變量a指向abs函數(shù)
a(-1)
- 定義函數(shù)
import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny #實(shí)際上是返回了一個(gè)tuple
定義函數(shù)時(shí),需要確定函數(shù)名和參數(shù)個(gè)數(shù)谐算;
如果有必要熟尉,可以先對(duì)參數(shù)的數(shù)據(jù)類型做檢查;
函數(shù)體內(nèi)部可以用return隨時(shí)返回函數(shù)結(jié)果洲脂;
函數(shù)執(zhí)行完畢也沒(méi)有return語(yǔ)句時(shí)斤儿,自動(dòng)return None。
函數(shù)可以同時(shí)返回多個(gè)值恐锦,但其實(shí)就是一個(gè)tuple往果。
- 函數(shù)參數(shù)
參數(shù)定義的順序必須是:必選參數(shù)、默認(rèn)參數(shù)一铅、可變參數(shù)陕贮、命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù)。
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} #dict數(shù)據(jù)類型
d['Michael'] = 80 #修改
'Thomas' in d #返回False
d.get('Thomas') #返回None
d.pop('Bob') #刪除元素并返回
def f2(a, b, c=0, *args, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None} #output
*args是可變參數(shù)潘飘,args接收的是一個(gè)tuple肮之;
**kw是關(guān)鍵字參數(shù),kw接收的是一個(gè)dict卜录。
第二章:面向?qū)ο蟾呒?jí)編程
給類和實(shí)例綁定方法
s = Student()
s.name = 'Michael' # 給實(shí)例綁定屬性
def set_age(self, age): # 定義一個(gè)函數(shù)作為實(shí)例方法
self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s) # 給實(shí)例綁定一個(gè)方法
def set_score(self, score):
self.score = score
Student.set_score = set_score # 給對(duì)象綁定方法
使用slots
class Student(object):
__slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
使用@property
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
>>> s = Student()
>>> s.score = 60 # OK局骤,實(shí)際轉(zhuǎn)化為s.set_score(60)
>>> s.score # OK,實(shí)際轉(zhuǎn)化為s.get_score()
60
>>> s.score = 9999
error
python允許多重繼承
定制類
- 打印得漂亮一些
def __str__(self):
return 'Student object (name: %s)' % self.name
__repr__ = __str__ # 這是在直接輸入s的時(shí)候輸出的東西
>>> print(Student('Michael'))
Student object (name: Michael)
- 可以被for..in..調(diào)用
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個(gè)計(jì)數(shù)器a暴凑,b
def __iter__(self):
return self # 實(shí)例本身就是迭代對(duì)象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計(jì)算下一個(gè)值
if self.a > 100000: # 退出循環(huán)的條件
raise StopIteration();
return self.a # 返回下一個(gè)值
- 可以被下標(biāo)和切片訪問(wèn)
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
- 動(dòng)態(tài)返回屬性或者方法
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
# 一個(gè)非常有用的用法赘来,就是不判斷attr现喳,然后操作attr返回一個(gè)東西
-
__call__
,直接調(diào)用實(shí)例
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
調(diào)用方式如下:
>>> s = Student('Michael')
>>> s() # self參數(shù)不要傳入
My name is Michael.
使用元類
type()函數(shù)既可以返回一個(gè)對(duì)象的類型凯傲,又可以創(chuàng)建出新的類型,比如嗦篱,我們可以通過(guò)type()函數(shù)創(chuàng)建出Hello類冰单,而無(wú)需通過(guò)class Hello(object)...的定義
正常情況下,我們都用class Xxx...來(lái)定義類灸促,但是诫欠,type()函數(shù)也允許我們動(dòng)態(tài)創(chuàng)建出類來(lái)
>>> def fn(self, name='world'): # 先定義函數(shù)
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 創(chuàng)建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
- 如果我們想創(chuàng)建出類呢?那就必須根據(jù)metaclass創(chuàng)建出類浴栽,所以:先定義metaclass荒叼,然后創(chuàng)建類。連接起來(lái)就是:先定義metaclass典鸡,就可以創(chuàng)建類被廓,最后創(chuàng)建實(shí)例。
# metaclass是類的模板萝玷,所以必須從`type`類型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
閉包
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))
這個(gè)例子中嫁乘,函數(shù)line與環(huán)境變量a,b構(gòu)成閉包。
所謂的閉包是一個(gè)包含有環(huán)境變量取值的函數(shù)對(duì)象球碉。
python內(nèi)存管理
- Python的內(nèi)置函數(shù)id()蜓斧。它用于返回對(duì)象的身份(identity)。其實(shí)睁冬,這里所謂的身份挎春,就是該對(duì)象的內(nèi)存地址。
a = 1
b = 1
print(id(a)) #一樣的
print(id(b)) #一樣的
- 為了檢驗(yàn)兩個(gè)引用指向同一個(gè)對(duì)象痴突,我們可以用is關(guān)鍵字搂蜓。is用于判斷兩個(gè)引用所指的對(duì)象是否相同。
# True
a = 1
b = 1
print(a is b)
# True
a = "good"
b = "good"
print(a is b)
# False
a = "very good morning"
b = "very good morning"
print(a is b)
# False
a = []
b = []
print(a is b)
在Python中辽装,每個(gè)對(duì)象都有存有指向該對(duì)象的引用總數(shù)帮碰,即引用計(jì)數(shù)(reference count)。
我們可以使用sys包中的getrefcount()拾积,來(lái)查看某個(gè)對(duì)象的引用計(jì)數(shù)殉挽。當(dāng)Python運(yùn)行時(shí),會(huì)記錄其中分配對(duì)象(object allocation)和取消分配對(duì)象(object deallocation)的次數(shù)拓巧。當(dāng)兩者的差值高于某個(gè)閾值時(shí)斯碌,垃圾回收才會(huì)啟動(dòng)。
Python將所有的對(duì)象分為0肛度,1傻唾,2三代。所有的新建對(duì)象都是0代對(duì)象。當(dāng)某一代對(duì)象經(jīng)歷過(guò)垃圾回收冠骄,依然存活伪煤,那么它就被歸入下一代對(duì)象。垃圾回收啟動(dòng)時(shí)凛辣,一定會(huì)掃描所有的0代對(duì)象抱既。如果0代經(jīng)過(guò)一定次數(shù)垃圾回收,那么就啟動(dòng)對(duì)0代和1代的掃描清理扁誓。當(dāng)1代也經(jīng)歷了一定次數(shù)的垃圾回收后防泵,那么會(huì)啟動(dòng)對(duì)0,1蝗敢,2捷泞,即對(duì)所有對(duì)象進(jìn)行掃描。
import gc
gc.set_threshold(700, 10, 5)
# 第一個(gè)是多少個(gè)對(duì)象開(kāi)始一次回收前普,第二個(gè)是0代10次1代一次肚邢,第三個(gè)是1代5次2代一次
為了回收這樣的引用環(huán),Python復(fù)制每個(gè)對(duì)象的引用計(jì)數(shù)拭卿,可以記為gc_ref骡湖。假設(shè),每個(gè)對(duì)象i峻厚,該計(jì)數(shù)為gc_ref_i响蕴。Python會(huì)遍歷所有的對(duì)象i。對(duì)于每個(gè)對(duì)象i引用的對(duì)象j惠桃,將相應(yīng)的gc_ref_j減1浦夷。在結(jié)束遍歷后,gc_ref不為0的對(duì)象辜王,和這些對(duì)象引用的對(duì)象劈狐,以及繼續(xù)更下游引用的對(duì)象,需要被保留呐馆。而其它的對(duì)象則被垃圾回收肥缔。
JAVA則是從棧出發(fā),所有沒(méi)被引用到的都會(huì)被刪除汹来。
第三章:進(jìn)程和線程
多進(jìn)程
multiprocessing模塊就是跨平臺(tái)版本的多進(jìn)程模塊续膳。
一種方法是os.fork(),一種方法如下
from multiprocessing import Process
import os
# 子進(jìn)程要執(zhí)行的代碼
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
- 大量進(jìn)程時(shí)用進(jìn)程池
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
如果子進(jìn)程不是自身,而是一個(gè)外部進(jìn)程收班,則需要
import subprocess
進(jìn)程間通信
Process之間肯定是需要通信的坟岔,操作系統(tǒng)提供了很多機(jī)制來(lái)實(shí)現(xiàn)進(jìn)程間的通信。Python的multiprocessing模塊包裝了底層的機(jī)制摔桦,提供了Queue社付、Pipes等多種方式來(lái)交換數(shù)據(jù)。我們以Queue為例,在父進(jìn)程中創(chuàng)建兩個(gè)子進(jìn)程瘦穆,一個(gè)往Queue里寫(xiě)數(shù)據(jù)纪隙,一個(gè)從Queue里讀數(shù)據(jù)。
多線程
在Python中扛或,可以使用多線程,但不要指望能有效利用多核碘饼。如果一定要通過(guò)多線程利用多核熙兔,那只能通過(guò)C擴(kuò)展來(lái)實(shí)現(xiàn),不過(guò)這樣就失去了Python簡(jiǎn)單易用的特點(diǎn)艾恼。
Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù)住涉,但可以通過(guò)多進(jìn)程實(shí)現(xiàn)多核任務(wù)。多個(gè)Python進(jìn)程有各自獨(dú)立的GIL鎖钠绍,互不影響舆声。
ThreadLocal
- 一個(gè)ThreadLocal變量雖然是全局變量,但每個(gè)線程都只能讀寫(xiě)自己線程的獨(dú)立副本柳爽,互不干擾媳握。ThreadLocal解決了參數(shù)在一個(gè)線程中各個(gè)函數(shù)之間互相傳遞的問(wèn)題。
分布式
Python的multiprocessing模塊不但支持多進(jìn)程磷脯,其中managers子模塊還支持把多進(jìn)程分布到多臺(tái)機(jī)器上蛾找。一個(gè)服務(wù)進(jìn)程可以作為調(diào)度者,將任務(wù)分布到其他多個(gè)進(jìn)程中赵誓,依靠網(wǎng)絡(luò)通信。由于managers模塊封裝很好,不必了解網(wǎng)絡(luò)通信的細(xì)節(jié)乖仇,就可以很容易地編寫(xiě)分布式多進(jìn)程程序扔傅。
舉個(gè)例子:如果我們已經(jīng)有一個(gè)通過(guò)Queue通信的多進(jìn)程程序在同一臺(tái)機(jī)器上運(yùn)行,現(xiàn)在诡蜓,由于處理任務(wù)的進(jìn)程任務(wù)繁重熬甫,希望把發(fā)送任務(wù)的進(jìn)程和處理任務(wù)的進(jìn)程分布到兩臺(tái)機(jī)器上。怎么用分布式進(jìn)程實(shí)現(xiàn)万牺?
原有的Queue可以繼續(xù)使用罗珍,但是,通過(guò)managers模塊把Queue通過(guò)網(wǎng)絡(luò)暴露出去脚粟,就可以讓其他機(jī)器的進(jìn)程訪問(wèn)Queue了覆旱。
第四章:錯(cuò)誤處理
單元測(cè)試
- 為了編寫(xiě)單元測(cè)試,我們需要引入Python自帶的unittest模塊核无。
文檔測(cè)試
就是將測(cè)試直接寫(xiě)在類的定義中扣唱,又可以當(dāng)示例,又可以直接運(yùn)行測(cè)試。
doctest非常有用噪沙,不但可以用來(lái)測(cè)試炼彪,還可以直接作為示例代碼。通過(guò)某些文檔生成工具正歼,就可以自動(dòng)把包含doctest的注釋提取出來(lái)辐马。用戶看文檔的時(shí)候,同時(shí)也看到了doctest局义。
第五章:科學(xué)計(jì)算
numpy
# 多維數(shù)組切片
a = np.array([[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28 ,29, 30],
[31, 32, 33, 34, 35]])
print(a[0, 1:4]) # >>>[12 13 14]
print(a[1:4, 0]) # >>>[16 21 26]
print(a[::2,::2]) # >>>[[11 13 15]
# [21 23 25]
# [31 33 35]]
print(a[:, 1]) # >>>[12 17 22 27 32]
# 數(shù)組屬性
print(type(a)) # >>><class 'numpy.ndarray'>
print(a.dtype) # >>>int64
print(a.size) # >>>25
print(a.shape) # >>>(5, 5)
print(a.itemsize) # >>>8
print(a.ndim) # >>>2
print(a.nbytes) # >>>200
scipy
- SciPy是一款方便喜爷、易于使用、專為科學(xué)和工程設(shè)計(jì)的Python工具包.它包括統(tǒng)計(jì),優(yōu)化,整合,線性代數(shù)模塊,傅里葉變換,信號(hào)和圖像處理,常微分方程求解器等等
from scipy.optimize import leastsq
plsq = leastsq(residuals, p0, args=(y1, x))
theano快速入門
常用的數(shù)據(jù)類型
數(shù)值:iscalar(int類型的變量)萄唇、fscalar(float類型的變量)
一維向量:ivector(int 類型的向量)檩帐、fvector(float類型的向量)、
二維矩陣:fmatrix(float類型矩陣)另萤、imatrix(int類型的矩陣)
三維float類型矩陣:ftensor3
四維float類型矩陣:ftensor4
定義函數(shù)湃密,求偏導(dǎo)數(shù)
# -*- coding: utf-8 -*-
import theano
import numpy as np
x = theano.tensor.fscalar('x')#聲明一個(gè)int類型的變量x
y = theano.tensor.pow(x,3)#定義y=x^3
f = theano.function([x],y)#定義函數(shù)的自變量為x(輸入),因變量為y(輸出)
print f(2)#計(jì)算當(dāng)x=2的時(shí)候四敞,函數(shù)f(x)的值
dx = theano.grad(y,x) # 偏導(dǎo)數(shù)函數(shù)
f2 = theano.function([x],dx) # 定義函數(shù)f泛源,輸入為x,輸出為s函數(shù)的偏導(dǎo)數(shù)
共享變量
- 在程序中目养,我們一般把神經(jīng)網(wǎng)絡(luò)的參數(shù)W俩由、b等定義為共享變量,因?yàn)榫W(wǎng)絡(luò)的參數(shù)癌蚁,基本上是每個(gè)線程都需要訪問(wèn)的幻梯。
import theano
import numpy
A=numpy.random.randn(3,4);#隨機(jī)生成一個(gè)矩陣
x = theano.shared(A)#從A,創(chuàng)建共享變量x
print x.get_value() #通過(guò)get_value()努释、set_value()可以查看碘梢、設(shè)置共享變量的數(shù)值。
#coding=utf-8
import theano
w= theano.shared(1)#定義一個(gè)共享變量w伐蒂,其初始值為1
x=theano.tensor.iscalar('x')
f=theano.function([x], w, updates=[[w, w+x]]) #定義函數(shù)自變量為x煞躬,因變量為w,當(dāng)函數(shù)執(zhí)行完畢后逸邦,更新參數(shù)w=w+x
print f(3)#函數(shù)輸出為w
print w.get_value()#這個(gè)時(shí)候可以看到w=w+x為4
邏輯回歸中求導(dǎo)
g_W = T.grad(cost=cost, wrt=classifier.W)
g_b = T.grad(cost=cost, wrt=classifier.b)
updates = [
(param, param - learning_rate * gparam)
for param, gparam in zip(classifier.params, gparams)
]
然后train的過(guò)程中就可以更新了
theano寫(xiě)神經(jīng)網(wǎng)絡(luò)
- 構(gòu)建從輸入到輸出的數(shù)學(xué)表達(dá)式
- 定義好loss func
- 寫(xiě)好update
- 準(zhǔn)備好輸入輸出
- 然后就可以train了恩沛,反向傳導(dǎo)全部都交給theano.tensor.grad