python核心程式設計

過不去的過去2發表於2017-08-13
元類
    1 類也是物件
        在大多數程式語言中,類就是物件的藍圖,在python中仍然成立,但在Python中類也是物件
class ObjectCreator(object):
…       pass
    

將在記憶體中建立一個物件,名字就是ObjectCreator。這個物件(類物件ObjectCreator)擁有建立物件(例項物件)的能力。但是,它的本質仍然是一個物件,於是乎你可以對它做如下的操作:

  1. 你可以將它賦值給一個變數
  2. 你可以拷貝它
  3. 你可以為它增加屬性
  4. 你可以將它作為函式引數進行

動態語言
    在執行時動態繫結屬性
生成器
    通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間。
    在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator
建立生成器:
    要建立一個生成器,有很多種方法。第一種方法很簡單,只要把一個列表生成式的 [ ] 改成 ( )
#普通列表
In [15]: L = [ x*2 for x in range(5)]

In [16]: L
Out[16]: [0, 2, 4, 6, 8]




生成器建立

In [17]: G = ( x*2 for x in range(5))

In [18]: G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>
#獲取數值
In [19]: next(G)
Out[19]: 0

In [20]: next(G)
Out[20]: 2

In [21]: next(G)
Out[21]: 4

In [22]: next(G)
Out[22]: 6

In [23]: next(G)
Out[23]: 8

In [24]: next(G)

StopIteration Traceback (most recent call last)
<ipython-input-24-380e167d6934> in <module>()
—-> 1 next(G)

StopIteration:

In [25]:
In [26]: G = ( x*2 for x in range(5))

In [27]: for x in G:
….: print(x)
….:
0
2
4
6
8

生成器的建立2

generator非常強大。如果推算的演算法比較複雜,用類似列表生成式的 for 迴圈無法實現的時候,還可以用函式來實現。

比如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, …


In [28]: def fib(times): 
....: n = 0
....: a,b = 0,1
....: while n<times:
....: print(b)
....: a,b = b,a+b
....: n+=1
....: return 'done'
....:

In [29]: fib(5)
1
1
2
3
5
Out[29]: 'done'

仔細觀察,可以看出,fib函式實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出後續任意的元素,這種邏輯其實非常類似generator。

也就是說,上面的函式和generator僅一步之遙。要把fib函式變成generator,只需要把print(b)改為yield b就可以了:


In [30]: def fib(times): 
….: n = 0
….: a,b = 0,1
….: while n<times:
….: yield b
….: a,b = b,a+b
….: n+=1
….: return ‘done’
….:

In [31]: F = fib(5)

In [32]: next(F)
Out[32]: 1

In [33]: next(F)
Out[33]: 1

In [34]: next(F)
Out[34]: 2

In [35]: next(F)
Out[35]: 3

In [36]: next(F)
Out[36]: 5

In [37]: next(F)

StopIteration Traceback (most recent call last)
<ipython-input-37-8c2b02b4361a> in <module>()
—-> 1 next(F)

StopIteration: done


In [38]: for n in fib(5): 
....: print(n)
....:
1
1
2
3
5


迭代器:
    迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的物件。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。

1. 可迭代物件

以直接作用於 for 迴圈的資料型別有以下幾種:

一類是集合資料型別,如 list 、 tuple 、 dict 、 set 、 str 等;

一類是 generator ,包括生成器和帶 yield 的generator function。

這些可以直接作用於 for 迴圈的物件統稱為可迭代物件: Iterable 。


閉包:

    

在函式內部再定義一個函式,並且這個函式用到了外邊函式的變數,那麼將這個函式以及用到的一些變數稱之為閉包

#定義一個函式 
def test(number):
#在函式內部再定義一個函式,並且這個函式用到了外邊函式的變數,那麼將這個函式以及用到的一些變數稱之為閉包
def test_in(number_in):
    print("in test_in 函式, number_in is %d"%number_in)
    return number+number_in
#其實這裡返回的就是閉包的結果
return test_in

給test函式賦值,這個20就是給引數number

ret = test(20)

注意這裡的100其實給引數number_in

print(ret(100))

注意這裡的200其實給引數number_in

print(ret(200))

內部函式對外部函式作用域非全域性變數的引用 則稱內部函式為閉包
    閉包的實踐
#建立直線方程y = kx+b的例子  
def line_conf(a,b):
def line(x):
nonlocal a
nonlocal b
return a*x+b
return line

line1 = line_conf(1,1)
line2 = line_conf(2,3)

print (line1(2))
print(line2(2))


裝飾器
    
#### 第一波 #### 
def foo():
print(‘foo’)

foo #表示是函式
foo() #表示執行foo函式

第二波

def foo():
print(‘foo’)

foo = lambda x: x + 1

foo() # 執行下面的lambda表示式,而不再是原來的foo函式,因為foo這個名字被重新指向了另外一個匿名函式


需求
初創公司有N個業務部門,1個基礎平臺部門,基礎平臺負責提供底層的功能,如:資料庫操作、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()

目前公司有條不紊的進行著,但是,以前基礎平臺的開發人員在寫程式碼時候沒有關注驗證相關的問題,即:基礎平臺的提供的功能可以被任何人使用。現在需要對基礎平臺的所有功能進行重構,為平臺提供的所有功能新增驗證機制,即:執行功能前,先進行驗證。

寫程式碼要遵循開放封閉原則,雖然在這個原則是用的物件導向開發,但是也適用於函數語言程式設計,簡單來說,它規定已經實現的功能程式碼不允許被修改,但可以被擴充套件,即:

  • 封閉:已實現的功能程式碼塊
  • 開放:對擴充套件開發
如果將開放封閉原則應用在上述需求中,那麼就不允許在函式 f1 、f2、f3、f4的內部進行修改程式碼,
def w1(func): 
def inner():
# 驗證1
# 驗證2
# 驗證3
func()
return inner

@w1
def f1():
print('f1')
@w1
def f2():
print('f2')
@w1
def f3():
print('f3')
@w1
def f4():
print('f4')


python直譯器就會從上到下解釋程式碼,步驟如下:

  1. def w1(func): ==>將w1函式載入到記憶體
  2. @w1
沒錯, 從表面上看直譯器僅僅會解釋這兩句程式碼,因為函式在 沒有被呼叫之前其內部程式碼不會被執行

從表面上看直譯器著實會執行這兩句,但是 @w1 這一句程式碼裡卻有大文章, @函式名 是python的一種語法糖

上例@w1內部會執行以下操作:

    執行w1函式 ,並將 @w1 下面的函式作為w1函式的引數,即@w1 等價於 w1(f1) 所以,內部就會去執行:

@w1 
def w1(func):
def inner():
# 驗證1
# 驗證2
# 驗證3
func()
return inner

語法糖

執行過程

def w1(f1):
# 驗證1
# 驗證2
# 驗證3
f1()
return inner


示例:
# -- coding: utf-8 -- 
“””
Created on Thu Aug 10 18:01:24 2017

@author: liaoxianfu
“”“

定義函式,實現包裹資料

def Bold(func):
def wrapped():
return “<b>”+func()+”</b>”
return wrapped

def Itailc(func):
def wrapped():
return “i”+func()+”</i>”
return wrapped

@Bold

def test1():
return “hello world 1”

@Itailc

def test2():
return “Heloord world 2”

@Bold
@Itailc

def test3():
return “Hello world 3”

print(test1())
print(test2())
print(test3())

執行結果:
runfile(‘C:/Users/liaoxianfu/demo1.py’, wdir=’C:/Users/liaoxianfu’) 
<b>hello world 1</b>
iHeloord world 2</i>

runfile(‘C:/Users/liaoxianfu/demo1.py’, wdir=’C:/Users/liaoxianfu’)
<b>hello world 1</b>
iHeloord world 2</i>
<b>iHello world 3</i></b>

裝飾器的功能
  1. 引入日誌
  2. 函式執行時間統計
  3. 執行函式前預備處理
  4. 執行函式後清理功能
  5. 許可權校驗等場景
  6. 快取

1 日誌
#無引數的函式

日誌

from time import ctime,sleep

def timefun(func):
def wrappedfunc():
print(“%s runned at %s”%(func.name,ctime()))
func()
return wrappedfunc

@timefun

def foo():
print (“runing “)

foo()
sleep(2)
foo()

過程分析 
@timefun
表示語法糖相當於將timefun(foo)傳入

foo = timefun(foo)

實際上在16行呼叫的foo相當於經過timefun(foo)處理後的函式
timefoo(foo)的返回值就是wrappedfunc()的內部函式
即:
foo()函式的呼叫等價於呼叫wrappedfunc()函式
內部函式被引用,所以timefun(func)中的func沒有被釋放,
即func儲存的示原來的foo函式

被裝飾的函式有引數
#被裝飾的函式有引數

日誌

from time import ctime,sleep

def timefun(func):
def wrappedfunc(a,b):
print(“%s runned at %s”%(func.name,ctime()))
print(a,b)
func(a,b)
return wrappedfunc

@timefun

def foo(a,b):
print (“runing “+str(a+b))

foo(3,5)
sleep(2)
foo(1,2)

執行結果
foo runned at Thu Aug 10 18:59:37 2017 
3 5
runing 8
foo runned at Thu Aug 10 18:59:39 2017
1 2
runing 3

被裝飾的函式有不定的引數

from time import ctime,sleep

def timefun(func):
def wrappedfunc(*args,**kwargs):
print(“%s runned at %s”%(func.name,ctime()))

    func(*args,**kwargs)
return wrappedfunc

@timefun

def foo(a,b):
print (“runing “+str(a+b))

@timefun
def foo1(a,b,c):
print (“runing “+str(a+b+c))

foo(3,5)
sleep(2)
foo1(1,2,3)

裝飾器的return以及外部引數
#被裝飾的函式有引數

日誌

from time import ctime,sleep

def timeargs(pre=”helo”):
def time(func):
def wapperd():
print(“func name is %s runing times is %s”%(func.name,ctime()))
#print(func())
return func()
return wapperd
return time

@timeargs(‘123’)

def foo():
return “Helo World”

print(foo())
sleep(2)
print(foo())



作用域

== && is
  • is 是比較兩個引用是否指向了同一個物件(引用比較)。
  • == 是比較兩個物件是否相等。
例子:
a = [11,22]

b = copy.deepcopy(a)

b is a
Out[24]: False

b == a
Out[25]: True


深拷貝、淺拷貝

1. 淺拷貝

  • 淺拷貝是對於一個物件的頂層拷貝

通俗的理解是:拷貝了引用,並沒有拷貝內容

2. 深拷貝

  • 深拷貝是對於一個物件所有層次的拷貝(遞迴)

3. 拷貝的其他方式

淺拷貝對不可變型別和可變型別的copy不同

















相關文章