1. 程式人生 > >python拓展知識

python拓展知識

類方法self的作用

未繫結的類方法:沒有self,通過類來引用方法返回一個未繫結方法物件。要呼叫它,你必須顯示地提供一個例項作為第一個引數。

繫結的例項方法:有self,通過例項訪問方法返回一個繫結的方法物件。Python自動地給方法繫結一個例項,所以我們呼叫它時不用再傳一個例項引數。

class Test(object):

    def func(self, message):
        print message


obj = Test()
x = obj.func
# x 是一個繫結的方法物件, 不需要傳遞一個例項引數
x('hi')

# fn 是一個未繫結的方法物件, 需要傳遞一個例項引數
fn = Test.func fn(obj, 'hi') # fn('hi') # 錯誤的呼叫

type關鍵字

既可以檢視一個物件的型別 ,也可以建立類物件

檢視物件型別

a = 1
type(a)
# <type 'int'>

b = 'hello'
type(b)
# <type 'str'>

 

建立類物件

# 需要給一個引數來繫結類
def fn(self):
    print 'hello world'

Hello = type('Hello', (object,), dict(hello=fn))

h 
= Hello() h.hello()

 

locals() 和 globals() 的區別

locals()返回區域性所有型別的變數的字典, globals()返回當前位置的全部全域性變數的字典

def test(arg):
    z = 1
    print locals()

test(4)
# {'z': 1, 'arg': 4}
print globals()
# {'__builtins__': <module '__builtin__' (built-in)>, '__file__': '...', '__package__': None, 'test': <function test at 0x10ea970c8>, '__name__': '__main__', '__doc__': None}

 

字串title函式

將字串的每個單詞的首字母大寫,下劃線連線的單詞首字母也要大寫

str = 'my_str'
print str.title()
# My_Str

 

字串的rpartition函式

以某個分隔符將字串分成兩段,取最後一個匹配的位置的分隔符分隔,返回一個三元組(head, sep, tail)

print 'django.core.app'.rpartition('.')
# ('django.core', '.', 'app')

python特殊函式__call__

所有的函式都是可呼叫物件,一個類例項也可以變成一個可呼叫物件,只需實現一個特殊方法__call__()

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __call__(self, job):
        print 'My name is %s' % self.name
        print 'My gender is %s' % self.gender
        print 'My job is %s' % job

p = Person('Bob', 'male')
p('student')

python strip(), lstrip(), rstrip()函式

strip(): 返回字串的副本,並刪除前導和字尾字元

lstrip(): 返回字串的副本,刪除前導字元

rstrip(): 返回字串的副本,刪除字尾字元

str = ' hello '
print len(str)  # 5
print len(str.strip())  # 7

str = 'rhellor'
print str.strip('r')  # hello
print str.lstrip('r')  # hellor
print str.rstrip('r')  # rhello

random.shuffle()

random.shuffle 將列表隨機排序

import random

li = [1, 2, 3, 4, 5]
random.shuffle(li)

for i in li:
    print i

 os.path模組

os.path.basename(path):獲取對應路徑下的檔名

os.path.abspath(path):返回path規範化的絕對路徑

os.path.split(path): 將path分割成目錄和檔名二元組返回

os.path.dirname(path): 返回path的目錄

os.path.exists(path): 如果path存在,返回True;如果path不存在,返回False

os.path.isabs(path): 如果path是絕對路徑,返回True

os.path.isfile(path): 如果path是一個存在的檔案,返回True。否則返回False

os.path.isdir(path): 如果path是一個存在的目錄,則返回True。否則返回False

os.path.splitext(path): 分離檔名與副檔名;預設返回(fname,fextension)元組,可做分片操作

os.path.getatime(path): 返回path所指向的檔案或者目錄的最後存取時間

os.path.getmtime(path): 返回path所指向的檔案或者目錄的最後修改時間

os.path.join(path1[, path2[, path3..]]):

os.path.join 可以傳入多個引數

  • 會從第一個以”/”開頭的引數開始拼接,之前的引數全部丟棄;
  • 以上一種情況為先。在上一種情況確保情況下,若出現”./”開頭的引數,會從”./”開頭的引數的上一個引數開始拼接。
import os

print "1:", os.path.join('aaaa', '/bbbb', 'ccccc.txt')
# 1: /bbbb/ccccc.txt

print "2:", os.path.join('/aaaa', '/bbbb', '/ccccc.txt')
# 2: /ccccc.txt

print "3:", os.path.join('aaaa', './bbb', 'ccccc.txt')
# 3: aaaa/./bbb/ccccc.txt

 os.stat()

方法用於在給定的路徑上執行一個系統 stat 的呼叫

import os
print os.stat("/root/python/zip.py")
# (33188, 2033080, 26626L, 1, 0, 0, 864, 1297653596, 1275528102, 1292892895)
print os.stat("/root/python/zip.py").st_mode   #許可權模式
# 33188
print os.stat("/root/python/zip.py").st_ino   #inode number
# 2033080
print os.stat("/root/python/zip.py").st_dev    #device
# 26626
print os.stat("/root/python/zip.py").st_nlink  #number of hard links
# 1
print os.stat("/root/python/zip.py").st_uid    #所有使用者的user id
# 0
print os.stat("/root/python/zip.py").st_gid    #所有使用者的group id
# 0
print os.stat("/root/python/zip.py").st_size  #檔案的大小,以位為單位
# 864
print os.stat("/root/python/zip.py").st_atime  #檔案最後訪問時間
# 1297653596
print os.stat("/root/python/zip.py").st_mtime  #檔案最後修改時間
# 1275528102
print os.stat("/root/python/zip.py").st_ctime  #檔案建立時間
# 1292892895

 

socket.getaddrinfo()

函式原型:socket.getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]])

返回值:[(family, socktype, proto, canonname, sockaddr)]有元組組成的列表,元組裡麵包含5個元素,其中sockaddr是(host, port)

  • family:表示socket使用的協議簇,常用的協議簇包括AF_UNIX(本機通訊) 、AF_INET(TCP/IP協議簇中的IPv4協議)、AF_INET6(TCP/IP協議簇中的IPv4協議)。python的socket包中AF_UNIX=1, AF_INET=2, AF_INET6=10
  • sockettype:表示socket的型別,常見的socket型別包括SOCK_STREAM(TCP流)、SOCK_DGRAM(UDP資料報)、SOCK_RAW(原始套接字),SOCK_STREAM=1,  SOCK_DGRAM=2, SOCK_RAW=3
  • proto:套介面所用的協議,若不指定,可用0,常用的協議有IPPROTO_TCP(6)和IPPTOTO_UDP(17),他們分別對應TCP傳輸協議、UDP傳輸協議
import socket
print socket.getaddrinfo('www.baidu.com', 80)
# [(2, 2, 17, '', ('180.97.33.108', 80)), (2, 1, 6, '', ('180.97.33.108', 80)), (2, 2, 17, '', ('180.97.33.107', 80)), (2, 1, 6, '', ('180.97.33.107', 80))]

struct模組pack和unpack

pack(fmt, v1, v2….):按照給定格式將資料封裝成字串(實際類似於c結構體的位元組流)

unpack(fmt, string ):按照給定格式解析位元組流,返回解析出的tuple

calcsize(fmt):計算給定的格式佔用多少位元組的記憶體

參考部落格: https://blog.csdn.net/w83761456/article/details/21171085

# coding: utf-8
import struct

a = 'hello'
b = 'world!'
c = 2
d = 45.123

bytes = struct.pack('5s6sif', a, b, c, d)
print bytes
# helloworld!�}4B

a, b, c, d = struct.unpack('5s6sif', bytes)
print a, b, c, d
# hello world! 2 45.1230010986

metaclass

允許你建立類或者修改類。換句話說,你可以把類看成是metaclass創建出來的“例項”。多喜歡自定義__new__方法

class Trick(FlyToSky):

  pass

當我們在建立上面的類的時候,python做了如下的操作:

Trick中有__metaclass__這個屬性嗎?如果有,那麼Python會在記憶體中通過__metaclass__建立一個名字為Trick的類物件,也就是Trick這個東西。如果Python沒有找到__metaclass__,它會繼續在自己的父類FlyToSky中尋找__metaclass__屬性,並且嘗試以__metaclass__指定的方法建立一個Trick類物件。如果Python在任何一個父類中都找不到__metaclass__,它也不會就此放棄,而是去模組中搜尋是否有__metaclass__的指定。如果還是找不到,那就是使用預設的type來建立Trick。

那麼問題來了,我們要在__metaclass__中放置什麼呢?答案是可以建立一個類的東西,type,或者任何用到type或子類化type的東西都行。

參考部落格:  https://www.cnblogs.com/piperck/p/5840443.html

class UpperAttrMetaClass(type):
    def __new__(mcs, class_name, class_parents, class_attr):
        attrs = ((name, value) for name, value in class_attr.items() if not name.startswith('__'))
        uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaClass, mcs).__new__(mcs, class_name, class_parents, uppercase_attrs)


class Trick(object):
    __metaclass__ = UpperAttrMetaClass
    bar = 12
    money = 'unlimited'

print Trick.BAR
print Trick.MONEY

設定守護程序 setDaemon(flag)

當一個程序啟動之後,會預設產生一個主執行緒,因為執行緒是程式執行流的最小單元,當設定多執行緒時,主執行緒會建立多個子執行緒,在python中,預設情況下(其實就是setDaemon(False)),主執行緒執行完自己的任務以後,就退出了,此時子執行緒會繼續執行自己的任務,直到自己的任務結束,當我們使用setDaemon(True)方法,設定子執行緒為守護執行緒時,主執行緒一旦執行結束,則全部執行緒全部被終止執行,可能出現的情況就是,子執行緒的任務還沒有完全執行結束,就被迫停止。

執行緒join方法

join所完成的工作就是執行緒同步,即主執行緒任務結束之後,進入阻塞狀態,一直等待其他的子執行緒執行結束之後,主執行緒再終止。

join有一個timeout引數:

  • 設定守護執行緒,主執行緒對於子執行緒等待timeout的時間將會殺死該子執行緒,最後退出程式。所以說,如果有10個子執行緒,全部的等待時間就是每個timeout的累加和。簡單的來說,就是給每個子執行緒一個timeout的時間,讓他去執行,時間一到,不管任務有沒有完成,直接殺死。
  • 沒有設定守護執行緒,主執行緒將會等待timeout的累加和這樣的一段時間,時間一到,主執行緒結束,但是並沒有殺死子執行緒,子執行緒依然可以繼續執行,直到子執行緒全部結束,程式退出。

參考部落格:https://www.cnblogs.com/cnkai/p/7504980.html

 

functools.wraps

在使用裝飾器時難免會損失一些東西,使用functools.wraps可以將原函式物件的指定屬性複製給包裝函式物件, 預設有 __module__、__name__、__doc__,或者通過引數選擇。

from functools import wraps

def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)

    return with_logging

@logged
def f(x):
    """does some math"""
    return x + x * x

print f.__name__
# 不加wraps: with_logging
# 加wraps: f
print f.__doc__
# 不加wraps: None
# 加wraps: does some math

 

孤兒程序和殭屍程序

什麼是孤兒程序?

一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。

什麼是殭屍程序?

首先核心會釋放終止程序(呼叫了exit系統呼叫)所使用的所有儲存區,關閉所有開啟的檔案等,但核心為每一個終止子程序儲存了一定量的資訊。這些資訊至少包括程序ID,程序的終止狀態,以及該程序使用的CPU時間,所以當終止子程序的父程序呼叫wait或waitpid時就可以得到這些資訊。

而殭屍程序就是指:一個程序執行了exit系統呼叫退出,而其父程序並沒有為它收屍(呼叫wait或waitpid來獲得它的結束狀態)的程序。

任何一個子程序(init除外)在exit後並非馬上就消失,而是留下一個稱為殭屍程序的資料結構,等待父程序處理。這是每個子程序都必需經歷的階段。另外子程序退出的時候會向其父程序傳送一個SIGCHLD訊號。

如何避免殭屍程序?

  • 通過signal(SIGCHLD, SIG_IGN)通知核心對子程序的結束不關心,由核心回收。如果不想讓父程序掛起,可以在父程序中加入一條語句:signal(SIGCHLD,SIG_IGN);表示父程序忽略SIGCHLD訊號,該訊號是子程序退出的時候向父程序傳送的。
  • 父程序呼叫wait/waitpid等函式等待子程序結束,如果尚無子程序退出wait會導致父程序阻塞。waitpid可以通過傳遞WNOHANG使父程序不阻塞立即返回。
  • 如果父程序很忙可以用signal註冊訊號處理函式,在訊號處理函式呼叫wait/waitpid等待子程序退出。
  • 通過兩次呼叫fork。父程序首先呼叫fork建立一個子程序然後waitpid等待子程序退出,子程序再fork一個孫程序後退出。這樣子程序退出後會被父程序等待回收,而對於孫子程序其父程序已經退出所以孫程序成為一個孤兒程序,孤兒程序由init程序接管,孫程序結束後,init會等待回收。

參考部落格:https://www.cnblogs.com/Anker/p/3271773.html

selectpollepoll三者的區別

為何使用select 單程序實現併發?

因為一個程序實現多併發比多執行緒是實現多併發的效率還要高,因為啟動多執行緒會有很多的開銷,而且CPU要不斷的檢查每個執行緒的狀態,確定哪個執行緒是否可以執行。

那麼單程序是如何實現多併發的呢?

之前的socket一個程序只能接收一個連線,當接收新的連線的時候產生阻塞,因為這個socket程序要先和客戶端進行通訊,二者是彼此互相等待的【客戶端發一條訊息,服務端收到,客戶端等著返回....服務端等著接收.........】一直在阻塞著,這個時候如果再來一個連線,要等之前的那個連線斷了,這個才可以連進來。也就是說用基本的socket實現多程序是阻塞的。為了解決這個問題採用每來一個連線產生一個執行緒,是不阻塞了,但是當執行緒數量過多的時候,對於cpu來說開銷和壓力是比較大的。

客戶端發起一個連線,會在服務端註冊一個檔案控制代碼,服務端會不斷輪詢這些檔案控制代碼的列表,主程序和客戶端建立連線而沒有啟動執行緒,這個時候主程序和客戶端進行互動,其他的客戶端是無法連線主程序的,為了實現主程序既能和已連線的客戶端收發訊息,又能和新的客戶端建立連線,就把輪詢變的非常快(死迴圈)去刷客戶端連線進來的檔案控制代碼的列表,只要客戶端發訊息了,服務端讀取了訊息之後,有另一個列表去接收給客戶端返回的訊息,也不斷的去刷這個列表,刷出來後返回給客戶端,這樣和客戶端的這次通訊就完成了,但是跟客戶端的連線還沒有斷,但是就進入了下一次的輪詢。

三者區別

select:單程序實現併發

poll: 它和select在本質上沒有多大差別,但是poll沒有最大檔案描述符數量的限制。

epoll: 直到Linux2.6才出現了由核心直接支援的實現方法,那就是epoll,它幾乎具備了之前所說的一切優點,被公認為Linux2.6下效能最好的多路I/O就緒通知方法, epoll可以同時支援水平觸發和邊緣觸發, epoll同樣只告知那些就緒的檔案描述符,而且當我們呼叫epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表 就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體對映(mmap)技術,這樣便徹底省掉了 這些檔案描述符在系統呼叫時複製的開銷, 另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描 述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥 機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()時便得到通知。

水平觸發和邊緣觸發

水平觸發:select()和poll()將就緒的檔案描述符告訴程序後,如果程序沒有對其進行IO操作,那麼下次呼叫select()和poll() 的時候將再次報告這些檔案描述符,所以它們一般不會丟失就緒的訊息,這種方式稱為水平觸發。

邊緣觸發:只告訴程序哪些檔案描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有采取行動,那麼它將不會再次告知,這種方式稱為邊緣觸發。

參考部落格:https://www.cnblogs.com/qianyuliang/p/6551553.html

atexit模組

atexit只定義了一個register函式用於註冊程式退出時的回撥函式,我們可以在這個回撥函式中做一些資源清理的操作

import atexit

def exit0(*args, **kwarg):
    print 'exit0'
    for arg in args:
        print arg

    for item in kwarg.items():
        print item

def exit1():
    print 'exit1'

atexit.register(exit0, *[1, 2, 3], **{"a": 1, "b": 2, })
atexit.register(exit1)

@atexit.register
def exit2():
    print 'exit2'

# 回撥函式執行的順序與它們被註冊的順序剛才相反
# exit2
# exit1
# exit0
# 1
# 2
# 3
# ('a', 1)
# ('b', 2)

 

shutil模組

高階的 檔案、資料夾、壓縮包處理模組

常用函式

copyfileobj(檔案1,檔案2):將檔案1的資料覆蓋copy給檔案2

copyfile(檔案1,檔案2):不用開啟檔案,直接用檔名進行覆蓋copy

copymode(檔案1,檔案2):之拷貝許可權,內容組,使用者,均不變

copystat(檔案1,檔案):只拷貝了許可權

copy(檔案1,檔案2):拷貝檔案和許可權都進行copy

copy2(檔案1,檔案2):拷貝了檔案和狀態資訊

copytree(源目錄,目標目錄):可以遞迴copy多個目錄到指定目錄下

rmtree(目標目錄):可以遞迴刪除目錄下的目錄及檔案

move(原始檔,指定路徑):遞迴移動一個檔案

make_archive():可以壓縮,打包檔案

psutil模組

psutil是一個跨平臺庫(http://pythonhosted.org/psutil/)能夠輕鬆實現獲取系統執行的程序和系統利用率(包括CPU、記憶體、磁碟、網路等)資訊。它主要用來做系統監控,效能分析,程序管理。它實現了同等命令列工具提供的功能,如ps、top、lsof、netstat、ifconfig、who、df、kill、free、nice、ionice、iostat、iotop、uptime、pidof、tty、taskset、pmap等。目前支援32位和64位的Linux、Windows、OS X、FreeBSD和Sun Solaris等作業系統

參考部落格:https://www.cnblogs.com/saneri/p/7528283.html

inspect模組

主要用處

  • 對是否是模組、框架、函式進行型別檢查
  • 獲取原始碼
  • 獲取類或者函式的引數資訊
  • 解析堆疊

常用函式

getargspec(func):返回一個命名元組ArgSpect(args, varargs, keywords, defaults),args是函式位置引數名列表,varargs是*引數名,keywords是**引數名,defaults是預設引數值的元組。

getmembers(object[, predicate]):返回一個包含物件的所有成員的(name, value)列表。返回的內容比物件的__dict__包含的內容多,原始碼是通過dir()實現的。predicate是一個可選的函式引數,被此函式判斷為True的成員才被返回。

getmodule(object):返回定義物件的模組

getsource(object):返回物件的原始碼

getsourcelines(object):返回一個元組,元組第一項為物件原始碼行的列表,第二項是第一行原始碼的行號

stack():第一列是物件名,第二列是當前指令碼檔名,第三列是行數,第四列是函式名,第五列是具體執行的程式。第一行是當前函式,第二行是父級函式,以此往上推

threading.local()

這個方法的特點用來儲存一個全域性變數,但是這個全域性變數只有在當前執行緒才能訪問,localVal.val = name這條語句可以儲存一個變數到當前執行緒,如果在另外一個執行緒裡面再次對localVal.val進行賦值,那麼會在另外一個執行緒單獨建立記憶體空間來儲存,也就是說在不同的執行緒裡面賦值 不會覆蓋之前的值,因為每個執行緒裡面都有一個單獨的空間來儲存這個資料,而且這個資料是隔離的,其他執行緒無法訪問

import threading
# 建立全域性ThreadLocal物件:
localVal = threading.local()
localVal.val = "Main-Thread"


def process_student():
    print '%s (in %s)' % (localVal.val, threading.current_thread().name)


def process_thread(name):
    #賦值
    localVal.val = name
    process_student()
    

t1 = threading.Thread(target=process_thread, args=('One',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('Two',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
print localVal.val

# One (in Thread-A)
# Two (in Thread-B)
# Main-Thread

logging模組

logging模組是Python內建的標準模組,主要用於輸出執行日誌,可以設定輸出日誌的等級、日誌儲存路徑、日誌檔案回滾等;相比print,具備如下優點:

  • 可以通過設定不同的日誌等級,在release版本中只輸出重要資訊,而不必顯示大量的除錯資訊
  • print將所有資訊都輸出到標準輸出中,嚴重影響開發者從標準輸出中檢視其它資料;logging則可以由開發者決定將資訊輸出到什麼地方,以及怎麼輸出
# 基本使用
import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

參考部落格:https://www.cnblogs.com/liujiacai/p/7804848.html

日誌高階版zipkin

參考部落格:https://blog.csdn.net/liumiaocn/article/details/80657943