基礎
同所有的語言一樣,Python都有它定義類型的方式,引入第三方庫的方式,輸入輸出等等基本操作。這些可以在這里查到。
閑話
import
的時候會執行模塊內容,比如里面要是有print會輸出
用if __name__ == __main__
來區分是否是主模塊,還只是非主模塊。
is 和 == 區別
is
用于判斷兩個變量引用對象是否為同一個, ==
用于判斷引用變量的值是否相等。
內置函數參考一覽
字符串之中文處理“解決中文亂碼問題”
#!/usr/local/bin/python
#encoding:utf-8 | #-*-coding:utf8-*- | coding:utf8
1. 簡介
字符串的編碼是將人能看懂的字符轉化成只有機器能看懂的01機器碼,于是這里就牽扯了字符串的編碼問題了。這個問題為什么要單獨拿出來說呢?那是因為在不同的編碼的環境下,中文字符都可能亂碼,如果你的環境是`utf-8`的環境,而終端是`GBK`環境,那么兩個環境之一必將亂碼。這是一個比較頭疼的現象。存文件的時候也是,不同的編碼最終存的機器碼是不同的。
**注:**Python2中支持str和unicode,而Python3中**只支持**unicode。
2. 編碼的演變方式如下:
ASCII -> ISO-8859-1 -> GB2 -> GBK -> GB18030
#ASCII是一個字節編碼一個字符只支持英文名,
#ISO-8859是對ASCII的一個拓展,還是一個字節編碼一個字符,支持西歐的國家
#帶GB的都是支持中文的,兩字節編一個字,GB的互相兼容
unicode -> UTF-8 -> UTF-32
#之后的統一是unicode,但是存儲效率太低。
#對unicode進行壓縮后是UTF-8和UTF-32。
3. 解決方案
import sys
print sys.stdin.encoding
print sys.stdout.encoding
x = raw_input(u'請輸入名字:'.encode(sys.stdout.encoding))
f = open('1.txt','w')
f.write(x.decode(sys.stdin.encoding).encode('utf-8'))
f.close()
#這樣子我們就讓文字在不同的環境能正確顯示。
#同時用解碼和編碼的方式把存下來的內容按UTF-8的方式存儲下來了
變量作用域
1. LEGB原則
'''python的變量按照以下的順序進行搜索
L(local):函數本地
E(enclose):任意上層的嵌套函數
G(global):全局作用域(模塊,文件內)
B(build-in):內置作用域(夸文件)
'''
x = 10
def fun():
global x
x += 2
pass
#這樣子就在函數里面調用了函數外的變量
函數的嵌套和閉包
閉包:能夠保留函數定義時的環境信息
嵌套:內部函數用了外部函數的變量,或者外部函數返回了內部函數
def f(x):
y = 100
def inner(z):
return x * y + z
return inner
a10 = f(10)#在創建的時候,給f這個函數的x賦值了10
print(a10(29)) #在調用函數內部函數inner輸出結果為1029
#這時候我們觀察到本來應該在調用后消失的x的值,在下面一步調用中還是保留著的。
#這種情況我們稱之為閉包,即在a10創建的時候保存了定義時的環境變量
#這種做法我們能夠更靈活的使用函數
生成器
生成器的定義為:能生成迭代器的東西
生成器表達式:
(expr for iter in iterable [if condition])
生成器函數:
如果函數的返回值用的不是return
而是yield
則是生成器函數,能用for
函數使用。
def inc(n = 1):
while True:
yield n
n += 1
a = inc()
print(a.next())
#可以變相的認為在yield的地方被打了一個端點。
#用yield放回的時候,函數的堆棧還在保存著沒有銷毀
Why yield?
當一個函數 f,f 返回一個結果,而這個結果是動態計算出來的,同時這個結果如果很大。這個時候,我們希望每次調用這個函數并使用迭代器進行循環的時候一個一個的得到每個 list 元素而不是直接得到一個完整的結果來節省內存,這個時候yield就很有用。
打個比方的話,yield有點像斷點。 加了yield的函數,每次執行到有yield的時候,會返回yield后面的值 并且函數會暫停,直到下次調用或迭代終止;
yield后面可以加多個數值(可以是任意類型),但返回的值是元組類型的。
具體怎么使用 yield 參考:Python yield 使用淺析
集合操作(這個在其他語言中不常見特此記錄)
1. Python中的集合分類
set #可變集合
frozenset #不可變集合,能hash
2. 運算
s | t #并集
s & t #交集
s - t #差集
s ^ t #對稱差集,拋去相交的剩下的
s |= t #把t并入s
s < t #看s是否為t的子集
s > t #看s是否為t的超集,即t包含于s
s.isdisjoint(t) #看s和t是否有交集
然后以下是可變集合特有的操作
python s.add(item) s.clear() s.dicard(item) && s.remove(item) #都是刪除操作,如果不存在,remove會拋出異常,discard不會拋出異常 s.update(t) #和t對比,把沒有的追加進來 s.difference_update(t) #在s中刪除和t交集的部分
裝飾器
本質是函數的嵌套調用
目的是不破壞原有的代碼的前提下,進行調試
#運行邏輯如下
#decorator(f)(*args,**kwargs)
@decorator
def f(): pass
#這個deorator可以換成任何名字
#decorator(name)(f)(*args,**kwargs)
@decorator(name)
def f():pass
#帶參數的函數
def log(func):
def wrapper(*args, **kwargs): #這么寫是可以通配所有的函數
print('~'*40)
start = time.clock()
res = func(*args, **kwargs)
end = time.clock()
print('calling',func.__name__,args,kwargs)
print('start at',start,' end at',end)
return wrapper
#以上就是對裝飾器的定義
def logEx(name):
def wrapper(func):
def wrapper1(*args,**kwargs):
print('~'*40)
start = time.clock()
res = func(*args, **kwargs)
end = time.clock()
print(name,'calling',func.__name__,args,kwargs)
print('start at',start,' end at',end)
return wrapper1
return wrapper
@logEx('Tom')
@log
#裝飾器可以連續插入新的功能,不會影響原來的業務功能
def f(x,y):
return x+y
#這樣子返回的是測試者姓名,函數的輸入參數,還有輸出的結果
列表和列表解析(常用的操作這里不說,僅記錄自己不太熟悉的操作)
1. 列表常用操作
append #加入元素
extend #合并列表
del #刪除
enumerate(iter) #返回一個迭代器,會返回索引和值
"""
example for enumerate
for (index, value) in enumerate(itr):
print index,value
"""
len #返回長度
reversed #反置操作
sorted(x,key=abs) #排序操作,高度可定制
sum #求和操作
2. 列表解析
列表解析是根據一個列表生成一個新的列表,幾乎可以和for循環等價,但是它的優點為代碼緊湊。同時性能更好。
[expr for iter in iterable [if condition]]
"""
result = [i**2 for in x if i<0]
result = [i**2 if i<0 else i for in x]
"""
迭代器
- 可迭代對象(Iterable):
如果一個對象可以用for ... in ...
的方式遍歷內容,則其為可迭代 - 迭代器(Iterator):
遍歷可迭代對象內容的方式 - itertools:
- permutations:排列
cite = itertools.permutations(x,3)
for c in cite:
print(c)
#對x里面的內容進行3個3個進行排列
- combinations:組合
itertools.combinations(x,3)
#對x里面的內容進行3個3個進行組合
- product:笛卡爾積
z = ['a','b','c']
y = range(1,4)
a = itertools.product(z,y)
for i in a:
print(i)
- repeat:重復
- chain:鏈接一組迭代器
#把多個迭代器塞在一起 itertools.chain(citer,piter,priter)
面向對象編程思想
面向對象編程的特點
1. 封裝
相當于一個黑匣子,功能的實現過程封裝了進去。
2. 繼承
字面意思,就是從父類中繼承了他們的功能。
3. 多態
繼承了不同的父類,雖然他們的是同一個類但是由于繼承了不同的父類,呈現了不同的功能,呈現了多樣性。
類的簡單說明
1. 經典類
2. 新式類
`__new__`
`__super`
`__slot__`
類的使用
class A:
pass
#不繼承任何類
class B(object):
pass
#繼承的類向上推最終為object的話,這個類就是新式類
class D(B):
pass
#如D類也是新式類
類的定義
1. 屬性
class Chinese(Person):
nation = 'China'
def __init__(self, name):
self.__name = name
#self指的是這個類自己,同理java和Cpp的this
#前后都有`__`的是已經有的方法
#用self來操作的屬性是對類里面的屬性進行操作的。
#屬性前面有`__`的屬性是私有屬性,沒有的話是公有屬性
def msg(self):
print(self.name)
a = Chinses(myname) #這樣創建實例
a.__dict__ #來看有哪些屬性
a.nation = 'blabla' #這樣子來修改
a._Chinese__name = 'blabla'
#這個證明用`__`定義的私有屬性只是改了名字來保護屬性,但是其實是有規律的
類屬性:
這個是類本身的屬性,在創建實例的時候不需要單獨在實例分配內存的屬性。如上述例子中的nation
屬性。如果修改了類的這個屬性,那么所有用這個類創建的實例中的這個屬性都會被更改。
實例屬性:
在__init__
初始函數中定義的屬性是實例屬性。這個屬性是在類創建實例的時候生成的屬性,這個是要單獨分配內存的,而不是類本身就有的屬性。
2. 屬性的可見性
在其他語言如`Cpp`和`Java`中都有特定的結構如`private`和`protected`來定義,但是python其實是沒有底層對他們進行實現的,所以python的共有屬性和私有屬性**是偽的**,所有的都可以用`__dict__`方式進行查到的。
**公有屬性**
**私有屬性 **:
我們在屬性前加`_`或者`__`來標記這個是私有屬性。
- `__`定義的屬性:
是會被python**通過改名換姓的方式進行保護。**
- `_`定義的屬性:
只是**形式上的私有屬性**,提醒你這個是私有屬性不要嘗試去直接訪問它。
3. 訪問屬性
直接訪問:
這個不是一個好的方式,因為違背了封裝性。壞處是如果類的屬性名稱進行了更改,那所有直接訪問這個屬性的代碼都需要更改。
定義get
,set
方法:
這個方法就是Cpp
和Java
中常用的方法。
通過屬性裝飾器
- @property
:屬性的查看
- @***.setter
:屬性的寫入
- @***.deleter
:屬性的刪除
class Chinese(Person):
nation = 'China'
def __init__(self, id, name):
self.__name = name
self.__id = id
slef.__email = None
def getID(self):
print(self.__id)
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
@name.deleter
def name(self):
del self.name
VDeamoV = Chinese(1,'VDeamoV')
print(VDeamoV.getID())
VDeamoV.name = 'Other Name'
print(VDeamoV.name)
#這里替換了并輸出了保護的屬性,但是沒有用類中的名字
這個好處就是,使用的人調用的時候看起來就像函數調用的感覺,而不是調用函數的感覺。
4. 通過描述符訪問
編寫類的時候,如果類滿足了__get__
、__set__
和__del__
這三個方法就可以稱之為描述符。
描述符的兩個目的:
- 代碼重用
- 描述符屬性必須定義成類屬性
class Property(object):
def __init__(self, propname, datatype, default = None):
self._name ='_' + propname + '_'
#這個在最終在實例中的屬性是什么名字
self._type = datatype
self._default = default if default else self._type()
def __get__(self, instance, owner):
return getattr(instance, self._name, self._default)
#getattr是python內置的獲取屬性的值,如果沒有是缺省值
pass
def __set__(self, instance, value):
if not isinstance(value, self._type):
raise TypeError('TypeError, must be %s type0' self._type)
#isinstance()是判斷是不是一個類型的,raise是拋出異常
setattr(instance, self._name, value)
#只有這個執行之后,才會真的在實例中創建屬性
pass
def __del__(self):
pass
class Email(Property):
def __init__(self, propname, default = None):
pass
def __set__(self, instance, value):
pass
class Chinese(object):
ID = Property('id', int)
Name = Property('name', str)
Email = Email('email')
def __init__(self, id, name, email):
self.ID = id
self.Name = name
self.Email = email
VDeamoV = Chinese() #這時候創建Chinese類的時候沒有任何屬性
VDeamoV.ID = 123
VDeamoV.Name = 'test'
VDeamoV.Email = 'asd@bupt.edu.cn'
類的方法
1. 實例方法
- 第一個參數是self
2. 類方法
@classmethod
- 第一個參數是
cls
,綁定到類
這個cls
也是它和靜態方法的區別,它可以直接用cls
來調用自己的類本身而不是向靜態方法一樣將返回的類寫死,很大的區別會體現在繼承類的時候返回的類的類型,見下方例子的注釋說明
注意:
Python不支持多態,即不支持函數名字相同但是變量不同,但是Cpp
支持。
3. 靜態方法
@staticmethod
- 和普通函數一樣,無綁定
4. 特殊方法
- 一些內置的如
__init__
這些函數。
class Test():
def Hi(self):
pass
#實例方法
#中間略
@staticmethod
def getPeopleByParents(father, mother):
print(father, mother)
return Chinese(10,'father'+'_son','mother'+'_son')
#靜態方法
@classmethod
def getPeopleBySibling(cls, sibling):
print sibling
return cls(10, 'TestName', 'blabla')
#類方法
class Test2(Test):
pass
a = Test2.getPeopleByParents('blabla', 'blablabla')
b = Test2.getPeopleBySibling('blabls')
#這時候a類的類型為Chinese,而b類的類型為Test2
運算符重載
1. 構造函數、析構函數
-
__new__
:和__init__
的區別在它是在創建內存空間的時候調用 -
__init__
:是在初始化的時候才會調用 -
__del__
:刪除或者回收的時候調用的函數
2. 四則運算
__add__
__sub__
__mul__
__div__
3. 比較
__lt__
__gt__
__cmp__
4. 其他
__str__
__repr__
__contains__
__contains__
__bool__
class Test():
def __str__(self):
return "Here is the Return"
def __repr__(self):
return "ReprTest"
def __add__(self, other):
return self.ID + other.ID
def __lt__(self, other):
return self.ID < other.ID
a = Test()
print(a) #得到的結果是"Here is the Return"這個函數
a
#直接敲入對象名稱的時候輸出的內容由repr來決定
繼承
定義:根據已有的類生成新的類
目的:代碼重用、多態
class A(object):
def __init__(self):
print("In class A")
pass
class B(A):
pass
b = B()
#這個在構造類B的時候繼承了類A,所以會執行類A構造的時候的__init__
#不過如果類B自身有__init__的話,就相當于對類A中的__init__進行了重載
#針對第二行注釋說的情況如果我們還想使用A的__init__可以用A.__init__的方式
1. 菱形繼承
多重繼承中出現的問題,當出現菱形的多重繼承的時候,函數名解析的問題
- 經典類:深度優先
- 新式類:廣度優先
解決這種情況的方案就是使用class A(object): def __init__(self): print("In class A") pass class B(A): def __init__(self): A.__init__(self) print("In B") class C(A): def __init__(self): A.__init__(self) print("In C") class D(B,C): def __init__(self): B.__init__(self) C.__init__(self) print("In D") pass d = D() #由于繼承的時候,有兩個相同名字的函數,到底會繼承哪個 #在執行的時候,我們會發現這么調用父函數的話,A類會被執行2遍 #這樣子的話可能會出現問題
super
調用父類的函數的時候,推薦都適用super調用,這樣子不容易出現問題。但是class A(object): def __init__(self): print("In class A") pass class B(A): def __init__(self): #A.__init__(self) super(B,self).__init__() print("In B") class C(A): def __init__(self): #A.__init__(self) super(C,self).__init__() print("In C") class D(B,C): def __init__(self): #B.__init__(self) #C.__init__(self) super(D,self).__init__() print("In D") pass
super
只支持新式類。
2. inspect.getmro(class)
在1中我們知道了,同名函數繼承和調用的時候是有一個查找順序的,除了死記住一個固有的順序,我們還可以用`inspect`包中的`getmtro`函數。
```python
import inspect
inspect.getmro(D)
```
多線程和多進程
線程和進程的定義
可以參考王道考研操作系統的線程和進程的定義。
1. 進程具有獨立功能的程序
進程是系統進行資源分配和調度的一個獨立單位。線程是進程的一個實體,是CPU調度的基本單位,它是比進程更小的能獨立運行的基本單位。
2. 進程擁有獨立的地址空間
一個進程崩潰后,不會對其他進程產生影響。線程沒有單獨的地址空間,一個線程死掉等于整個進程死掉。所以多進程的程序比多線程的程序魯棒性要好。
3. GIL和多線程
- Global Interpreter Lock
- CPU密集型
因為是假的多線程,所以CPU其實沒有啟動多個CPU去運行這些線程,所以效果不是很好。 - IO密集型
雖然在CPU分配的方面,線程的優勢其實是沒有得到很好的效果,但是進程的概念中,還是解決了IO交互速度和CPU運算速度不匹配的問題。所以在這種情況下使用多線程還是有意義的。
- CPU密集型
多線程模塊
1. thread模塊:便底層
2. threading模塊:
相對高層,提供RLock
, Lock
等同步機制。
threading的實現細節
繼承threading.Thread
,實現run()
方法。
-
join()
:掛起當前線程,直到被調用線程結束 -
start()
:啟動線程,執行run
中的代碼 -
is.Alive()
:判斷線程是否已經結束。
線程中的鎖
threading.Lock
threading.RLock
-
acquire()
:申請鎖 -
release()
:釋放鎖
import threading
class A(threading.Thread):
def __init__(self, n):
threading.Thread.__init__(self)
#一定要引入這個父類的初始化
self._n = n
def run(self):
while True:
print('in thread %s' % self._n)
time.sleep(1)
pass
if __name__ == '__main__':
mt = [A(i) for i in range(4)]
for t in mt:
t.start()
for t in mt:
t.join()
#運行結果會出現一行多個'in thread',
#原因是,多個線程打印的結果被同時扔進了輸出緩存區,
#這樣可以看出print操作其實不是一個原子操作
CPU密集的實例
from faker import Factory
faker =. Factroy.create()
cnt = 10000
x1 = [faker.paragraph() for i in range(cnt)]
x2 = [faker.paragraph() for i in range(cnt)]
import time
start = time.clock()
for one in x1:
len(one)
for one in x2:
len(one)
end = time.clock()
print('time: %s' % (end - start))
import threading
class A(threading.Thread):
def __init__(self, x):
threading.Thread.__init__(self)
self.x = x
def run(self):
for each in self.x:
len(each)
t1 = A(x1)
t2 = A(x2)
start1 = time.clock()
t1.start()
t2.start()
t1.join()
t2.join()
##等t1,t2都完成了才記錄
end1 = time.clock()
print("Thread time: %s" % (end1 - start1))
隨著數據的加大,(CPU密集操作的)單線線程的版本反而顯著優于多線程
多進程
- 多個python進程 = 多個GIL鎖
- multiprocessing模塊
在python中,多進程的使用是可以充分利用多核CPU,但是進程的切換的開銷往往是非常大的。
multiprocessing.Process的細節實現
-
join()
:掛起當前進程,直到被調用進程結束。 -
start()
:啟動進程,執行run
中的代碼
進程間的通信
- 進程的內存空間是獨立的,所以是沒有鎖的概念。
- 通過multiprocessing.Queue實現通信
Queue.put()
Queue.get()
import multiprocessing
class A(multiprocessing.Process):
def __init__(self, n):
multiprocessing.Process.__init__(self)
#一定要引入這個父類的初始化
self._n = n
def run(self):
while True:
print('in processing %s' % self._n)
time.sleep(1)
pass
if __name__ == '__main__':
mt = [A(i) for i in range(4)]
for t in mt:
t.start()
for t in mt:
t.join()
#這個時候在進程里面看,會發現出現了5個進程
進程的通信案例,生產者和消費者
import multiprocessing
import time
class Producer(multiprocessing.Process):
def __init__(self, q):
multiprocessing.Process.__init__(self)
self._q = q
def run(self):
while True:
self._q.put('Time is %s' % time.time())
pass
clas Consumer(multiprocessingl.Process):
def __init__(self, q, n):
multiprocessing.Process.__init__(self)
self._q = q
self._n = n
def run(self):
while True:
msf = None
try:
msg = self._q.get()
except:
time.sleep(1)
continue
if msg:
print('in consumer %s' % (self._n,msg))
pass
if __name__ == '__main__':
q = multiprocessing.Queue()
producer = Producer(q)
c1 = Consumer(q, 1)
c2 = Cousumer(q, 2)
producer.start()
c1.start()
c2.start()
結語
此次的Python個人總結,由于Python的教程實在非常多,盡量省去了那些耳熟能詳的特別容易就能學會的內容,如循環和創建函數類等等。本文挑選的為就目前個人而言,覺得對于初學者比較不常用且重要的內容進行記錄。內容的整理來自于目前自身報名的網課“北風網”的課程學習,不過目前不是很推薦大家去學。
最后,文章內容有點雜亂,希望能得到大家諒解。