標籤:cti 核心 許可權 rap 控制 方法 開放封閉原則 for 佔用
1. 產生器
- 通過列表產生式,可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個
包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數
的元素佔用的空間都白白浪費了。所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的
過程中不斷推算出後續的元素呢?這樣,就不必建立完整的list,從而節省大量的空間。
- 在Python中,這種一邊迴圈一邊計算的機制,稱為產生器(generator)
# 建立產生器的方法一:只要把一個列表產生式的 [] 改成 ()L = [ x*2 for x in range(5)]L # 輸出: [0, 2, 4, 6, 8]G = ( x*2 for x in range(5))G # 輸出: <generator object <genexpr> at 0x1077d9db0>next(G) # 每執行一次 next(),輸出一個元素# 建立產生器的第二種方式: 使用函數# 斐波拉契數列(Fibonacci)def fib(times): n = 0 a, b = 0, 1 while n < times: print(b) a, b = b, a+b n += 1 return 'done'fib(5)# 產生器(ipython 中操作)def fib(): print("=== start ===") a, b = 0, 1 for i in range(5): print("=== 1 ===") yield b print("=== 2 ===") a, b = b, a+b print("=== 3 ===") print("=== stop ===")a = fib()a # 輸出: <generator object fib at 0x23355>next(a) # 等價於 a.__next__()# 輸出:# === start ===# === 1 ===# 1# 此時,fib()函數就是產生器,可以使用迴圈來迭代for num in a: print(num)
1.1 send
# 樣本:# ipython3 中操作def gen(): i = 0 while i<5: temp = yield i print(temp) i+=1a = gen()a.__next__() # 輸出: 0a.__next__() # 輸出: None 1a.send("haha") # 輸出: haha 2# 說明: 程式執行到yield時,gen函數暫時停止,同時返回i的值;# temp 接收send過來的值(haha);# a.__next__() 等價 a.send(None)# 若是第一次直接調用 a.send("haha"), 程式會報錯
2. 迭代器
- 迭代是訪問集合元素的一種方式。
- 迭代器是一個可以記住遍曆的位置的對象;
- 迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束;
- 迭代器只能往前不會後退;
2.1 可迭代對象
- 可以直接作用於 for 迴圈的資料類型有以下幾種:
- 集合資料類型:
list, tuple, dict, set, str等;
generator, 包括產生器和帶 yield的 generation function;
- 這些可以直接作用於for迴圈的對象統稱為可迭代對象(Iterable)
- 可以使用
isinstance() 判斷一個對象是否是 Iterable對象;
# 樣本:from collections import Iterableisinstance([], Iterable) # True
2.2 迭代器
- 可以被
next() 函數調用並不斷返回下一個值的對象稱為迭代器(Iterator);
- 可以使用
isinstance() 判斷一個對象是否是 Iterator 對象;
# 樣本:from collections import Iteratorisinstance((x for x in range(10), Iterator) # Trueisinstance([], Iterator) # False
2.3 iter()函數
- 產生器都是 Iterator 對象;
list, dict, str雖然是 Iterable,卻不是 Iterator;
- 可以使用
iter() 函數把 list,dict, str等 Iterable 變成 Iterator;
# 樣本:from collections import Iteratorisinstance(iter([]), Iterator) # True
3. 閉包
# 樣本一:def test(): print("=== in test func ===")# 調用函數test()# 引用函數, 直接書寫函數名,表示引用函數;# 在 ipython 中運行 test,會輸出: <function __main__.test>ret = testprint(id(ret))print(id(test))ret()# 運行結果:=== in test func ===44092110804409211080=== in test func ===# 樣本二: 閉包定義# 定義一個函數def test1(number): # 在函數內部再定義一個函數,並且這個函數用到了外邊函數(test1)的變數(number), # 那麼將這個函數以及用到的一些變數稱之為閉包 def test_in(number_in): print("in test_in 函數, number_in is %d" % number_in) return number + number_in # 其實,這裡返回的就是閉包的結果 return test_in# 給 test1 函數賦值,這個20就是給參數 number# 此時, ret 指向 test_in, 也就是說, ret = test_in, number = 20ret = test1(20)# 注意,這裡的100,其實是給參數 number_inprint(ret(100)) # 輸出 120#注意這裡的200,其實是給參數 number_inprint(ret(200)) # 輸出 220
4. 裝飾器
# 樣本一:def foo(): print('foo')foo # 表示是函數foo() # 表示執行foo函數# 樣本二:def foo(): print('foo')foo = lambda x:x + 1foo() # 執行下面的lambda運算式,而不再是原來的foo函數,因為foo這個名字被重新指向了另外一個匿名函數# 樣本三:裝飾器之前代碼# 需求: 一個基礎平台部門,負責提供底層的功能,如資料庫操作,redis調用,監控API等功能# 其他業務部門使用基礎功能時,只需調用基礎平台提供的功能即可;========== 基礎平台提供的功能 ===========def f1(): print("f1")def f2(): print("f2")def f3(): print("f3")def f4(): print("f4")=========== 業務部門A 調用基礎功能 ==========f1()f2()f3()f4()========== 業務部門B 調用基礎功能 ==========f1()f2()f3()f4()# 樣本四:裝飾器# 需求: 執行基礎功能之前,需要先進行驗證# 寫代碼的開放封閉原則:# 封閉: 已實現的功能代碼塊# 開放: 對擴充開放def w1(func): def inner(): # 驗證1 # 驗證2 # 驗證3 func() return inner# @w1 等價於 f1 = w1(f1), 此時, f1 指向 (inner 指向的函數體)# 調用方,繼續為 f1(),不需要做修改@w1def f1(): print('f1')@w1def f2(): print('f2')@w1def f3(): print('f3')@w1def f4(): print('f4')# 樣本五: 裝飾器擴充def makeBold(fn): print("=== bold ===") def wrapped(): print("=== 1 ===") return "<b>" + fn() + "</b>" return wrappeddef makeItalic(fn): print("=== italic ===") def wrapped(): print("=== 2 ===") return "<i>" + fn() + "</i>" return wrapped@makeBold@makeItalicdef test1(): print("=== 3 ===") return "Hello world!"print(test1()) # 輸出:# (前面兩句,不調用test1()函數也會輸出)# === italic ===# === bold ===# === 1 ===# === 2 ===# === 3 ===# <b><i>Hello world!</i></b># 樣本六: 裝飾器什麼時間執行def w1(func): print("=== 正在裝飾 ===") def inner(): print("=== 正在驗證許可權 ===") func() return inner# 只要Python 解譯器執行到 @w1 這行代碼,那麼就會自動的進行裝飾,# 而不是等到調用f1()的時候才裝飾# 控制台會輸出: === 正在裝飾 ===@w1def f1(): print("=== f1 ===")# 樣本七: 被裝飾的函數帶有參數def w1(func): def inner(x, y): # 如果x,y沒有定義,那麼會導致f1(22, 33)調用失敗 # 驗證1 func(x, y) # 如果沒有把x, y當做實參進行傳遞,那麼,會導致調用f1(a, b)失敗 return inner@w1def f1(a, b): print('a = %d, b = %d' % (a, b))f1(22, 33)# 樣本八: 被裝飾的函數帶有不定長參數def w1(func): def inner(*args, **kwargs): # 驗證1 func(*args, **kwargs) return inner@w1def f2(a, b, c): print('a = %d, b = %d, c = %d' % (a, b, c))@w1def f3(a, b, c, d): print('a = %d, b = %d, c = %d, d = %d' % (a, b, c, d))f2(11, 22, 33)f3(11, 22, 33, 44)# 樣本九: 對帶有傳回值的函數裝飾def w1(func): def inner(): print("=== 執行前 ===") result = func() print("=== 執行後 ===") return result return inner@w1def f1(): print("=== 功能代碼 ===") return "zhangsan"name = f1()print("查詢到的姓名: %s" % name)# 樣本十: 通用裝飾器((不)帶參數,(不)帶傳回值)def w1(func): def inner(*args, **kwargs): print("=== 執行前 ===") result = func(*args, **kwargs) print("=== 執行後 ===") return result return inner@w1def f1(): print("=== 功能代碼 ===") return "haha"@w1def f1(a): print("=== 功能代碼 ===") print("輸入的字元為: %s" % a)# 樣本十一: 裝飾器帶參數,在原有裝飾器的基礎上,設定外部變數from time import ctime, sleepdef timefun_arg(pre="hello"): def timefun(func): def wrappedfunc(): print("%s called at %s %s" % (func.__name__, ctime(), pre)) return func() return wrappedfunc return timefun@timefun_arg("noodles")def foo(): print("I am Noodles")@timefun_arg("google")def too(): print("I am google")foo()sleep(2)too()# 輸出:foo called at Sat Mar 10 17:56:06 2018 noodlesI am Noodlestoo called at Sat Mar 10 17:56:08 2018 googleI am google# 備忘:# 1. 先執行timefun_arg("noodles")函數, 這個函數返回的結果是 timefun 函數的引用# 2. @timefun# 3. 使用 @timefun 對 foo 進行裝飾
4.1 類裝飾器
- 裝飾器函數其實是這樣一個介面約束,它必須接受一個callable對象作為參數,然後返回一個callable對象。
- 在Python中一般callable對象都是函數,但也有例外,只要某個對象重寫了
__call__()方法,那麼這個
對象就是callable的;
# 樣本一: callableclass Test(): def __call__(self): print("call me!")t = Test()t() # 輸出: call me!# 對象(),會調用 __call__ 方法# 樣本: 類裝飾器class Test(object): def __init__(self, func): print("=== 初始化 ===") print("func name is %s" % func.__name__) self.__func = func def __call__(self): print("=== 裝飾器中的功能 ===") self.__func()# 說明:# 1, 當有Test來裝作裝飾器對test函數進行裝飾的時候,首先會建立Test的執行個體對象,並且會把test這個函數# 名當做參數傳遞到 __init__ 方法中;## 2, test函數相當於指向了用Test建立出來的執行個體對象;## 3, 當在使用test()進行調用時,就相當於讓這個對象(),因此會調用這個對象的 __call__ 方法;## 4. 為了能夠在__call__ 方法中調用原來test指向的函數體,所以在__init__方法中就需要一個執行個體屬性來儲存# 所以才有了 self.__func = func 這句代碼,從而在調用 __call__ 方法中能夠調用到test之前的函數體@Testdef test(): print("=== test ===")test()
5. 元類
- 在大多數程式設計語言中,類就是一組用來描述如何產生一個對象的程式碼片段;
- 在Python中的類遠不止如此,類還是一種對象;
- 使用
type動態建立類,
- 格式:
type(類名, 有父類名稱組成的元組(針對繼承的情況,可以為空白), 包括屬性的字典(名稱和值))
type就是Python在背後用來建立所有類的元類;
# 樣本:# 傳統方式定義類class Test: passt1 = Test()# type 定義類Test2 = type("Test2", (), {})t2 = Test2()type(t1) # 輸出: __main__.Testtype(t2) # 輸出: __main__.Test2# 帶有屬性的類class Person: age = 15Person2 = type("Person2", (), {"age":15})# 帶有方法的類# 定義方法def printAge(self): print("=== 年齡是: %d ===" % self.num)Person3 = type("Person3", (), {"printAge": printAge})p = Person3()p.age = 22p.printAge()# 存在繼承關係的類class Animal: def eat(self): print("=== eat ===")Cat = type("Cat", (Animal,), {})tom = Cat()tom.eat()
5.1
__metaclass__ 屬性
# 樣本:class Foo(Bar): pass# Python 建立類的過程中,做了如下操作# 1, Foo 中有 __metaclass__ 這個屬性嗎?如果有,Python 會通過 __metaclass__ 建立一個名字為Foo的類對象;# 2, 如果Python沒有找到__metaclass__, 它會繼續在Bar(父類)中尋找 __metaclass__ 屬性,並嘗試做和前面同樣的操作;# 3, 如果Python在任何父類中到找不到__metaclass__, 它就會在模組層次中去尋找 __metaclass__, 並嘗試做同樣的操作;# 4. 如果還是找不到 __metaclass__, Python 就會用內建的type來建立這個類對象;
參考資料:
- Python 核心編程
- 深入理解Python中的元類(metaclass)
Python 物件導向進階(一)