Python學習筆記:基礎、函數、高級特性、常用庫、常見問題總結

目錄

0. Python的一些考點

1. 基礎

2. 函數

2.1 定義函數

3. 高級特性

4. 函數式編程 Functional Programming

5. Python - logging module

5.1 開始 - 小案例

5.2 logging中的日誌級別

5.3 logging中的基礎類

5.3.1 第一個基礎類:LogRecord

5.3.2 第二個基礎類:Formatter

5.3.3 第三個基礎類:Filter和Filterer

5.4 logging中的高階類

5.4.1 Handler——抽象了log的輸出過程

5.4.2 Logger —— 一個獨立的log管道

5.4.3 Manager —— 管理logger的類

5.4.4 LoggerAdapter —— 對標準logger的一個擴展

5.5 logging中的config函數

5.5.1 def basicConfig(**kwargs)

6. session & Cookie

7. decorator

8. 一些常用的庫

8.1 pandas

8.2 matplotlib

8.3 pickle

8.4 numpy

9. 一些常問的問題總結


0. Python的一些考點

參考鏈接:https://www.nowcoder.com/discuss/117365

廖雪峯的官方網址 https://www.ntliaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0014021031294178f993c85204e4d1b81ab032070641ce5000

  • 裝飾器(什麼是AOP/面向切面編程)、
  • 迭代器與生成器的區別什麼
  • Python代碼執行原理
  • Python的int是怎麼實現的
  • 解釋Python的對象
  • 如何自己實現一個字典
  • 什麼是GIL、爲什麼要加GIL、如何理解Python多線程
  • 什麼是協程
  • Python的IO多路複用是怎麼實現的
  • 什麼是上下文管理器
  • 你知道右加麼(__radd__)
  • 什麼是閉包
  • python中一般的類都繼承object,那object的父類是什麼(type)
  • 談談元類、元類的應用
  • 用Python寫一個單例模式
  • 談談super原理
  • 什麼是多重繼承
  • 淺複製和深複製有什麼區別

1. 基礎

  • if條件判斷: if判斷條件還可以簡寫,比如寫:
if x:
    print('True')

# 只要x是非零數值、非空字符串、非空list等,就判斷爲True,否則爲False。

 

  • 循環:
# 1. For x in …
# 2. while
# 3. Continue:可以通過continue語句,跳過當前的這次循環,直接開始下一次循環。
# 4. break語句,提前推出循環
  • List: 數組
  • Tuple:

tuple所謂的“不變”是說,tuple的每個元素,指向永遠不變。即指向'a',就不能改成指向'b',指向一個list,就不能改成指向其他對象,但指向的這個list本身是可變的!

  • Dict:  dictionary, 在其他語言中也稱爲map,使用key-value

注意:dic的key是不可變對象;通過key計算位置的算法成爲Hash算法

# e.g 定義一個dict
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}

# e.g 判斷key存不存在:

>>> 'Thomas' in d
False

# e.g get方法

>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1
  • Set:是一組key的集合,但不存儲value;key不能重複。方法有:add(key) ; remove(key)

注意:set的原理和dict一樣,所以,同樣不可以放入可變對象,因爲無法判斷兩個可變對象是否相等,也就無法保證set內部“不會有重複元素”。試試把list放入set,看看是否會報錯。

  • string:不可變對象

2. 函數

  • help(function_name)查詢函數幫助信息
  • abs(),max()
  • 數據類型轉換int(),bool(),str(),float()
  • 函數名其實就是指向一個函數對象的引用,完全可以把函數名賦給一個變量,相當於給這個函數起了一個“別名”:
>>> a = abs # 變量a指向abs函數
>>> a(-1) # 所以也可以通過a調用abs函數
1
  • 2.1 定義函數

  • 空函數 pass   如果想定義一個什麼事也不做的空函數,可以用pass語句:
def nop():
    pass

pass語句什麼都不做,那有什麼用?實際上pass可以用來作爲佔位符,比如現在還沒想好怎麼寫函數的代碼,就可以先放一個pass讓代碼能運行起來。

pass還可以用在其他語句裏,比如:

if age >= 18:
    pass

缺少了pass,代碼運行就會有語法錯誤:(1)參數類型檢查:isinstance() (2)返回多個值:實際上返回的是一個tuple

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)

    return nx, ny
  • 函數參數
    • 默認參數 : 注意:Python函數在定義的時候,默認參數L的值就被計算出來了,即[],因爲默認參數L也是一個變量,它指向對象[],每次調用該函數,如果改變了L的內容,則下次調用時,默認參數的內容就變了,不再是函數定義時的[]了。
    • 可變參數:
      • >>> nums = [1, 2, 3]
        >>> calc(*nums)
        14
    • 關鍵字參數
      • def person(name, age, **kw):
            print('name:', name, 'age:', age, 'other:', kw)

        函數person除了必選參數nameage外,還接受關鍵字參數kw。在調用該函數時,可以只傳入必選參數:

        可變參數允許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝爲一個tuple。

        而關鍵字參數允許你傳入0個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝爲一個dict。

      • 命名關鍵字參數:如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收cityjob作爲關鍵字參數。這種方式定義的函數如下:
        • def person(name, age, *, city, job):
              print(name, age, city, job)
          
          
        • 和關鍵字參數**kw不同,命名關鍵字參數需要一個特殊分隔符**後面的參數被視爲命名關鍵字參數。 調用方式如下:
          • >>> person('Jack', 24, city='Beijing', job='Engineer')
            Jack 24 Beijing Engineer
        • 如果函數定義中已經有了一個可變參數,後面跟着的命名關鍵字參數就不再需要一個特殊分隔符*了:
          • def person(name, age, *args, city, job):
                print(name, age, args, city, job)

            命名關鍵字參數必須傳入參數名,這和位置參數不同。如果沒有傳入參數名,調用將報錯:

參數組合順序:必選參數,默認參數,可變參數,命名關鍵字參數,關鍵字參數

  • 遞歸函數

3. 高級特性

  • 切片

  • 迭代

  • 列表生成式:列表生成式即List Comprehensions,是Python內置的非常簡單卻強大的可以用來創建list的生成式。
  • 生成器generator

  • 迭代器
  • yield關鍵字

4. 函數式編程 Functional Programming

  • 函數式編程

函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之爲沒有副作用。而允許使用變量的程序設計語言,由於函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。

函數式編程的一個特點就是,允許把函數本身作爲參數傳入另一個函數,還允許返回一個函數!

  • 高階函數

變量可以指向函數  --> 如果一個變量只想一個函數可以通過變量來調用這個函數

函數名也是變量:他是指向函數的變量

高階function:就是讓函數能夠接受函數作爲參數

5. Python - logging module

參考鏈接:https://www.jianshu.com/p/e3abceb9ab43

5.1 開始 - 小案例

最開始,我們用最短的代碼體驗一下logging的基本功能。

  • 第一步,通過logging.getLoger函數,獲得一個loger對象,但這個對象暫時是無法使用的。
  • 第二步,logging.basicConfig函數,進行一系列默認的配置,包括format、handler等。
  • 第三步、loger調用setLevel函數定義日誌級別爲DEBUG
  • 最後,logger調用debug函數,輸出一條debug級別的message,顯示在了標準輸出上
import logging
loger=logging.getLoger()
logging.basicConfig()
loger.setLevel('DEBUG')
loger.debug('logsomething')
# 輸出
# out>>DEBUG:root:logsomething

5.2 logging中的日誌級別

logging在生成日誌的時候,有一個日誌級別的機制,默認有以下幾個日誌級別:

CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
NOTSET = 0

每一個logger對象,都有一個日誌級別,它只會輸出高於它level的日誌。

如果一個logger的level是INFO,那麼調用logger.debug()是無法輸出日誌的,而logger.warning()能夠輸出。

一般來說,以上的6個日誌級別完全滿足我們日常使用了。

5.3 logging中的基礎類

logging是python中的一個基礎模塊,它在python中的源碼位置如下:

#主幹代碼
/usr/lib/python2.7/logging/__init__.py

#擴展的handler和config
/usr/lib/python2.7/logging/config.py
/usr/lib/python2.7/logging/handlers.py

組成logging的主幹的幾個基礎類都在__init__.py中:

5.3.1 第一個基礎類:LogRecord

一個LogRecord對象,對應了日誌中的一行數據。通常包含:時間、日誌級別、message信息、當前執行的模塊、行號、函數名……這些信息都包含在一個LogRecord對象裏。

LogRecord對象可以想象成一個大字典

class LogRecord(object):
    #代表一條日誌的類
    def getMessage(self):
        #獲取self.msg

def makeLogRecord(dict):
    #這個方法很重要,生成一個空的LogRecord,然後通過一個字典,直接更新LogRecord中的成員變量
    rv = LogRecord(None, None, "", 0, "", (), None, None)
    rv.__dict__.update(dict)
    return rv   

5.3.2 第二個基礎類:Formatter

Formatter對象是用來定義日誌格式的,LogRecord保存了很多信息,但是打日誌的時候我們只需要其中幾個,Formatter就提供了這樣的功能,它依賴於python的一個功能:

#通過字典的方式,輸出格式化字符串
print '%(name)s:%(num)d' % {'name':'my_name','num': 100}
out>>my_name:100

如果說LogRecord是後面的那個字典,那麼Formatter就是前面的那個格式字符串……的抽象

重要的代碼如下:

class Formatter(object):
    def __init__(self,fmt=None,datefmt=None):
        if fmt:
            self._fmt = fmt
        else:
            #默認的format
            self._fmt = "%(message)s"
    def format(self,record)
        #使用self._fmt進行格式化
        s=self._fmt % record.__dict__
        return s

5.3.3 第三個基礎類:FilterFilterer

Filter類,功能很簡單。Flter.filter()函數傳入一個LogRecord對象,通過篩選返回1,否則返回0。從代碼中可以看到,其實是對LogRecord.name的篩選。

Filterer類中有一個Filter對象的列表,它是一組Filter的抽象。

重要的代碼如下:

class Filter(object):
    def __init__(self,name=''):
        self.name=name
        self.nlen=len(name)
    def filter(self,record)
        #返回1表示record通過,0表示record不通過
        if self.nlen==0:
            return 1
        elif self.name==record.name:
            return 1
        #record.name不是以filter開頭
        elif record.name.find(self.name,0,self.nlen) !=0:
            return 0
        #最後一位是否爲.
        return (record.name[self.nlen] == ".")

class Filterer(object):
    #這個類其實是定義了一個self.filters=[]的列表管理多個filter
    def addFilter(self,filter)
    def removeFilter(self,filter)
    def filter(self,record):
        #使用列表中所有的filter進行篩選,任何一個失敗都會返回0
        #例如:
        #filter1.name='A',filter2.name='A.B',filter3.name='A.B.C'
        #此時record.name='A.B.C.D'這樣的record才能通過所有filter的篩選

5.4 logging中的高階類

有了以上三個基礎的類,就可以拼湊一些更重要的高級類了,高級類可以實現logging的重要功能。

5.4.1 Handler——抽象了log的輸出過程

  • Handler類繼承自Filterer。Handler類是log輸出這個過程的抽象。
  • 同時Handler類具有一個成員變量self.level,在第二節討論的日誌級別的機制,就是在Handler中實現的。
  • Handler有一個emit(record)函數,這個函數負責輸出log,必須在Handler的子類中實現。

重要代碼如下:

class Handler(Filterer):
    def __init__(self,level=NOTSET):
        #handler必須有level屬性
        self.level=_checkLevel(level)
    def format(self,record):
        #使用self.formatter,formatrecord
    def handle(self,record):
        #如果通過filter的篩選,則emit這條log
        rv=self.filter(record)
        self.emit(record)
    def emit(self,record):
        #等待子類去實現

接下來看兩個簡單的handler的子類,其實在logging源碼中,有一個handler.py專門定義了很多更復雜的handler,有的可以將log緩存在內存中,有的可以將log做rotation等

5.4.1.1 StreamHandler

最簡單的handler實現,將log寫入一個流中,默認的stream是sys.stderr

重要的代碼如下:

class StreamHandler(Handler):
    def __init__(self, stream=None):
        if stream is None:
            stream=sys.stderr
        self.stream=stream
    def emit(self,record):
        #將record的信息寫入流中
        #處理一些編碼的異常
        fs='%s\n' #每條日誌都有換行
        stream=self.stream
        stream.write(fs % msg)

5.4.1.2 FileHandler

將log輸出到文件的handler,繼承自StreamHandler

重要代碼如下:

class FileHandler(StreamHandler):
    def __init__(self,filename, mode='a'):
        #append方式,打開一個文件
        StreamHandler.__init__(self, self._open())
    def emit(self,record):
        #和streamhandler保持一致
        StreamHandler.emit(self, record)

5.4.2 Logger —— 一個獨立的log管道

什麼是logger?

  • logger類繼承自Filterer,
  • logger對象有logger.level日誌級別
  • logger對象控制多個handler:logger.handlers=[]
  • logger對象之間存在父子關係

簡單的來說,logger這個類,集中了我們以上所有的LogRecord類、Filter類、Formatter類、handler類。首先,logger根據輸入生成一個LogRecord對象,經過Filter和Formatter之後,再通過self.handlers列表中的所有handler,把log發送出去。一個logger中可能有多個handler,可以實現把一份log放到多個任意的位置。

重要代碼:

class Logger(Filterer):
    def __init__ (self,name,level=NONSET):
        #handler列表
        self.handlers=[]
        self.level=_checkLevel(level)
    def addHandler(self,hdlr):
    def removeHandler(self,hdlr):
    def _log(self, level, msg, args, exc_info=None, extra=None):
        #在_log函數中創建了一個LogRecord對象
        record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
        #交給handle函數
        self.handle(record)
    def handle(self,record):
        #進行filter,然後調用callHandlers
        if (not self.disabled) and self.filter(record):
            self.callHandlers(record)
    def callHandlers(self, record):
        #從當前logger到所有的父logger,遞歸的handl傳入的record
        c=self
        while c:
            for hdlr in c.handlers:
                hdlr.handle(record) #進入handler的emit函數發送log
            ……
            c=c.parent

5.4.3 Manager —— 管理logger的類

Manager這個類,對用戶其實是不可見的,如果生成了Logger,Manager就會自動存在,Manager對象負責管理所有的Logger。

logger和manager的關係,總結了一下幾條:

  • Logger是輸出log的對象,Manager類提供了管理多個Logger的功能。
  • 一個程序中只能有一個manager對象,生成manager時,必定也會生成RootLogger,manager對象中的self.root指向了RootLogger
  • manager對象中的self.loggerDict,這個字典保存了當前所有的logger對象(不包含rootlogger)
  • 如果使用logging.getLogger的name爲空,那麼默認指向了name爲'root'的RootLogger
  • 如果使用logging.getLogger的name不爲空,生成的logger會自動掛載到RootLogger下,除非指定其他的父logger
  • 其他的logger通過name建立父子關係

父子關係示例:

loger1=logging.getLogger('A')
loger2=logging.getLogger('A.B')
#loger2的父loger是loger1
loger2.parent

# out>><logging.Logger object at 0xb7230d6c>
# loger1的父loger是rootlogger
# loger1.parent
# out>><logging.RootLogger object at 0xb7230b6c>

這些關係都在manager中進行管理

重要的代碼:

class Manager(object):
    def getLogger(self,name):
        #生成一個logger,將logger中的manager指向self
        #維護所有logger的父子關係
    def _fixupParents(self,aloger):
    def _fixupChildren(self,ph,aloger):
        #修復所有logger的父子關係

5.4.4 LoggerAdapter —— 對標準logger的一個擴展

LogRecord這個大字典中提供的成員變量已經很多,但是,如果在輸出log時候仍然希望能夠夾帶一些自己想要看到的更多信息,例如產生這個log的時候,調用某些函數去獲得其他信息,那麼就可以把這些添加到Logger中,LoggerAdapter這個類就起到這個作用。

LoggerAdapter這個類很有意思,如果不做什麼改動,那麼LoggerAdapter類和Logger並沒有什麼區別。LoggerAdapter只是對Logger類進行了一下包裝

LoggerAdapter的用法其實是在它的成員函數process()的註釋中已經說明了:

def process(self, msg, kwargs):
    """
    Normally, you'll only need to override this one method in a
    LoggerAdapter subclass for your specific needs.
    """

也就是說重寫process函數,以下是一個例子:
 

import logging
import random
L=logging.getLogger('name')

#定義一個函數,生成0~1000的隨機數
def func():
    return random.randint(1,1000)

class myLogger(logging.LoggerAdapter):
    #繼承LoggerAdapter,重寫process,生成隨機數添加到msg前面
    def process(self,msg,kwargs):
        return '(%d),%s' % (self.extra['name'](),msg)  ,kwargs

# 函數對象放入字典中傳入 
LA=myLogger(L,{'name':func})

# now,do some logging
LA.debug('some_loging_messsage')

# out>>DEBUG:name:(167),some_loging_messsage

5.5 logging中的config函數

5.5.1 def basicConfig(**kwargs)

basicConfig函數將對各種參數進行配置,如果不傳入參數,則進行默認配置:

  1. format配置
  2. handler配置
  3. level配置

6. session & Cookie

  • 由於HTTP協議是無狀態的協議(發送一次請求即斷開),所以服務端需要記錄用戶的狀態時,就需要用某種機制來識具體的用戶,這個機制就是Session. 典型的場景比如購物車,當你點擊下單按鈕時,由於HTTP協議無狀態,所以並不知道是哪個用戶操作的,所以服務端要爲特定的用戶創建了特定的Session,用用於標識這個用戶,並且跟蹤用戶,這樣才知道購物車裏面有幾本書。這個Session是保存在服務端的,有一個唯一標識。在服務端保存Session的方法很多,內存、數據庫、文件都有。集羣的時候也要考慮Session的轉移,在大型的網站,一般會有專門的Session服務器集羣,用來保存用戶會話,這個時候 Session信息都是放在內存的,使用一些緩存服務比如Memcached之類的來放 Session。
  • 思考一下服務端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發送相應的Cookie信息到服務端。實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次創建Session的時候,服務端會在HTTP協議中告訴客戶端,需要在 Cookie 裏面記錄一個Session ID,以後每次請求把這個會話ID發送到服務器,我就知道你是誰了。有人問,如果客戶端的瀏覽器禁用了 Cookie 怎麼辦?一般這種情況下,會使用一種叫做URL重寫的技術來進行會話跟蹤,即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。
  • Cookie其實還可以用在一些方便用戶的場景下,設想你某次登陸過一個網站,下次登錄的時候不想再次輸入賬號了,怎麼辦?這個信息可以寫到Cookie裏面,訪問網站的時候,網站頁面的腳本可以讀取這個信息,就自動幫你把用戶名給填了,能夠方便一下用戶。這也是Cookie名稱的由來,給用戶的一點甜頭。
  • 總結一下:
    • Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集羣、數據庫、文件中;
    • Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式。
    • Cookie是寫在客戶端的,很容易被篡改,不適合存放敏感信息;而session保存在服務器,客戶端只是接收一個隨機字符串,相對安全。

7. decorator

python中函數也是一個對象,所以可以將:

1. 函數複製給變量

2. 將函數當做參數

3. 返回一個函數

decorator就是一個:使用函數作參數並且返回函數的函數。通過改進我們可以得到:

  • 更簡短的代碼,將結合點放在函數定義時
  • 不改變原函數的函數名

在Python解釋器發現login調用時,他會將login轉換爲printdebug(login)()。也就是說真正執行的是__decorator 函數

Decorators are a shortcut to applying wrapper functions. This is helpful to “wrap” functionality with the same code over and over again

裝飾器是一個函數,其主要用途是包裝另一個函數或類。這種包裝的首要目的是透明地修改或增強被包裝對象的行爲。

8. 一些常用的庫

8.1 pandas

Python Data Analysis Library 或 pandas 是基於NumPy 的一種工具,該工具是爲了解決數據分析任務而創建的。Pandas 納入了大量庫和一些標準的數據模型,提供了高效地操作大型數據集所需的工具。pandas提供了大量能使我們快速便捷地處理數據的函數和方法。你很快就會發現,它是使Python成爲強大而高效的數據分析環境的重要因素之一。

8.2 matplotlib

Matplotlib 是一個 Python 的 2D繪圖庫,它以各種硬拷貝格式和跨平臺的交互式環境生成出版質量級別的圖形。

鏈接:http://python.jobbole.com/85106/

8.3 pickle

序列化和反序列化的庫

參考:https://docs.python.org/3/library/pickle.html

8.4 numpy

NumPy系統是Python的一種開源的數值計算擴展。這種工具可用來存儲和處理大型矩陣,比Python自身的嵌套列表(nested list structure)結構要高效的多(該結構也可以用來表示矩陣(matrix))。

9. 一些常問的問題總結

  1. Python中__all__的用法:它是一個string元素組成的list變量,定義了當使用 from <module> import * 導入某個模塊的時候能導出的符號 (這個符號代表變量,函數,類等)
  2. Python class中__init__()方法的作用
  3. hasattr(object, name):判斷對象是否包含對應的屬性
  4. Python pop(key[,default])刪除字典給定鍵key及對應的值,返回值爲本刪除的值
  5. python中*args,**kwargs
    • *args 和 **kwargs主要用於函數定義,將不定數量的參數傳遞給一個函數
    • *args 用來發送一個非鍵值對的可數變量的參數列表給一個函數
    • **kwargs允許將不定長度的鍵值對,作爲參數傳給一個函數。如果想要在一個函數裏處理帶名字的參數,應使用**kwargs
    • *會把list或者tuple分解爲一個個參數傳遞給函數
    • **會把dict轉成關鍵字參數
    • 函數定義和函數調用,可以同時出現*或者**,如foo3(*args,**kwargs),但是必須*在前,**在後
    • 出現在函數調用時,一定要注意參數匹配問題 some_func(fargs, *args, **kwargs

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章