python語言的特點
- 解釋性語言,無需編譯
- 語法簡單
- 開源,模塊豐富
深淺拷貝的區別
Python 中的copy.copy()和 copy.deepcopy()實現了深淺拷貝
import copy
a = [[1,2], 'gly', 666]
b = copy.copy(a)
c = copy.deepcopy(a)
print(b)
print(c)
print(id(a[0]))
print(id(b[0]))
print(id(c[0]))
print('-------')
print(id(a[1]))
print(id(b[1]))
print(id(c[1]))
a[0][0] = 0
a[1] = 'gly qaq'
print('-------')
print(id(a[1]))
print(id(b[1]))
print(id(c[1]))
print(id(a[0]))
print(id(b[0]))
print(id(c[0]))
print(b)
print(c)
[[1, 2], 'gly', 666]
[[1, 2], 'gly', 666]
1508853212808
1508853212808
1508853213320
-------
1508853208624
1508853208624
1508853208624
-------
1508853207216
1508853208624
1508853208624
1508853212808
1508853212808
1508853213320
[[0, 2], 'gly', 666]
[[1, 2], 'gly', 666]
可以發現,淺拷貝是對拷貝對象地址指針的引用,當copy對象的可變對象的值發生改變時,當前對象的值也發生了改變。
對於深拷貝來說,對於copy對象的每一個可變對象都建立了一個新的副本,源數據的改變並不會應到到深拷貝對象的值。
當然,對於那些不可變對象,如int,string,float,tuple等,深淺拷貝並無區別。
Python中的三元表達式
python中的三元表達式的結構爲:
res = ‘變量1’ if 條件 else ‘變量2’
如果條件爲True 則 res 賦值爲變量1 ,否則爲變量2
Python中多線程的原理和實現
-
什麼是線程
線程是處理器進行調度的基本單元 -
與進程的區別
一個進程可以擁有多個線程,進程是線程的容器,進程是資源分配的最小單位,而線程是處理器調度的基本單元 -
Python中的多線程模塊 threading.Thread
-
線程之間的同步
- 可以使用Lock 或者RLock 來實現需要注意的是,Lock 只能acquire一次, Rlock可以acquire多次,但是兩者releae的次數都要和acquire的次數相等。
- queue來實現,queue.Queue 中的 get和 task.done()類似於 acquire 和 release
-
由於全局解釋器鎖的存在 GIL Python 中同時只能由一個線程在運行(我們看起來像是並行)。CIL並不是Python語言的特性,Python也完全可以不依賴於GIL。
Python中的匿名函數 lambda
b = lambda x:x+1
print(b(1))
2
可以看出來,匿名函數就像是函數的簡單寫法,前一個x爲參數,後一個x爲返回值。
另一個使用匿名函數的簡單例子
將數組裏的值都加1
a = [1,2,3,4,5]
b = list(map(lambda x:x+1, a))
print(b)
[2, 3, 4, 5, 6]
說明: map函數接收兩個參數,前一個爲函數,後一個爲可迭代對象,對象裏的每個值都執行一次lambda函數
Python 中的列表推導式
語法:
a = [表達式 for 變量 in 列表 ]
Python 中的生成器和迭代器(重要)
迭代器
1. 迭代是訪問集合對象的一種方式
2. 迭代器可以記住訪問對象的位置
3. 迭代器從訪問對象的第一個位置開始訪問,只能前進不能後退
4. 迭代器有兩個基本的方法,iter()和next()
創建一個迭代器
list = [1,2,3,4,5]
it = iter(list)
print(next(it))
print(next(it))
1
2
iter()方法創建一個迭代器對象,next()用於訪問迭代器的下一個元素。
使用for循環遍歷
list = [1,2,3,4,5]
it = iter(list)
for i in it:
print(i)
1
2
3
4
5
# 遍歷字典
dic = {
1 :'gly',
2 : 'gly 666'
}
for i in iter(dic):
print(i,dic[i])
1 gly
2 gly 666
創建一個迭代器
- 把一個類作爲迭代對象需要在類中實現iter()方法和 next()方法
- iter 方法返回一個迭代器對象,這個迭代器對象實現了next方法,並且通過StopIteration標識迭代完成
- next 方法,返回對象的值,並讓當前的迭代器指向下一個對象(其實就是根據規則更新對象的內容)。
具體實現
class test():
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = test()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
1
2
上述代碼如果用for循環去遍歷的話就會無限的迭代下去,如果是迭代停止呢?
StopIteration 異常用來標識一個迭代的終點
class test():
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
if x <= 5:
self.a += 1
return x
else:
raise StopIteration
myclass = test()
myiter = iter(myclass)
while True:
try:
print(next(myiter))
except:
print('stopiteration')
break
1
2
3
4
5
stopiteration
這種寫法寫起來比較麻煩,for循環能夠幫助我們判斷sotpiteration異常
class test():
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
if x <= 5:
self.a += 1
return x
else:
raise StopIteration
myclass = test()
myiter = iter(myclass)
for i in myiter:
print(i)
1
2
3
4
5
生成器
在Python中使用了yield關鍵字的函數稱之爲生成器(generator)
當調用生成器時返回的是一個迭代器對象。簡單理解生成器就是:返回迭代器的函數
在調用生成器的過程中,每次遇到yield的時,函數會保存當前的所有信息並返回yield 的值。直到
下一次調用next函數時,函數接着上次保存的狀態繼續執行,直到遇到下一個yield。
使用yield 實現斐波那契數列
def fib(max):
n, a, b = 0, 1, 1
while(n<max):
a, b = b, a+b
yield b
n += 1
return 'done'
f = fib(5)
print(next(f))
print(next(f))
2
3
使用for循環遍歷
def fib(max):
n, a, b = 0, 1, 1
while (n < max):
a, b = b, a + b
yield b
n += 1
return 'done'
f = fib(5)
for it in f:
print(it)
2
3
5
8
13
但是我們發現上述函數並沒有拿到最終的返回值done
原因是:for循環幫助我們處理了StopIteration 的異常,但是最終的返回值就在StopIteration的value中
def fib(max):
n, a, b = 0, 1, 1
while (n < max):
a, b = b, a + b
yield b
n += 1
return 'done'
f = fib(5)
while True:
try:
print(next(f))
except StopIteration as e:
print(e.value)
break
2
3
5
8
13
done
這樣我們就拿到了,fib函數的返回值。
補充send 的作用
def test():
i = 1
print('aaa')
tmp = yield i*3
print(tmp)
if tmp == 'hello':
print('send hello')
f = test()
try:
print(next(f))
f.send('hello')
except StopIteration as e:
print(e)
aaa
3
hello
send hello
說明:第一次調用next會使生成器執行到tmp==yield 這裏然後掛起,這時候打印出來的值就是3
當我們send的時候,相當於給tmp賦值爲send的內容,並且讓生成器函數繼續執行。
生成器表達式
a = (x * 3 for x in range(5))
print(a)
for i in iter(a):
print(i)
<generator object <genexpr> at 0x0000015F4EB2D3C8>
0
3
6
9
12
生成器表達式用(),他與生成器函數並無二至,只是生成器的兩種寫法。
總結:
- 迭代器 ,任何可迭代對象都有迭代器,如果要實現一個可迭代的類,則需要在類中實現iter方法,和next方法。其中iter方法返回一個迭代器,也就是對象本身。next方法,返回一個值,並且使當前的迭代器指向下一個值。迭代的終點用StopIteration表示,for循環能夠幫助我們處理這個異常,而next方法則需要手動處理。
- 生成器, 當一個函數使用了yield 使。這個函數就是一個生成器函數,簡單理解生成器就是返回迭代器的函數,當我們調用函數名+()時即得到了一個迭代器。當我們遇到yield時會 返回yield的值並記錄當前所有的信息,直到下一次調用next函數時纔回到上一次的位置繼續運行。 我們也可以使用send函數來使生成器函數繼續運行,並且傳入一個值。
- 生成器的使用情景; 一個簡單的使用情景就是使用生成器實現斐波那契數列,我們在實現的過程中並不需要用一個列表把所有的項都記錄下來,而是在求出一項個輸出,然後調用next函數讓生成器函數繼續執行。這樣比較節省內存。
Python中的裝飾器 decorators
什麼是裝飾器
裝飾器就是在不改變原函數(類)名的情況下,給函數添加新的功能
大致看了一下博客,然後再回頭總結。
理解裝飾器之前,首先要知道,python中
- 函數可以嵌套
- 函數的參數可以是一個函數
- 函數的返回值也可以是一個函數
- 函數名就相當於指向該函數的指針,我們是可以改變他的指向,讓他指向另一個函數的。
理解以上三點,就可以實現一個簡單的裝飾器了。
def first_decorator(f):
def wrap():
print('doing something before %s'% f.__name__)
f()
print('doing something after %s'% f.__name__)
return wrap
def foo():
print('i am foo')
foo = first_decorator(foo)
foo()
doing something before foo
i am foo
doing something after foo
這正是裝飾器起到的作用,我們在不修改foo函數的情況下,給foo函數添加了一些其他的功能。
但是這會出現另一個問題
from functools import wraps
def first_decorator(f):
def wrap():
"""
我是wrap的註釋
:return:
"""
print('doing something before %s'% f.__name__)
f()
print('doing something after %s'% f.__name__)
return wrap
def foo():
"""
我是foo的註釋
:return:
"""
print('i am foo')
foo = first_decorator(foo)
foo()
print(foo.__name__)
print(foo.__doc__)
doing something before foo
i am foo
doing something after foo
wrap
我是wrap的註釋
:return:
我們發現foo函數的名字和文檔被wrap替換掉了。
其實這個時候foo函數已經等同於wrap函數了。
如何避免這種情況發生呢。
from functools import wraps
def first_decorator(f):
@wraps(f)
def wrap():
"""
我是wrap的註釋
:return:
"""
print('doing something before %s'% f.__name__)
f()
print('doing something after %s'% f.__name__)
return wrap
def foo():
"""
我是foo的註釋
:return:
"""
print('i am foo')
foo = first_decorator(foo)
foo()
print(foo.__name__)
print(foo.__doc__)
doing something before foo
i am foo
doing something after foo
foo
我是foo的註釋
:return:
只需在wrap前加上@wraps(函數名)即可。
wraps是python functools包中的一個模塊。
@wraps接受一個函數來進行裝飾,並加入了複製函數名稱、註釋文檔、參數列表等等的功能。這可以讓我們在裝飾器裏面訪問在裝飾之前的函數的屬性
同時我們也能用更簡單的一個方法來裝飾一個函數
from functools import wraps
def first_decorator(f):
@wraps(f)
def wrap():
"""
我是wrap的註釋
:return:
"""
print('doing something before %s'% f.__name__)
f()
print('doing something after %s'% f.__name__)
return wrap
@first_decorator
def foo():
"""
我是foo的註釋
:return:
"""
print('i am foo')
foo()
print(foo.__name__)
print(foo.__doc__)
doing something before foo
i am foo
doing something after foo
foo
我是foo的註釋
:return:
在函數foo上面加一個@first_decorator 就相當於實現了
foo = first_decorator(foo)
然後直接調用foo就可以了。
帶參數的裝飾器
from functools import wraps
def first_decorator(f):
@wraps(f)
def wrap(*args, **kwargs):
print('do sth before')
return f(*args, **kwargs)
return wrap
@first_decorator
def foo(*args, **kwargs):
print(args)
print(kwargs)
return args[0]*args[0]
print(foo(4,5,6,name='gly', age=20))
do sth before
(4, 5, 6)
{'name': 'gly', 'age': 20}
16
上面是一個帶參數的裝飾器的例子。
裝飾器的使用藍本
from functools import wraps
def first_decorator(f):
@wraps(f)
def wrap():
if not can_run:
return ("%s cant run"% f.__name__)
else:
return f()
return wrap
@first_decorator
def foo():
return ("foo is running")
can_run = False
print(foo())
can_run = True
print(foo())
foo cant run
foo is running
注意這裏裝飾器中的寫法,return 的是f(),當然這裏可以加一些參數,這裏相當於
執行了f函數,然後把結果返回了。
相當於
res = f()
return res
內置的裝飾器
staticmethod
staticmethod裝飾器用來去掉定義的類方法默認傳入的第一個參數(也就是self),使得該方法與普通的函數一樣,並且不實例化也能調用該方法
class myclass(object):
name = 'gly'
def show(self): # 普通的類方法
print('qaq')
@staticmethod
def show1():
print('hahaha!')
print(myclass.name)
myclass.show1()
gly
hahaha!
name是類屬性,show是類方法,當類沒有實例化爲對象時,我們時不能調用類方法。
但是有時候我們希望運用一些類方法像運用普通的函數一樣,這個時候就需要staticmethod裝飾器
classmethod
類方法裝飾器,當我們爲一個類加上類方法裝飾器時,第一個參數默認是cls,類方法是和類綁定的方法,不是和實例綁定的方法。類方法可以修改類的狀態並應用到實例中。
property
property是一個裝飾器,包含三種方法,deleter,setter,getter,分別對應刪除屬性,設置屬性的值,和取屬性的值。
class student(object):
def get_soc(self):
return self.soc
def set_soc(self, val):
if not isinstance(val, int):
raise ValueError('soc must be int')
if val < 0 or val > 100:
raise ValueError('soc must between 0-100')
self.soc = val
def del_soc(self):
print('del soc')
del self.soc
s = student()
s.set_soc(100)
print(s.get_soc())
s.del_soc()
100
del soc
上述代碼分別對應了設置值,取值和刪除值。但是這樣寫起來比較麻煩,能不能像訪問屬性一樣修改soc,同時又能檢查設置的soc是否合法呢?
class student(object):
@property
def soc(self):
return self._soc
@soc.setter
def soc(self, val):
if not isinstance(val, int):
raise ValueError('soc must be int')
if val < 0 or val > 100 :
raise ValueError('sco must between 0-100')
self._soc = val
s = student()
s.soc = 99 # 相當於 s.set_soc
print(s.soc) # 相當於 s.get_soc
99
這樣,我們在設置_soc的值的時候,即沒有暴漏屬性名,也能夠在設置值的是否檢查是否符合規範。
Python 中的異常處理
Python進階的作者Muhammad Yasoob Ullah Khalid. 說:
異常處理是一門藝術,一旦你掌握,回授予你無窮的力量。
1. try/except 從句。
將可能會除發異常的代碼放在try裏,在except裏邊處理捕捉到的異常
1個簡單的例子
try:
s = 1/0
except ZeroDivisionError as e:
print(e)
division by zero
2. 處理多個異常
try:
f = open('text.txt', 'rb')
except EOFError as e:
print('an error occurred')
except IOError as e:
print('other error occurred!')
other error occurred!
3. 捕獲所有異常
try:
f = open('a.txt')
except Exception:
# 打印異常日誌
raise
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-29-2fe564bb1386> in <module>
1 try:
----> 2 f = open('a.txt')
3 except Exception:
4 raise
FileNotFoundError: [Errno 2] No such file or directory: 'a.txt'
finally 從句
finally從句不管異常是否除發,都會執行,一般用於腳本執行後的收尾工作。
try:
f = open('file.txt')
except Exception as e:
print(e)
finally:
print("nothing happend")
[Errno 2] No such file or directory: 'file.txt'
nothing happend
try/except/else/finally
try:
...
except:
else:
....
finally:
.....
else語句裏邊的代碼只會在try裏面的語句沒有拋出異常時執行。
用戶自定義異常
class myerror(Exception):
def __init__(self, arg):
self.arg = arg
try:
raise myerror('an error occurred')
except myerror as e:
print(e)
an error occurred
用戶自定義的異常需要創建自己的異常類,並且可以直接或者間接的繼承自Exception
Python中的內存管理
內存池的概念:
頻繁的創建釋放消耗小內存的對象時,會使效率低。內存池就是事先申請一定數量,大小相等的內存塊放在內存池中,當有的對象需要分配內存時,直接從內存池中分配,如果不夠的話就再申請內存。
Python中的內存管理機制-pymalloc:
針對小對象,大小小於256bits時,直接從內存池中申請,當大於256bits直接執行new/malloc來申請空間。
內從空間的釋放:當一個對象的引用計數變爲0時,系統會調用它的析構函數,在析構時,如果內存來自內存池,則會把內存返回到內存池中,以避免頻繁的釋放。