Python常用知識點一二

只愛一個人發表於2017-09-16

寫在前面

Python是一門解釋型的、動態型的OOP語言,結識Python的這幾年來,其語法的簡潔、結構的條理性深刻的吸引了我。下面是我總結出來的一些Python常用知識點。

列表碾平式

需求: 將[[1,2],[3,4]] 轉換為[1,2,3,4],具體實現有以下幾種方法

test_list = [[1,2],[3,4]]

1. from itertools import chain
   list(chain.from_iterable(test_list))
   結果:[1, 2, 3, 4]

2. from itertools import chain
   list(chain(*test_list))
   結果:[1, 2, 3, 4]

3. sum(test_list, [])
   結果:[1, 2, 3, 4]

4. [x for y in test_list for x in y]
   結果:[1, 2, 3, 4]

5. 萬能方法(遞迴)
   func = lambda x: [y for t in x for y in func(t)] if type(x) is list else [x]
   func(test_list)
   結果:[1, 2, 3, 4]複製程式碼

PS: 專案中,難免會有類似的需求,對於結構巢狀一致的情況,上述的1,2,3,4方法都可以很好的解決(不建議用for迴圈巢狀的方式,那是最low的方法,沒有之一);對於結構巢狀不一致的情況,第5種方法就派上了用場,其採用了遞迴的思想,堪稱萬能的方法,屢試不爽。專案中,大家可以根據實際應用場景來挑選最適合自己的方法。肚中有糧,心中不慌;大家可以把上述方法都記下來,以做到有的放矢。

三目操作符

對於Python的三元表示式,想必大家對if else都不會感到陌生,但是對and or操作想必是另一種感覺了,不過大家對其他語言的? :應該不會陌生,沒錯,這次的主角and or就和? :有著異曲同工之妙。

程式碼參考:

1 == 1 and 2 or 3   返回2
1 == 2 and 2 or 3   返回3複製程式碼

PS:習慣於if else的同學偶爾用下and or是不是會給人耳目一新的感覺。

and or 分開來用

  • [x] and 用法如下:
1 and 2 and 3   返回3
1 and 2 and ''   返回''
'' and 2 and 0  返回''
PS:如果都為真則返回最後一個值,如果其中某些值為假,則返回第一個為假的值複製程式碼
  • [x] or 用法如下
1 or '' or 0    返回1
'' or 0 or []    返回[]
PS:如果都為假返回最後一個值,如果其中某些值為真,則返回第一個為真的值複製程式碼

使用場景:在專案中我們經常會有這樣的需求,在將一個字典更新之後還想要返回更新後的字典,這是我們就可以這樣寫:dic = dic1.update(dic2) or dic1

列表推導式

需求:將[1,2,3]中的每一項都加1

good: [x+1 for x in [1,2,3]]
bad:  def add_list(goal_list):
          tmp_list = []
          for x in goal_list:
              tmp_list.append(x+1)複製程式碼

PS: 列表推導式底層是用C實現的,其執行速度要比for迴圈快好多

vars() 用法

程式碼參考:

```
def func(a, b, c):
    print vars()
執行func(1, 2, 3)
輸出:{"a":1,"b":2,"c":3}
PS: vars()的值為字典,其鍵值對來源於當前作用域的所有變數。
```複製程式碼

使用場景:在呼叫他人介面或方法時,需要將傳入的引數列印以記錄日誌,此刻vars()便派上用場了。

偏函式之partial

程式碼示例:

import functools
def add(a, b):
    return a + b
add(4, 2)
    6
plus3 = functools.partial(add, 3)
plus5 = functools.partial(add, 5)
plus3(4)
    7
plus3(7)
    10
plus5(10)
    15複製程式碼

實際使用心得:

之前做物件儲存的專案中,我需要同時去呼叫三個一樣的介面(大部分引數一樣)下面是我的部分程式碼,供大家參考

my_thread = functools.partial(myThread, dic, zone_id, start, end, customer_id)
my_thread1 = my_thread("day", "gets", "2")
my_thread1 = my_thread("day", "original_traffic", "3")複製程式碼

使用場景:
當我們需要同時去呼叫一個函式,並且發現大部分引數一致的時候,便可以採取上述方法,一來程式碼簡潔,二來可讀性高。

Python搭建簡易服務

  • Python搭建簡易郵件伺服器:python -m smtpd -n -c DebuggingServer localhost:1025
  • Python搭建簡易web伺服器:

    • Python2: python -m SimpleHTTPServer port
    • Python3: python -m http.server port
  • Python搭建簡易ftp服務

      pip install pyftpdlib
      python -m pyftpdlib -p 21
      ftp://localhost:21複製程式碼

遍歷字典

在專案中遍歷字典是很常見的需求,下面介紹下常用的方法並做下比較:

dic = {'name': 'peter', 'age': 27}
1. for key, value in dic.items():
       print key, value
2. for key, value in dic.iteritems():
       print key, value複製程式碼

PS:
iteritemsitems的區別在於iteritems採用了生成器的原理,只有在需要的時候才會把值生成,其之間的區別類似於rangexrangereadlinexreadline

記憶體管理

Python的記憶體管理主要分為引用計數和垃圾回收機制兩大部分,且看下面程式碼:

  • [ ] 記憶體分配:

      a = 1
      b = 1
      a is b      True
      ---------------------
      a = 1000
      b = 1000
      a is b      False複製程式碼

    PS: 在Python中,整數和短小的字元,Python都會快取這些物件,以便重複使用。當我們建立多個等於1的引用時,實際上是讓這些引用指向了同一個物件。

  • [ ] 引用計數:

    在Python中,所謂引用計數(reference count)是指所有指向該物件的引用的總數;

    我們可以使用sys包中的getrefcount(),來檢視某個物件的引用計數。需要注意的是,當使用該函式檢視某個物件的引用計數時,實際上是臨時建立了該物件的一個新的引用,所有使用getrefcount()所得到的結果,會比期望的值多1。

      from sys import getrefcount
      aa = 'test refcount'
      print(getrefcount(a))
      bb = aa
      print(getrefcount(a))複製程式碼

    PS: 由於上述原因,兩個getrefcount()將返回2和3,並不是期望的1和2.

  • [ ] 引用減少

    引用減少大致分為兩類:

    • 指向該物件的引用指向了其他物件

         from sys import getrefcount
      
         aa = 'test refcount'
         bb = aa
         print(getrefcount(aa))      3
      
         bb = 1
         print(getrefcount(aa))      2複製程式碼
    • 使用del關鍵字顯示的刪除某個引用

         from sys import getrefcount
      
         aa = 'test refcount'
         bb = aa
         print(getrefcount(aa))      3
      
         del bb
         print(getrefcount(aa))      2複製程式碼
  • [ ] 垃圾回收

    不斷的建立物件,如果不及時銷燬的話,那Python的體積會越來越大,再大的記憶體也會有耗完的時候;不用像C語言那樣,需要手動的去管理記憶體、Python已經幫我們做好了(Python的垃圾回收機制),你只需要去關心你的業務邏輯即可,其他的都交給Python來處理。

    從原理上講,當Python中某個物件的引用計數降為0時,該物件就應該被回收。但是頻繁的啟動垃圾回收機制畢竟是個很耗時的問題;因此Python只有在特定條件下(當Python中被分配物件和取消分配物件的次數之間的差值達到某個閾值時),Python會自動啟動垃圾回收機制。

    我們可以通過gc模組的get_threshold()方法,檢視該閾值:

      import gc
      print(gc.get_threshold())複製程式碼

    該方法會返回(700, 10, 10),後面的倆10是與分代回收相關的,稍後講解。700便是垃圾回收機制啟動的閾值。可以通過gc模組中的set_threshold()方法重新設定該值。

    當然了,我們也可以手動啟動垃圾回收機制,使用gc.collect()即可。

  • [ ] 分代回收

    Python同時採用了分代回收的機制,設想一下:存活越久的物件、越不可能是垃圾物件。程式在執行時,往往會產生大量的臨時物件,程式結束之後,這些臨時物件的生命週期也就隨之告一段落。但有一些物件會被長期佔用,垃圾回收機制在啟動的時候會減少掃描到他們的頻率。

    Python將所有物件分為0,1,2三代。所有新建立的物件都是0代,當垃圾回收機制在啟動多次0代機制並掃描到他們的時候,這些物件如果依然存活在記憶體的話,他們就會被歸入下一代物件,以此類推。

    剛才上面所提到的(700, 10, 10)三個引數後面的倆10所代表的意思是:每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收會配合1次的2代垃圾回收。

    當然我們同樣可以使用set_threshold()來調整此策略的比例,比如對1代物件進行更頻繁的掃描。

      import gc
      gc.set_threshold(700, 5, 10)複製程式碼

新式類、經典類

  • [ ] 新式類: 顯示的繼承了object的類

      class A(object):
          attr = 1
    
      class B(A):
          pass
    
      class C(A):
          attr = 3
    
      class D(B, C):
          pass
    
      if __name__ == '__main__':
          d = D()
          print 'attr = ', d.attr     # attr = 3複製程式碼
  • [ ] 經典類:沒有繼承自object的類

      class A():
          attr = 1
    
      class B(A):
          pass
    
      class C(A):
          attr = 3
    
      class D(B, C):
          pass
    
      if __name__ == '__main__':
          d = D()
          print 'attr = ', d.attr     # attr = 1複製程式碼

    PS: 通過以上程式碼的輸出結果可以看出,新式類會廣度搜尋,也就是一層層的向上搜尋;經典類是深度優先,即遇到一個超類點就向上搜尋。

裝飾器

Python的裝飾器被稱為Python的語法糖,哪裡需要粘哪裡。

程式碼示例:

@makeh1
@makeeitalic
def say():
    return 'Peter'

我們希望輸出結果為:<h1><i>Peter</i></h1>複製程式碼

去看看官方文件,答案就看下面:

def makeh1(func):
    def wrp():
        return "<h1>" + func() + "</h1>"
    return wrp

def makeeitalic(func):
    def wrp():
        return "<i>" + func() + "</i>"
    return wrp

@makeh1
@makeeitalic
def say():
    return 'Hello Peter'

print say()
輸出:<h1><i>Hello Peter</i></h1>複製程式碼

實際應用場景:

使用過django的小夥伴想必都用過login_required裝飾器,但是如果使用者沒登入的話login_required會重定向到登入頁面;在做web開發的過程中,我們會經常用
ajax非同步提交資料到後臺,這時如果再繼續使用原有的login_required裝飾器肯定是不行了(該裝飾器不會重定向到登入頁面,ajax也沒有任何返回結果),下面我們改變下原有程式碼:

from django.shortcuts import HttpResponse
import json

def is_authenticat(func):
    def wrp(req, **kwargs):
        if req.user.is_authenticated():
            return func(req, **kwargs)
        else:
            json_str = {'status': 0, 'msg': u'請登入'}
            return HttpResponse(json.dumps(json_str), content_type='application/json')
    return wrp複製程式碼

上述程式碼便很好的解決了問題,也算是對Python裝飾器的一個很好的使用場景。

寫在結語

Python的奧妙遠不止於此

Python的深度還需要繼續探索

以上就是平時工作中所總結出來的常用的知識點

希望對大家以後的工作會有所幫助

相關文章