迭代器-生成器-裝飾器-函數(shù)閉包-繼承-類方法和靜態(tài)方法-魔術(shù)方法-垃圾收集-* 和 ** 的用法-Python 賦值和引用-PEP8編碼規(guī)范

捕獲.PNG

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)嗎?
      1. 美觀,因人而異。
      2. 可讀性
      3. 可維護(hù)性
      4. 健壯性
    • 團(tuán)隊(duì)內(nèi)最好的代碼狀態(tài): 所有人寫出的代碼像一個(gè)人寫出來的
  • 代碼編排:

    • 縮進(jìn) 4 個(gè)空格, 禁止空格與 Tab 混用
    • 行長(zhǎng) 80 字符: 防止單行邏輯過于復(fù)雜
  • import

    • 不要使用 from xxx import * 避免標(biāo)準(zhǔn)庫(kù)中的名字沖突
      image.png

      image.png
    • 順序
      1. 標(biāo)準(zhǔn)庫(kù)
      2. 第三方庫(kù)
      3. 自定義庫(kù)
    • 單行不要 import 多個(gè)庫(kù)
    • 模塊內(nèi)用不到的不要去 import


      image.png
  • 空格

    • : , 后面跟一個(gè)空格, 前面無空格 (行尾分號(hào)后無空格)
    • 二元操作符前后各一個(gè)空格, 包括以下幾類:
      1. 數(shù)學(xué)運(yùn)算符: + - * / // = & |
      2. 比較運(yùn)算符: == != > < >= <= is not in
      3. 邏輯運(yùn)算符: and or not
      4. 位運(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é)尾加 ErrorWraning 后綴
    • 全局變量盡量使用大寫, 一組同類型的全局變量要加上統(tǒng)一前綴, 單詞用下劃線連接
    • 函數(shù)名必須有動(dòng)詞, 最好是 do_something 的句式, 或者 somebody_do_something 句式
  • 語(yǔ)意明確、直白

    • not xx in yy VS xx not in yy
    • not a is b VS a 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é)果
copy_deepcopy.png
```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é)尾的方法

  1. __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
  1. __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
  1. 數(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

  1. 容器方法

    • __len__ -> len
    • __iter__ -> for
    • __contains__ -> in
    • __getitem__ 對(duì) string, bytes, list, tuple, dict 有效
    • __setitem__ 對(duì) list, dict 有效
  2. 可執(zhí)行對(duì)象: __call__

  3. 上下文管理 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>
  1. __setattr__, __getattribute__, __getattr__, __dict__
    • 內(nèi)建函數(shù):setattr(), getattr(), hasattr()
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}
  1. 槽: __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ǔ)言,稍微慢了一些

  1. 計(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)
  2. I/O 密集型

    • 網(wǎng)絡(luò) IO, 文件 IO, 設(shè)備 IO 等
    • 一切皆文件
  3. 多任務(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
  1. 全局解釋器鎖 ( 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)]
  2. 什么是同步、異步、阻塞、非阻塞?

    • 同步, 異步: 客戶端調(diào)用服務(wù)器接口時(shí)
    • 阻塞, 非阻塞: 服務(wù)端發(fā)生等待
    • 阻塞 -> 非阻塞
    • 同步 -> 異步
  3. 協(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) )
    
  4. 線程安全, 鎖

    • 獲得鎖之后, 一定要釋放, 避免死鎖
    • 盡量使用 with 去操作鎖
    • 獲得鎖之后, 執(zhí)行的語(yǔ)句, 只跟被鎖資源有關(guān)
    • 線程之間的數(shù)據(jù)交互盡量使用 Queue

12. 一些技巧和誤區(qū)

  1. 格式化打印 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=[',',':'])
  2. 確保能取到有效值

    • 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ì)象都有真值
  3. try...except... 的濫用

    • 不要把所有東西全都包住, 程序錯(cuò)誤需要報(bào)出來
    • 使用 try...except 要指明具體錯(cuò)誤, try 結(jié)構(gòu)不是用來隱藏錯(cuò)誤的, 而是用來有方向的處理錯(cuò)誤的
  4. 利用 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()
    
  5. inf, -inf, nan
    inf 極大數(shù) 任何數(shù)相比都是-inf大
    -inf 極小數(shù) 任何數(shù)相比都是-inf小

  6. 字符串拼接盡量使用 join 方式: 速度快, 內(nèi)存消耗小
    如果用 ‘+’號(hào),會(huì)有內(nèi)存碎片,用join則無。

  7. 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'
  1. else 子句: if, for, while, try
    1. 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è)置了值即不可變。
image.png
  1. pyenv day19-14:55
    若提示未裝git
    則yum install git

11.devops運(yùn)維

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容