python 隨筆

python 學習筆記html

運算符重載

PYTHON-進階-魔術方法小結(方法運算符重載)
python有着像C++類似的運算符重載,只須要在類中重寫__add__、sub 等方法,就能夠直接對對象進行 + - 等操做,就好像內置對象同樣。也能夠重寫__getattr__、__setattr__等方法來操做屬性,initdelstr 、__len__等基本方法均可以重載,比較符的重載包括cmplt、__gt__等,以及getitem、__setitem__等操做索引的方法。總之,徹底能夠經過重載將一個類寫的python之父都不認得。python

coroutine

傳統咱們實現消費者 生產者模型 都是須要經過多線程之間 互相協做,一個線程生產,一個線程消費。協程就是將多線程的事情在一個線程裏面幹了。傳統咱們一個函數老是一個入口,一個出口,很明確。協程就是容許一個子程序內部能夠中斷,而後執行其餘子程序,在適當的時候再返回來繼續執行,即便在while True這樣的函數。這樣的話咱們就能夠生產 後就調用消費者,而後消費後再繼續生產。
python 2.x中實現協程的方式 是經過generator,yield 中止一個子程序後,能夠經過send(xxx) 繼續執行
協程的好處:sql

  1. 極高的效率,不須要操做系統切換線程,只是程序內部控制segmentfault

    由於協程是一個線程執行,那怎麼利用多核CPU呢?最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可得到極高的性能。數組

  2. 並且也不須要多線程的鎖機制。。。由於這根本就只有一個線程嘛
  3. 經過協程也能夠將一個複雜的程序分紅多個相互協做的子程序,就好像unix 的管道同樣,read ---> grep ---> print 這樣,程序會更簡單明瞭,複用性更高多線程

# 簡潔高效的生產消費模式

# 裝飾器, 用來自動啓動協程
def coroutine(func):
    def go(*args, **kwargs):
        g = func(*args, **kwargs)
        g.send(None)
        return g
    return go


@coroutine
def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print ('consume {}'.format(n))
        time.sleep(1)
        r = '200 OK'


def producer(target):
    while True:
        n = random.randint(1, 100)
        print ('produce {}'.format(n))
        r = target.send(n)
        print ('consumer return {}'.format(r))


if __name__ == '__main__':
    producer(consumer())

generator

generator 跟普通的方法差很少,只是能夠經過yield返回多個值,以下閉包

def gen():
    yield 1
    yield 2

g = gen()
print g # 輸出<generator object gen at 0x00000000026BCFC0>
print g.next() # 輸出1
print g.next() # 輸出2
print g.next() # StopIteration 異常

經過dir(g) 能夠看到generator 實現了__iter__ 和 next 方法,因此generator 也是一個迭代器, 生成器能夠用來簡化迭代器的實現app

# 簡化版的小寫字母生成器,相對自定義迭代器來講,真的簡單多了
def lower_letters():
    current = 'a'
    while current <= 'z':
        yield current
        current = chr(ord(current) + 1)

for letter in lower_letters():
    print letter

iterator & iterable

python 中的for in語法,能夠遍歷iterable 對象,例如list,array,map等,其實就是用iter()構造了一個iterator,並捕獲StopIteration異常dom

_iter = iter(iterableObj)
while True:
    try:
        x = _iter.next()
    except StopIteration:
        break
    do_your_func(x)

iterator 和 iterable 本質上的區別是 iterator 實現了__next__(python2.x 是next)和 __iter__方法,而iterable只實現了__iter__方法,經過dir([1,2]) 能夠看到數組中只實現了iter ,只是iterable對象函數

因此,咱們能夠自定義一個迭代器,只要實現了__next__ 和 iter,而且__next__ 拋出StopIteration異常

class UpperLetter(object):
    def __init__(self):
        self.current = 'A'

    def next(self):
        if self.current > 'Z':
            raise StopIteration()

        result = self.current
        self.current = chr(ord(self.current) + 1)
        return result

    def __iter__(self):
        return self


letters = UpperLetter()
# 自定義的迭代器能夠經過for in遍歷哦
for letter in letters:
    print (letter)

下劃線

方法先後雙下劃線

約定這是python中的特殊方法,一般你將覆蓋這些方法,實現所需的功能,例如__init__

方法前單下劃線

約定這是私有方法,外部不能訪問。
對於解釋器來講, from <模塊 包名> import * 是沒法導入以_開頭的方法

拷貝

python都對象之間賦值都是拷貝引用,若是要拷貝對象,須要使用copy模塊

  1. copy.copy 淺拷貝 只拷貝父對象,不會拷貝對象的內部的子對象。
  2. copy.deepcopy 深拷貝 拷貝對象及其子對象
userInfo = {
    "resultCode": 0,
    "users": {
        'name': "jobs"
    }
}

a = userInfo
b = copy.copy(userInfo)
c = copy.deepcopy(userInfo)

# 修改原來的對象
userInfo['resultCode'] = 1
userInfo['users']['name'] = 'ryan'

print a
print b
print c
# 輸出:
# {'resultCode': 1, 'users': {'name': 'ryan'}}
# {'resultCode': 0, 'users': {'name': 'ryan'}}
# {'resultCode': 0, 'users': {'name': 'jobs'}}

能夠看到,傳引用的全改了,淺拷貝的話子對象仍是保留着原來的引用,因此子對象跟着改了,深拷貝巋然不動!

切片

切片是python一個頗有趣的語法糖,提供了簡潔的方法來取一個數組的一部分

L=[1, 2, 3,4,5]
print L[1: 3] # 取索引1到索引3,不包含索引3
print L[:] # 第一個參數不填表示第一位,最後一個參數不填表示取最後一位
print L[-2:] # 負數表示倒數第n個元素
print L[::2] # 最後一個參數表示隔幾個取一次,跟range相似

# 輸出
# [2, 3]
# [1, 2, 3, 4, 5]
# [4, 5]
# [1, 3, 5]

# 優雅地將數組 進行分組操做,這裏若是i + 3大於數組len, 切片也能自動切到最後一截哈哈
for i in range(0, len(L), 3):
    print L[i: i+3]
# 輸出
# [1, 2, 3]
# [4, 5]

閉包

定義

父函數中返回的函數引用了父函數中的參數或者局部變量,這個函數就稱爲閉包。所以,閉包也是攜帶狀態的函數,而且它的狀態能夠對外隱藏起來。閉包之於父函數就好像實例之於類

閉包修改外部變量?

通常來講閉包不能修改外部變量,由於對於同名變量的修改 python 把它看作了 子函數中的 局部變量,因此你直接修改會返回UnboundLocalError: local variable 'm' referenced before assignment錯誤(聽說在python3中能夠用nolocal 聲明外部變量便可修改)

閉包實現的原理

閉包函數相對於普通函數多出了一個__closure__屬性,這是一個元組,這個元祖裏面包含了全部閉包函數中引用到的外部變量

參考:
說說 Python 中的閉包


裝飾器

裝飾器就是一種特殊的閉包,經過傳遞一個待修飾的函數 給 修飾函數,並返回一個修飾後的函數,能夠用@decoratorFunc 來簡化語法,裝飾器的做用就是爲已經存在的函數或對象添加額外的功能

參考:
Python 的閉包和裝飾器:https://segmentfault.com/a/1190000004461404
詳解 Python 的裝飾器

列表生成式/字典生成式(簡潔美觀)

[ x * x for x in list] & { x[a] : x[b] for x in list}

python 變量做用域

  1. python的做用域是一層一層向外查的,可是若是在內層想要改變外層的變量,就須要聲明global,不然只是至關於定義了一個覆蓋全局的局部變量,以下
var = 1

def func1():
    # 此處聲明 global var 解決
    var = var + 1
    return var

print func1() 
# 報錯UnboundLocalError: local variable 'var' referenced before assignment
# 由於咱們企圖在內層改變外層的var,因此python就把他當作局部變量了,那很明顯,這個var在局部並無賦值
  1. 一樣的,對於閉包,也是儘可能不要在閉包中修改外部函數的變量,理由同上(能夠聲明nonlocal)
  2. 除了def/class/lambda以外,其餘如:if/else try/except for/while並不能改變其做用域. 略坑啊,定義在這些東西里面的變量,外部是能夠訪問的。。。

with as 字句

用with as 字句代替以前的try .... finally fs.close()之類的語句方便了不少

tuple

tuple 就是不可變的list, 用(1, 2, 3)定義
**若是要定義只有一個元素的tuple,不能定義爲(1),python把他當作數字1(括號運算),正確的寫法爲(1, )

參數

默認參數的坑

python 的默認參數若是爲數組的話,要千萬當心了

def func2(array=[]):
    array.append("haha")
    print array

func2()  # 輸出 ['haha']
func2()  # 輸出 ['haha', 'haha']

爲何每次調用的結果都不同? 由於默認參數的值在函數定義的時候就生成好了,因此當默認參數指向的是 可變對象如list
就會改變它的值,因此**默認參數必定要是不可變對象"",如str,int等

可變參數

要往可變參數中傳入一個數組,有兩種方法

def func3(*arr):
    for i in arr:
        print i

nums = [1, 2, 3]
func3(nums[0], nums[1], nums[2])  # 不得不說,這種寫法巨醜
func3(*nums)  # 好看

關鍵字參數

可變參數在方法中就是一個tuple,而關鍵字參數在方法中就是一個dict,它能夠擴展函數的功能,讓用戶自定義配置,例如sqlalchemy的create_engine(*args, kwargs)就使咱們能夠根據須要定義encoding,echo等參數
一樣的,要傳入一個dict給可變參數的方法能夠用**dict**

星解

星解提供了一個很藝術化的方法來unpack一個list或dict

def test(x, y, z):
    print(x, y, z)
 
testDict = {'x': 1, 'y': 2, 'z': 3} 
testList = [10, 20, 30]
 
test(*testDict)
test(*testList)
 
#1-> x y z
#2-> 10 20 30
相關文章
相關標籤/搜索