1. PEP8 編碼規(guī)范, 及開發(fā)中的一些慣例和建議
-
練習(xí): 規(guī)范化這段代碼
from django.conf import settings from user.models import * import sys, os mod=0xffffffff def foo ( a , b = 123 ): c={ 'x' : 111 , 'y' : 222 }#定義一個(gè)字典 d=[ 1 , 3,5 ] return a,b , c def bar(x): if x%2==0 : return True
-
為什么要有編碼規(guī)范
- 編碼是給人看的還是給機(jī)器看的?
當(dāng)然是給人看的。 - 美觀是重點(diǎn)嗎?
- 美觀,因人而異。
- 可讀性
- 可維護(hù)性
- 健壯性
- 團(tuán)隊(duì)內(nèi)最好的代碼狀態(tài): 所有人寫出的代碼像一個(gè)人寫出來的
- 編碼是給人看的還是給機(jī)器看的?
-
代碼編排:
- 縮進(jìn) 4 個(gè)空格, 禁止空格與 Tab 混用
- 行長(zhǎng) 80 字符: 防止單行邏輯過于復(fù)雜
-
import
- 不要使用
from xxx import *
避免標(biāo)準(zhǔn)庫(kù)中的名字沖突
image.png
image.png - 順序
- 標(biāo)準(zhǔn)庫(kù)
- 第三方庫(kù)
- 自定義庫(kù)
- 單行不要 import 多個(gè)庫(kù)
-
模塊內(nèi)用不到的不要去 import
image.png
- 不要使用
-
空格
-
: ,
后面跟一個(gè)空格, 前面無空格 (行尾分號(hào)后無空格) - 二元操作符前后各一個(gè)空格, 包括以下幾類:
- 數(shù)學(xué)運(yùn)算符:
+ - * / // = & |
- 比較運(yùn)算符:
== != > < >= <= is not in
- 邏輯運(yùn)算符:
and or not
- 位運(yùn)算符:
& | ^ << >>
- 數(shù)學(xué)運(yùn)算符:
- 當(dāng)
=
用于指示關(guān)鍵字參數(shù)或默認(rèn)參數(shù)值時(shí), 不要在其兩側(cè)使用空格
-
-
適當(dāng)添加空行
- 函數(shù)間: 頂級(jí)函數(shù)間空 2 行, 類的方法之間空 1 行
- 函數(shù)內(nèi): 同一函數(shù)內(nèi)的邏輯塊之間, 空 1 行
- 文件結(jié)尾: 留一個(gè)空行 (Unix 中 \n 是文件的結(jié)束符)
-
注釋
- 忌: 逐行添加注釋, 沒有一個(gè)注釋
- 行尾注釋: 單行邏輯過于復(fù)雜時(shí)添加
- 塊注釋: 一段邏輯開始時(shí)添加
- 引入外來算法或者配置時(shí)須在注釋中添加源連接, 標(biāo)明出處
- 函數(shù)、類、模塊盡可能添加
docstring
-
命名
- 好的變量名要能做到“詞能達(dá)意”
- 除非在 lambda 函數(shù)中, 否則不要用 單字母 的變量名 (即使是 lambda 函數(shù)中的變量名也應(yīng)該盡可能的有意義)
- 包名、模塊名、函數(shù)名、方法、普通變量名全部使用小寫, 單詞間用下劃線連接
- 類名、異常名使用 CapWords (首字母大寫) 的方式, 異常名結(jié)尾加
Error
或Wraning
后綴 - 全局變量盡量使用大寫, 一組同類型的全局變量要加上統(tǒng)一前綴, 單詞用下劃線連接
- 函數(shù)名必須有動(dòng)詞, 最好是 do_something 的句式, 或者 somebody_do_something 句式
-
語(yǔ)意明確、直白
-
not xx in yy
VSxx not in yy
-
not a is b
VSa is not b
-
-
程序的構(gòu)建
- 函數(shù)是模塊化思想的體現(xiàn)
- 獨(dú)立的邏輯應(yīng)該抽離成獨(dú)立函數(shù),讓代碼結(jié)構(gòu)更清晰,可復(fù)用度更高
- 一個(gè)函數(shù)只做一件事情, 并把這件事做好
- 大的功能用小函數(shù)之間靈活組合來完成
- 避免編寫龐大的程序, “大” 意味著體積龐大, 邏輯復(fù)雜甚至混亂
自定義的變量名、函數(shù)名不要與標(biāo)準(zhǔn)庫(kù)中的名字沖突
-
pip install pycodestyle pylint flake8 autopep8 #語(yǔ)句以分號(hào)結(jié)束
image.png
2. * 和 ** 的用法
-
函數(shù)定義時(shí)接收不定長(zhǎng)參數(shù)
def foo(*args, **kwargs): pass
參數(shù)傳遞
def foo(x, y, z, a, b):
print(x)
print(y)
print(z)
print(a)
print(b)
lst = [1, 2, 3]
dic = {'a': 22, 'b': 77}
foo(*lst, **dic)
1
2
3
22
77
-
import * 語(yǔ)法
-
文件 xyz.py
__all__ = ('a', 'e', '_d') a = 123 _b = 456 c = 'asdfghjkl' _d = [1,2,3,4,5,6] e = (9,8,7,6,5,4)
-
文件 abc.py
from xyz import * print(a) print(_b) print(c) print(_d) print(e) #此時(shí)報(bào)錯(cuò),因?yàn)槭褂昧薩_all__方法,只允許調(diào)用a, e, _d變量
-
3. Python 的賦值和引用
-
==, is
:==
判斷的是值,is
判斷的是內(nèi)存地址 (即對(duì)象的id) - 小整數(shù)對(duì)象: [-5, 256]
-
copy, deepcopy
的區(qū)別-
copy
: 只拷貝表層元素 -
deepcopy
: 在內(nèi)存中重新創(chuàng)建所有子元素
copy.png
-
-
練習(xí)1: 說出執(zhí)行結(jié)果
default_list.png
```python
def extendList(val, lst=[]):
lst.append(val)
return lst
list1 = extendList(10)
list2 = extendList(123, [])
list3 = extendList('a')
```
- 練習(xí)2: 說出下面執(zhí)行結(jié)果
```python
from copy import copy, deepcopy
from pickle import dumps, loads
a = ['x', 'y', 'z']
b = [a] * 3
c = copy(b)
d = deepcopy(b)
e = loads(dumps(b, 4))
b[1].append(999)
b.append(777)
c[1].append(999)
c.append(555)
d[1].append(999)
d.append(333)
e[1].append(999)
e.append(111)
```
- 自定義 deepcopy:
my_deepcopy = lambda item: loads(dumps(item, 4))
4. 迭代器, 生成器
- 練習(xí): 說出如下代碼的打印結(jié)果
不帶參
>>> def foo():
... print(111)
... yield 222
... print(333)
... yield 444
... print(555)
>>> n = foo()
>>> next(n)
>>> next(n)
>>> next(n)
111
222
333
444
#拋出異常
StopIteration
帶參
def foo():
print(111)
r = yield 222
print(r, 333)
r = yield 444
print(r, 555)
n = foo()
n.send(None)
n.send('a')
n.send('b')
111
a 333
#拋出異常
StopIteration
- generator: 生成器是一種特殊的迭代器, 不需要自定義
__iter__
和__next__
- 生成器函數(shù) (yield)
- 生成器表達(dá)式
a = (i for i in range(5))
print(type(a)) # <class 'generator'>
自定義一個(gè)range
class Range:
def __init__(self, start, end=None, step=1):
if end is None:
self.end = start
self.start = 0
else:
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.start < self.end:
current = self.start
self.start += self.step
return current
else:
raise StopIteration()
-
iterator迭代器: 任何實(shí)現(xiàn)了
__iter__
和__next__
方法的對(duì)象都是迭代器.-
__iter__
得到一個(gè)迭代器。迭代器的__iter__()
返回自身 -
__next__
返回迭代器下一個(gè)值 - 如果容器中沒有更多元素, 則拋出 StopIteration 異常
- Python2中沒有
__next__()
, 而是next()
-
str / bytes / list / tuple / dict / set
自身不是迭代器,他們自身不具備__next__()
, 但是具有__iter__()
,__iter__()
方法用來把自身轉(zhuǎn)換成一個(gè)迭代器-
練習(xí)1: 定義一個(gè)隨機(jī)數(shù)迭代器, 隨機(jī)范圍為 [1, 50], 最大迭代次數(shù) 30
import random class RandomIter: def __init__(self, start, end, times): self.start = start self.end = end self.count = times def __iter__(self): return self def __next__(self): self.count -= 1 if self.count >= 0: return random.randint(self.start, self.end) else: raise StopIteration()
-
練習(xí)2: 自定義一個(gè)迭代器, 實(shí)現(xiàn)斐波那契數(shù)列
class Fib: def __init__(self, max_value): self.prev = 0 self.curr = 1 self.max_value = max_value def __iter__(self): return self def __next__(self): if self.curr < self.max_value: res = self.curr self.prev, self.curr = self.curr, self.prev + self.curr # 為下一次做準(zhǔn)備 return res else: raise StopIteration()
class Fab:
def __init__(self, times):
self.prev = 0
self.curr = 1
self.times = times
self.count = 0
def __iter__(self):
return self
def __next__(self):
# 需要一個(gè)退出條件
if self.count < self.times:
current = self.curr
self.prev, self.curr = self.curr, self.prev + self.curr
self.count += 1
return current
else:
raise StopIteration
for i in Fib(5):
print(i)
1
1
2
3
5
8
-
練習(xí)3: 自定義一個(gè)生成器函數(shù), 實(shí)現(xiàn)斐波那契數(shù)列
def fib(max_value): prev = 0 curr = 1 while curr < max_value: yield curr prev, curr = curr, curr + prev
def bar(n):
# prev = previous
prev = 0
# current
curr = 1
# 計(jì)數(shù)
count = 0
while count <= n:
yield curr
prev, curr = curr, prev + curr
count += 1
-
迭代器、生成器有什么好處?
- 節(jié)省內(nèi)存
- 惰性求值 (惰性求值思想來自于 Lisp 語(yǔ)言)
-
各種推導(dǎo)式
- 分三部分:生成值的表達(dá)式, 循環(huán)主體, 過濾條件表達(dá)式
- 列表:
[i * 3 for i in range(5) if i % 2 == 0]
- 字典:
{i: i + 3 for i in range(5)}
- 集合:
{i for i in range(5)}
- 生成器表達(dá)式:(i for i in range(5))
s = 'abcd'
#{'a': [1,1,1,1], 'b': [2,2,2,2], ...}
one = {key: value*4 for key, value in zip(s, [[1], [2], [3], [4]])}
two = {value: [1+index]*4 for index, value in enumerate(s)}
print(one,two)
{'a': [1, 1, 1, 1], 'b': [2, 2, 2, 2], 'c': [3, 3, 3, 3], 'd': [4, 4, 4, 4]}
{'a': [1, 1, 1, 1], 'b': [2, 2, 2, 2], 'c': [3, 3, 3, 3], 'd': [4, 4, 4, 4]}
5. 裝飾器
判斷是不是裝飾器:函數(shù)進(jìn),函數(shù)出
類裝飾器和普通裝飾器區(qū)別不大,類裝飾器未帶參的通過init方法接收函授,通過call接收函數(shù)的參數(shù),帶參數(shù)反之。
裝飾器體現(xiàn)了什么編程思想?
AOP aspect oriented programming
面向切片編程,不會(huì)影響原函數(shù),會(huì)額外添加功能。類似中間件
- 使用場(chǎng)景
- 參數(shù)、結(jié)果檢查
- 緩存、計(jì)數(shù)
- 日志、統(tǒng)計(jì)
- 權(quán)限管理
- 重試
- 其他
- 最簡(jiǎn)裝飾器
def deco(func): # 傳入func為函數(shù)進(jìn) def wrap(*args, **kwargs): return func(*args, **kwargs) return wrap # 返回函數(shù)為函數(shù)出 @deco def foo(a, b): return a ** b
裝飾器原理
import random
from functools import wraps
'''
原函數(shù)為foo
(1)中
@deco
def wrap(*args, **kwargs):
為什么可以這樣調(diào)用,裝飾器語(yǔ)法上已賦值給原始函數(shù)名
# 等同于走了(2)中
wrap = deco(foo)
bar = wrap
賦值給bar,實(shí)際調(diào)用wrap。
原函數(shù)調(diào)用裝飾器以后__name__,__doc__發(fā)生變化,這樣不友好,
未使用@wraps(func)
0
1
True
True
需用@wraps(func)還原被裝飾器修改的原函數(shù)屬性
0
5
False
False
'''
def deco(func):
'''
deco
'''
@wraps(func) # 還原被裝飾器修改的原函數(shù)屬性
def wrap(*args, **kwargs):
'''
wrap
'''
res = func(*args, **kwargs)
if res < 0:
return 0
else:
return res
return wrap
# (1)
@deco
def foo(m, n):
'''
foo
'''
return random.randint(m, n)
# 調(diào)用
print(foo(-5, 5))
# (2)
def bar(m, n):
'''
bar
'''
return random.randint(m, n)
wrap = deco(bar)
bar = wrap
print(wrap(-5, 5))
print(bar.__name__ is foo.__name__)
print(bar.__doc__ is foo.__doc__)
0
5
False
False
* 多個(gè)裝飾器疊加調(diào)用的過程
```python
@deco1
@deco2
@deco3
def foo(x, y):
return x ** y
# 過程拆解 1
fn3 = deco3(foo)
fn2 = deco2(fn3)
fn1 = deco1(fn2)
foo = fn1
foo(3, 4)
# 過程拆解 2
# 單行: deco1( deco2( deco3(foo) ) )(3, 2)
deco1(
deco2(
deco3(foo)
)
)(3, 4)
```
打比喻
def deco1(func):
def wrap1(*args, **kwargs):
print('enter wrap1(%s, %s)' % (args))
func(*args, **kwargs)
print('exit wrap1..')
return wrap1
def deco2(func):
def wrap2(*args, **kwargs):
print('enter wrap2(%s, %s)' % (args))
func(*args, **kwargs)
print('exit wrap2..')
return wrap2
def deco3(func):
def wrap3(*args, **kwargs):
print('enter wrap3(%s, %s)' % (args))
func(*args, **kwargs)
print('exit wrap3..')
return wrap3
@deco1
@deco2
@deco3
def foobar(x, y):
print(x, y)
return x ** y
foobar(1, 2)
#結(jié)果
enter wrap1(1, 2)
enter wrap2(1, 2)
enter wrap3(1, 2)
1 2
exit wrap3
exit wrap2
exit wrap1
用裝飾器由內(nèi)到外執(zhí)行
等于以下不用裝飾器的執(zhí)行過程,都是wrap1
@deco1
@deco2
@deco3
def foobar(x, y):
print(x, y)
return x ** y
# 不用裝飾器
def foobar2(x, y):
return x ** y
wrap3 = deco3(foobar2)
wrap2 = deco2(wrap3)
wrap1 = deco1(wrap2)
foobar2 = wrap1
print(foobar2.__name__)
print(foobar.__name__)
wrap1
wrap1
類似于flask
從里往外執(zhí)行
@route('/')
@login_required # 登陸才能改
@permission_required # 加權(quán)限
def index(xxx):
return 'hello world'
- 帶參數(shù)的裝飾器
def deco(n):
def wrap1(func):
def wrap2(*args, **kwargs):
return func(*args, **kwargs)
return wrap2
return wrap1
# 調(diào)用過程
wrap1 = deco(n)
wrap2 = wrap1(foo)
foo = wrap2
foo()
# 單行形式
check_result(30)(foo)(4, 8)
import time
# 帶參數(shù)的裝飾器
def times(count):
def calc_time(func):
def wrap(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print('時(shí)間%s,次數(shù)%d' % (end - start, count))
return res
return wrap
return calc_time
@times(10000)
def foo(x, y):
return x**y
foo(1000, 1000000)
時(shí)間1.159147024154663,次數(shù)10000
類似于flask傳url
@route('/')
def index(xxx):
return 'hello world'
另一種帶參
def control_time(n, name):
def sleep_time(func):
def wrap(*args, **kwargs):
print('只能玩%s%d分鐘' % (name, n))
res = func(*args, *kwargs)
time.sleep(n)
return res
return wrap
return sleep_time
@control_time(3, '王者榮耀')
def play():
pass
play()
只能玩王者榮耀3分鐘
- 裝飾器類和
__call__
以上代碼拆分延申至以下問題
python中有一個(gè)函數(shù),判斷能不能被調(diào)用callable()
a不能被調(diào)用, A能調(diào)用,因?yàn)锳類隱含object,從object里面引用的
class A:
pass
a = A()
print(callable(a))
print(callable(A))
True
False
f = foobar(1, 2)
print(callable(foobar))
print(callable(f))
True
False
實(shí)現(xiàn)call方法后b可調(diào)用
class B:
def __call__(self, x, y):
return x**y
b = B()
print(callable(b))
print(b(2, 2))
True
4
通過call方法實(shí)現(xiàn)類裝飾
class Deco:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
@Deco
def foo(x, y):
return x ** y
# 過程拆解
fn = Deco(foo)
foo = fn
foo(12, 34)
定義一個(gè)未帶參數(shù)的類裝飾器,功能:隨機(jī)正整數(shù)
import random
import time
class Deco:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
res = self.func(*args, **kwargs)
if res < 0:
return 0
else:
return res
@Deco
def foo(x, y):
return random.randint(x, y)
print(foo(-5, 20))
# 不用裝飾器
def toobar(x, y):
return random.randint(x, y)
toobar = Deco(toobar)
print(toobar(-5, 20))
#結(jié)果
0
6
帶參數(shù)的類裝飾器
class Control_time:
def __init__(self, name, minutes):
self.name = name
self.minutes = minutes
def __call__(self, func):
def wrapper(*args, **kwargs):
print('開始玩%s游戲,只能玩%d分鐘' % (self.name, self.minutes))
res = func(*args, **kwargs)
time.sleep(self.minutes)
print('時(shí)間到')
return res
return wrapper
@Control_time('War3', 1)
def play():
print('a')
play()
開始玩War3游戲,只能玩1分鐘
a
時(shí)間到
- 練習(xí)1: 寫一個(gè) timer 裝飾器, 計(jì)算出被裝飾函數(shù)調(diào)用一次花多長(zhǎng)時(shí)間, 并把時(shí)間打印出來
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrap(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print('耗時(shí):%s' % (end - start))
return res
return wrap
@timer
def foo(x, y):
return x ** y
foo(10, 1000000)
耗時(shí):0.19840478897094727
- 練習(xí)2: 寫一個(gè) Retry 裝飾器
作為裝飾器類名變?yōu)樾?/li>
import time
class retry(object):
def __init__(self, max_retries=3, wait=0, exceptions=(Exception,)):
self.max_retries = max_retries
self.exceptions = exceptions
self.wait = wait
def __call__(self, func):
def wrapper(*args, **kwargs):
for i in range(self.max_retries + 1):
try: # 如果try,沒有發(fā)生異常,則執(zhí)行else。發(fā)生異常則執(zhí)行except,然后continue重新執(zhí)行for循環(huán)
result = func(*args, **kwargs)
except self.exceptions:
time.sleep(self.wait)
print('retry %d' % i)
continue
else:
return result
return wrapper
@retry(3, 1, (ValueError,)) # (嘗試次數(shù),等待時(shí)間,異常)
def foo(x, y):
res = random.randint(x, y)
if res < 0:
raise ValueError
else:
return res
print(foo(-10, 5))
#結(jié)果
retry 0
retry 1
retry 2
retry 3
None
#重新執(zhí)行的結(jié)果
retry 0
retry 1
3
6. 函數(shù)閉包
Function Closure: 引用了自由變量的函數(shù)即是一個(gè)閉包. 這個(gè)被引用的自由變量和這個(gè)函數(shù)一同存在, 即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外.
-函數(shù)閉包3條件
1.必須有函數(shù)的嵌套
2.內(nèi)部函數(shù)引用了外部函數(shù)的變量
3.外部函數(shù)調(diào)用內(nèi)部函數(shù)-
說出下面函數(shù)返回值
def foo(): l = [] def bar(i): l.append(i) return l return bar f1 = foo() f2 = foo() # 說出下列語(yǔ)句執(zhí)行結(jié)果 f1(1) f1(2) f2(3) [1] [1, 2] [3]
深入一點(diǎn):
object.__closure__
print(f1.__closure__) # 返回元組
# (<cell at 0x0000015C73290C48: list object at 0x0000015C752B22C8>,)
cell = f1.__closure__[0] # 里面只有一個(gè),取第一個(gè)
print(cell) # <cell at 0x0000015C73290C48: list object at 0x0000015C752B22C8>
print(cell.cell_contents) # 存放閉包的自由變量的值 # [1, 2]
-
作用域
┌───────────────────────────┐ │ built-in namespace │ ├───────────────────────────┤ ↑ │ global namespace │ │ ┌───────────────────────┤ │ │ local namespace │ n = 123 │ │ ┌───────────────────┤ │ │ │ local namespace │ ↑ │ │ │ ┌───────────────┤ │ │ │ │ ... │ print(n) └───┴───┴───┴───────────────┘
- 聲明全局變量:
global
- 聲明非本層的 局部變量 :
nonlocal
- 查看全局變量:
globals()
- 查看局部變量:
locals()
- 查看變量:
vars([object]) # 不傳參數(shù)相當(dāng)于 locals(), 傳入對(duì)象后, 會(huì)得到 object.__dict__
- 聲明全局變量:
n = 100
m = 200
def deco(func):
m = 300
def wrap():
global n # 聲明全局變量
nonlocal m # 聲明局部變量
n = n + 1
m = m + 2
print(n)
print(m)
print('局部變量', locals()) # 查找局部變量
return func()
return wrap
@deco
def foo():
global m
m = m +3
print(m)
return print('全局變量', globals()) # 查找全局變量
foo()
# 結(jié)果
101
302
局部變量 {'m': 302, 'func': <function foo at 0x0000016BF5CCAAE8>}
203
全局變量 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000016BF5A7B160>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/python all/PythonCourses/advance/day16/python進(jìn)階/global_7.py', '__cached__': None, 'n': 101, 'm': 203, 'deco': <function deco at 0x0000016BF5CCAA60>, 'foo': <function deco.<locals>.wrap at 0x0000016BF5CCAB70>}
class A:
def __init__(self, x, y):
self.x = x
self.y = y
print(vars())
print(locals())
print(vars(A))
print(A.__dict__)
# 調(diào)用
print(vars(A(1, 2)))
# 結(jié)果
{'y': 2, 'x': 1, 'self': <__main__.A object at 0x000002AC9DECB240>}
{'y': 2, 'x': 1, 'self': <__main__.A object at 0x000002AC9DECB240>}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x000002AC9FCBAAE8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x000002AC9FCBAAE8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'x': 1, 'y': 2}
7. 類方法和靜態(tài)方法
區(qū)別
普通方法需傳self
類需傳cls
靜態(tài)方法什么都不傳
-
method
- 通過實(shí)例調(diào)用
- 可以引用類內(nèi)部的任何屬性和方法
-
classmethod
- 無需實(shí)例化
- 可以調(diào)用類屬性和類方法
- 無法取到普通的成員屬性和方法
-
staticmethod
- 無需實(shí)例化
- 無法取到類內(nèi)部的任何屬性和方法, 完全獨(dú)立的一個(gè)方法
練習(xí): 說出下面代碼的運(yùn)行結(jié)果
class Test(object):
# 類屬性
x = 123
def __init__(self):
self.y = 456 # 實(shí)例方法
def bar1(self):
print('i am a method')
@classmethod
def bar2(cls): # 類方法
print('i am a classmethod')
@staticmethod # 靜態(tài)方法
def bar3():
print('i am a staticmethod')
def foo1(self): # 普通方法
print(self.x)
print(self.y)
self.bar1()
self.bar2()
self.bar3()
@classmethod
def foo2(cls):
print(cls.x)
# print(cls.y) # 類方法不能調(diào)用普通方法的屬性
# cls.bar1() # 類方法不能調(diào)用普通方法
cls.bar2()
cls.bar3()
@staticmethod
def foo3(obj):
print(obj.x)
print(obj.y)
obj.bar1()
obj.bar2()
obj.bar3()
t = Test()
# print(t.foo1())
# print(t.foo2()) # 類方法可以調(diào)用類和靜態(tài)
print(t.foo3())
#結(jié)果
TypeError: foo3() missing 1 required positional argument: 'obj'
8. 繼承相關(guān)問題
- 什么是多態(tài)
多態(tài)指的是一類事物有多種形態(tài),(一個(gè)抽象類有多個(gè)子類,因而多態(tài)的概念依賴于繼承)
class Animal(object):
def run(self):
print("Animal running")
class Tiger(Animal):
def run(self):
print('Tiger running')
class Lion(Animal):
def run(self):
print('Lion running')
class LionTiger(Tiger, Lion):
def run(self):
print('LionTiger Animal')
pass
t = Tiger()
print(isinstance(t, Animal)) # True
l = Lion()
print(isinstance(l, Animal)) # True
lt = LionTiger()
print(isinstance(lt, Animal)) # True
print(isinstance(lt, Tiger)) # True
print(isinstance(lt, Lion)) # True
print(LionTiger.mro()) # 繼承鏈 順序會(huì)從前往后執(zhí)行
[<class '__main__.LionTiger'>, <class '__main__.Tiger'>, <class '__main__.Lion'>, <class '__main__.Animal'>, <class 'object'>]
-
多繼承
- 方法和屬性的繼承順序:
Cls.mro()
由內(nèi)到外 - 菱形繼承問題
繼承關(guān)系示意 菱形繼承 A.foo() / \ B C.foo() \ / D.mro() # 方法的繼承順序,由 C3 算法得到
- 方法和屬性的繼承順序:
Mixin: 通過單純的 mixin 類完成功能組合
super
如果A.init(self)這樣調(diào)用會(huì)出現(xiàn)調(diào)用兩次父類A,使用super優(yōu)化。
class A:
def __init__(self):
print('enter A')
self.x = 111
print('exit A')
class B(A):
def __init__(self):
print('enter B')
self.y = 222
# A.__init__(self)
super().__init__()
print('exit B')
class C(A):
def __init__(self):
print('enter C')
self.z = 333
# A.__init__(self)
super().__init__()
print('exit C')
class D(B, C):
def __init__(self):
print('enter D')
# B.__init__(self)
# C.__init__(self)
super().__init__()
print('exit D')
d = D()
print(D.mro())
enter D
enter B
enter C
enter A
exit A
exit C
exit B
exit D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
9. 垃圾收集 (GC)
- Garbage Collection (GC)
- 引用計(jì)數(shù)
優(yōu)點(diǎn): 簡(jiǎn)單、實(shí)時(shí)性高
-
缺點(diǎn): 消耗資源、循環(huán)引用
lst1 = [3, 4] # lst1->ref_count 1 lst2 = [8, 9] # lst2->ref_count 1 # lst1 -> [3, 4, lst2] lst1.append(lst2) # lst2->ref_count 2 # lst2 -> [8, 9, lst1] lst2.append(lst1) # lst1->ref_count 2 del lst1 # lst1->ref_count 1 del lst2 # lst2->ref_count 1
- 標(biāo)記-清除, 分代收集
- 用來回收引用計(jì)數(shù)無法清除的內(nèi)存
10. Python 魔術(shù)方法
凡是以雙下劃線開始和結(jié)尾的方法
-
__str__
格式化輸出對(duì)象
# __str__格式化輸出對(duì)象
# 凡是需要格式化輸出時(shí)調(diào)用這個(gè)方法
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'Point({self.x}, {self.y})'
# __repr__ 是python2中的,把它看作__str__。
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(1, 2)
print(p)
print(repr(p))
eval() 把字符串轉(zhuǎn)換為對(duì)象
x = '1234'
type(eval('1234'))
int
-
__init__
和__new__
區(qū)別-
__new__
創(chuàng)建一個(gè)實(shí)例,并返回類的實(shí)例 -
__init__
初始化實(shí)例,無返回值 -
__new__
是一個(gè)類方法
-
# 先創(chuàng)建對(duì)象再初始化
# __new__ 創(chuàng)建一個(gè)實(shí)例,并返回類的實(shí)例對(duì)象
# __init__ 初始化實(shí)例,無返回值
# __new__ 是一個(gè)特殊的類方法,不需要使用
p1 = object.__new__(Point) # 創(chuàng)建
print(isinstance(p1, Point)) # 初始化
p1.__init__(1, 2)
print(p1.x)
單例模式
# 這是最簡(jiǎn)單的單例模式
class A:
pass
aa = A()
# 什么是單例模式
# 單例模式,顧名思義就是程序在運(yùn)行的過程中,有且只有一個(gè)實(shí)例。它必須滿足三個(gè)關(guān)鍵點(diǎn)。
# 1)一個(gè)類只有一個(gè)實(shí)例
# 2)它必須自行創(chuàng)建這個(gè)實(shí)例。
# 3)它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。
# 經(jīng)常用于數(shù)據(jù)庫(kù)的連接池,限制連接數(shù)量,達(dá)到限制數(shù)量則排隊(duì)等待
# 單例模式有幾種,八種。我熟悉元類,__new__方法,和 模塊
# 實(shí)現(xiàn)單例模式
class A:
# 存實(shí)例
instance = None
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = object.__new__(cls)
return cls.instance
a = A()
b = A()
print(id(a))
print(id(b))
2423951827072
2423951827072
-
數(shù)學(xué)運(yùn)算、比較運(yùn)算
python類型數(shù)學(xué)運(yùn)算
image.png-
運(yùn)算符重載
-
+
:__add__(value)
-
-
:__sub__(value)
-
*
:__mul__(value)
-
/
:__truediv__(value)
(Python 3.x),__div__(value)
(Python 2.x) -
//
:__floordiv__(value)
-
//
:__ceil__(value)
import math math.ceil() 向上取整 -
%
:__mod__(value)
-
&
:__and__(value)
-
|
:__or__(value)
-
練習(xí): 實(shí)現(xiàn)字典的
__add__
方法, 作用相當(dāng)于 d.update(other) # 相同的key替換值,不同的添加后面
-
class Dict(dict):
def __add__(self, other):
if isinstance(other, dict):
new_dict = {}
new_dict.update(self)
new_dict.update(other)
return new_dict
else:
raise TypeError('not a dict')
dd = {2: 2, 4: 4, 3: 6}
dddd = Dict(dd)
print(dddd + {1: 3, 4: 8})
{2: 2, 4: 8, 3: 6, 1: 3}
key缺失時(shí)會(huì)調(diào)用missing方法
class Dict(dict):
# key缺失時(shí)會(huì)調(diào)用missing
def __missing__(self, key):
self[key] = None
return self[key]
d2 = Dict({1: 2, 2: 4, 3: 6})
# print(d2[5])
# 點(diǎn)之間減運(yùn)算
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __sub__(self, other):
if isinstance(other, Point):
x = self.x - other.x
y = self.y - other.y
return (x, y)
else:
raise TypeError('not a class')
p1 = Point(2, 2)
p2 = Point(2, 2)
print(p1 - p2)
(0, 0)
* 比較運(yùn)算符的重載
+ `==`: `__eq__(value)`
+ `!=`: `__ne__(value)`
+ `>`: `__gt__(value)`
+ `>=`: `__ge__(value)`
+ `<`: `__lt__(value)`
+ `<=`: `__le__(value)`
# 比較兩個(gè)box的大小
class Box:
def __init__(self, l, w, h):
self.l = l
self.w = w
self.h = h
@property
def bulk(self):
return self.l * self.w * self.h
def __lt__(self, other):
if isinstance(other, Box):
return self.bulk < other.bulk
else:
raise TypeError('not a box')
def __str__(self):
return f'Box{self.l}{self.w}{self.h}'
def __repr__(self):
return f'(Box{self.l},{self.w},{self.h})'
b1 = Box(1, 2, 3)
b2 = Box(1, 2, 2)
print(b1 < b2)
print(sorted([b1, b2]))
False
[(Box1,2,2), (Box1,2,3)]
- 練習(xí): 完成一個(gè)類, 實(shí)現(xiàn)數(shù)學(xué)上無窮大的概念
class Inf:
def __lt__(self, other):
return False
def __le__(self, other):
return False
def __ge__(self, other):
return True
def __gt__(self, other):
return True
def __eq__(self, other):
return False
def __ne__(self, other):
return True
o = Infinite()
print(o > 149837498317413)
o1 = Infinite()
print(o1 == o)
True
False
-
容器方法
-
__len__
-> len -
__iter__
-> for -
__contains__
-> in -
__getitem__
對(duì)string, bytes, list, tuple, dict
有效 -
__setitem__
對(duì)list, dict
有效
-
可執(zhí)行對(duì)象:
__call__
-
上下文管理 with:
-
__enter__
進(jìn)入with
代碼塊前的準(zhǔn)備操作 -
__exit__
退出時(shí)的善后操作 - 文件對(duì)象、線程鎖、socket 對(duì)象 等 都可以使用 with 操作
- 示例
class A: def __enter__(self): return self def __exit__(self, Error, error, traceback): print(args)
-
test = open('test.txt')
print(test.read()) # this is test file.
print(test.closed) # False
test.__exit__()
print(test.closed) # True
print(test.read())
# ValueError: I/O operation on closed file.
with lock:
pass
print(lock.locked()) # True
print(lock.locked()) # False
exceptions = []
class A:
def __enter__(self):
return self
def __exit__(self, *args):
exceptions.append(args)
return exceptions
with A() as a:
pass
print(1)
print(a)
raise KeyError("key error")
print(2)
print(exceptions)
a, b, c = exceptions[0]
print(a)
print(b)
print(c)
#結(jié)果
1
<__main__.A object at 0x000001EFDFD86E80>
[(<class 'KeyError'>, KeyError('key error',), <traceback object at 0x000001EFDFD8EB88>)]
<class 'KeyError'>
'key error'
<traceback object at 0x000001EFDFD8EB88>
-
__setattr__, __getattribute__, __getattr__, __dict__
- 內(nèi)建函數(shù):
setattr(), getattr(), hasattr()
- 內(nèi)建函數(shù):
class A:
pass
a = A()
setattr(a, 'x', 123)
print(a.x) # 123
a.y = 234
print(a.y) # 234
a.__setattr__('z', 345)
print(a.z) # 345
getattr(a, 'g', 123)
print(a.g) # AttributeError: 'A' object has no attribute 'g'
print(hasattr(a, 'g')) # False
getattr(a, 'l') if hasattr(a, 'l') else setattr(a, 'l', 999)
print(a.l, a.__dict__.get('l'), a.__dict__) # 999 999 {'x': 123, 'y': 234, 'z': 345, 'l': 999}
print(a.__dict__.pop('l'), a.__dict__) # 999 {'x': 123, 'y': 234, 'z': 345}
a.__dict__['l'] = 999
print(a.__dict__) # {'x': 123, 'y': 234, 'z': 345, 'l': 999}
* 常用來做屬性監(jiān)聽
```python
class User:
'''TestClass'''
z = [7,8,9]
def __init__(self):
self.money = 10000
self.y = 'abc'
def __setattr__(self, name, value):
if name == 'money' and value < 0:
raise ValueError('money < 0')
print('set %s to %s' % (name, value))
object.__setattr__(self, name, value)
def __getattribute__(self, name):
print('get %s' % name)
return object.__getattribute__(self, name)
def __getattr__(self, name):
print('not has %s' % name)
return -1
def foo(self, x, y):
return x ** y
```
class B:
z = 123
def __init__(self, x, y):
self.x = x
self.y = y
b = B(1, 2)
print(b.__dict__) # {'x': 1, 'y': 2}
print(b.z) # 123
print(B.__dict__)
# {'__module__': '__main__', 'z': 123, '__init__': <function B.__init__ at 0x000001FFF2D71AE8>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
- 槽:
__slots__
- 固定類所具有的屬性
- 實(shí)例不會(huì)分配
__dict__
- 實(shí)例無法動(dòng)態(tài)添加屬性
- 優(yōu)化內(nèi)存分配,大約能節(jié)省40%內(nèi)存
意思就是:買來一臺(tái)電腦只有兩個(gè)內(nèi)存條卡槽,你再買一條來加內(nèi)存,無法加入。
class C:
__slots__ = ('x', 'y')
c = C()
c.__dict__ # AttributeError: 'C' object has no attribute '__dict__'
c.x = 10
c.y = 20
print(c.x) # 20
c.l = 20 # AttributeError: 'C' object has no attribute 'l'
11. Python 性能之困
python相對(duì)于靜態(tài)語(yǔ)言,稍微慢了一些
-
計(jì)算密集型
- CPU 長(zhǎng)時(shí)間滿負(fù)荷運(yùn)行, 如圖像處理、大數(shù)據(jù)運(yùn)算、圓周率計(jì)算等
- 計(jì)算密集型: 用 C 語(yǔ)言補(bǔ)充
- Profile #統(tǒng)計(jì)函數(shù)運(yùn)行的時(shí)間, timeit (timeit -n times -r func)
-
I/O 密集型
- 網(wǎng)絡(luò) IO, 文件 IO, 設(shè)備 IO 等
- 一切皆文件
-
多任務(wù)處理
進(jìn)程、線程不能控制,由操作系統(tǒng)控制- 進(jìn)程、線程、協(xié)程調(diào)度的過程叫做上下文切換
- 進(jìn)程、線程、協(xié)程對(duì)比
進(jìn)程
import random
from multiprocessing import Process
def foo(n):
x = 0
for i in range(n):
x += random.randint(1, 100)
q.put(x)
return x
def bar(q, n):
for i in range(n):
data = q.get()
print(f'取到了數(shù)據(jù){data}')
if __name == 'main':
#創(chuàng)建一個(gè)隊(duì)列
q = Queue()
# q = Queue(10) #限制10個(gè)
# for i in range(15):
# print(i)
# #q.put(i, block=False) # 當(dāng)block=False會(huì)報(bào)異常queue full
# q.put(i, timeout=5) # 5秒后報(bào)異常queue full
# 創(chuàng)建進(jìn)程1
p1 = Process(target=foo, args=(1000,q))
p1.start()
# p.join() # 同步執(zhí)行,這句話保證子進(jìn)程結(jié)束后再向下執(zhí)行 # p.join(2)#等待2s
# print(q.qsize()) # 長(zhǎng)度
# 1000
# 創(chuàng)建進(jìn)程1
p2 = Process(target=foo, args=(1000,q))
p2.start()
名稱 | 資源占用 | 數(shù)據(jù)通信 | 上下文切換 (Context)
-----|---------|------------------------------|------------------
進(jìn)程 | 大 | 不方便 (網(wǎng)絡(luò)、共享內(nèi)存、管道等) | 操作系統(tǒng)按時(shí)間片切換, 不夠靈活, 慢
線程 | 小 | 非常方便 | 按時(shí)間片切換, 不夠靈活, 快
協(xié)程 | 非常小 | 非常方便 | 根據(jù)I/O事件切換, 更加有效的利用 CPU
-
全局解釋器鎖 ( GIL )
- 它確保任何時(shí)候一個(gè)進(jìn)程中都只有一個(gè) Python 線程能進(jìn)入 CPU 執(zhí)行。
- 全局解釋器鎖造成單個(gè)進(jìn)程無法使用多個(gè) CPU 核心
- 通過多進(jìn)程來利用多個(gè) CPU 核心,一般進(jìn)程數(shù)與CPU核心數(shù)相等,或者CPU核心數(shù)兩倍
[圖片上傳失敗...(image-7ae5ee-1537289041588)]
-
什么是同步、異步、阻塞、非阻塞?
- 同步, 異步: 客戶端調(diào)用服務(wù)器接口時(shí)
- 阻塞, 非阻塞: 服務(wù)端發(fā)生等待
- 阻塞 -> 非阻塞
- 同步 -> 異步
-
協(xié)程:Stackless / greenlets / gevent | tornado / asyncio
import asyncio async def foo(n): for i in range(10): print('wait %s s' % n) await asyncio.sleep(n) return i task1 = foo(1) task2 = foo(1.5) tasks = [asyncio.ensure_future(task1), asyncio.ensure_future(task2)] loop = asyncio.get_event_loop() # 事件循環(huán),協(xié)程調(diào)度器 loop.run_until_complete( asyncio.wait(tasks) )
-
線程安全, 鎖
- 獲得鎖之后, 一定要釋放, 避免死鎖
- 盡量使用 with 去操作鎖
- 獲得鎖之后, 執(zhí)行的語(yǔ)句, 只跟被鎖資源有關(guān)
- 線程之間的數(shù)據(jù)交互盡量使用 Queue
12. 一些技巧和誤區(qū)
-
格式化打印 json
- 調(diào)試時(shí)數(shù)據(jù)格式化:
json.dumps(data, indent=4, sort_keys=True, ensure_ascii=False)
- 傳輸時(shí) json 壓縮:
json.dumps(data, ensure_ascii=False, separators=[',',':'])
- 調(diào)試時(shí)數(shù)據(jù)格式化:
-
確保能取到有效值
-
d.get(k, default)
無值時(shí)使用默認(rèn)值,對(duì)原字典無修改 -
d.setdefault
無值時(shí)使用默認(rèn)值,并將默認(rèn)值寫入原字典 x = a if foo() else b
-
a or b
python中所有的對(duì)象都有真值
-
-
try...except... 的濫用
- 不要把所有東西全都包住, 程序錯(cuò)誤需要報(bào)出來
- 使用
try...except
要指明具體錯(cuò)誤,try
結(jié)構(gòu)不是用來隱藏錯(cuò)誤的, 而是用來有方向的處理錯(cuò)誤的
-
利用 dict 做模式匹配
def do1(): print('i am do1') def do2(): print('i am do2') def do3(): print('i am do3') def do4(): print('i am do4') mapping = {1: do1, 2: do2, 3: do3, 4: do4} mod = random.randint(1, 10) func = mapping.get(mod, do4) func()
inf, -inf, nan
inf 極大數(shù) 任何數(shù)相比都是-inf大
-inf 極小數(shù) 任何數(shù)相比都是-inf小字符串拼接盡量使用
join
方式: 速度快, 內(nèi)存消耗小
如果用 ‘+’號(hào),會(huì)有內(nèi)存碎片,用join則無。property: 把一個(gè)方法屬性化
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
if __name__ == '__main__':
c = C()
c.x = 10
print(c.x)
del c.x
# print(c.x)
if __name__ == '__main__':
c = C()
c.x = 10 # set 10
print(c.x) # 10
del c.x # del 10
print(c.x) # AttributeError: 'C' object has no attribute '_x'
- else 子句:
if, for, while, try
-
- collections 模塊
- defaultdict 當(dāng)你找不到的時(shí)候會(huì)把找不到的key和value添加到里面取
- OrderedDict 是一個(gè)有序的字典,按插入時(shí)的順序循環(huán)出來
- Counter 計(jì)數(shù)器
c = Counter() for i in range(1000) n = random.randint(1,100) c[n] += 1
- namedtuple 當(dāng)成一個(gè)類,特性是當(dāng)設(shè)置了值即不可變。
- pyenv day19-14:55
若提示未裝git
則yum install git
11.devops運(yùn)維