Python 核心編程 (全)

淺拷貝和深拷貝python

1.淺拷貝:是對於一個對象的頂層拷貝,通俗的理解是:拷貝了引用,並無拷貝內容。至關於把變量裏面指向的一個地址給了另外一個變量就是淺拷貝,而沒有建立一個新的對象,如a=bc++

2.深拷貝:首先要import copy,而後c = copy.deepcopy(a),就表示把a的內容深拷貝到c中,若是發現了a中也存在引用的內容,則遞歸拷貝,也就是把當前的這個引用的對象繼續深拷貝編程

3. copy和deepcopy的區別
    ①copy:淺拷貝,裏面若是有可變類型,修改這個可變類型(如list),被拷貝的對象也會相應改變,僅僅拷第一層,若是是不可變類型,就一層都不拷,若是是可變類型就拷一層
    ②deepcopy:深拷貝,裏面無論是可變類型和不可變類型,被拷貝的對象都不會受到影響,遞歸拷貝緩存

4.copy和deepcopy拷貝元組的特色
    使用copy模塊的copy功能的時候,它會根據當前拷貝的數據類型是可變類型仍是不可變類型有不一樣的處理方式,如元組是不可變類型,拷貝多份沒有用,對copy來講,若是是可變類型就拷一層,若是是不可變類型,就一層都不拷閉包

屬性propertyapp

1.屬性property-1
    ①私有屬性添加getter和setter方法
    ②使用property升級getter和setter方法編程語言

num = property(getNum,setNum)

    注意:
        Num究竟是調用getNum()仍是setNum(),要根據實際的場景來判斷,值得注意的是必定要先填getNum後setNum
        若是是給t.num賦值,那麼必定調用setNum()
        若是是獲取t.num的值,那麼就必定調用getNum()
        property的做用:至關於把方法進行了封裝,開發者在對屬性設置數據的時候更方便 函數式編程

2.屬性property-2函數

class Money(object):
    @property #修飾器
    def num(self): 
        print("------getter-----")
        return self.__num

    @num.setter #修飾器
    def num(self,new_num):
        print("------setter------")
        self.__num = new_num
        t.num = 20
        print(t.num)

迭代器工具

1.迭代器
    迭代是訪問集合元素的一種方式。迭代器是一個能夠記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問,直到全部的元素被訪問完結束,迭代器只能往前不會後退

2.可迭代對象(for 循環遍歷的數據類型)
    ①一類是集合數據類型,如 list 、 tuple 、 dict 、 set 、 str 等
    ②一類是 generator(列表生成式,生成器) ,包括生成器和帶 yield 的generator function
    ③這些能夠直接做用於for循環的對象統稱爲可迭代對象:Iterable 

from collections import Iterable
# 若是能夠迭代就返回True
isinstance([ ], Iterable)

3.判斷是否能夠迭代
    可使用isinstance()判斷一個對象是不是Iterable對象

from collections import Iterable
# 若是能夠迭代就返回True
isinstance([ ], Iterable)

    而生成器不但能夠做用於for循環,還能夠被next()函數不斷調用並返回下一個值,直到最後拋出StopIteration錯誤表示沒法繼續返回下一個值了

4.迭代器
    ①能夠被next()函數調用並不斷返回下一個值的對象稱爲迭代器:Iterator
    ②可使用isinstance()判斷一個對象是不是Iterator對象
    ③生成器(i for i in range(10))必定是迭代器,但迭代器不必定是生成器
    ④迭代器必定是可迭代對象,但可迭代對象不必定是迭代器      

from collections import Iterator
isinstance((x for x in range(10)), Iterator)  # 若是是的話就返回True

5.iter( )函數
    ①生成器都是Iterator(迭代器)對象,但 list、dict、str雖然是Iterable(可迭代),卻不是Iterator(迭代器)
    ②把list、dict、str 等 Iterable(可迭代)變成 Iterator(迭代器)可使用iter()函數,就比如人能夠游泳,但不是天生就會,可迭代對象就比如人,迭代器就比如會游泳的人,須要通過iter()訓練同樣

isinstance(iter([ ]), Iterator)
True

閉包

1.函數的引用     

test1() # 調用函數
ret = test1 # 引用函數
ret() # 經過引用調用函數

2.什麼是閉包
    在函數內部再定義一個函數,而且這個函數用到了外邊函數的變量,那麼將這個函數以及用到的一些變量稱之爲閉包      

def test(number):
    print("-----1-----")
    def test_in(number2):
        print("----2-----")
        print(number+number2)
    
    print("------3------")
    # 把函數的引用返回了
    return test_in

# 用來接收test(100),指向了一個函數體,這個100傳給了number
ret = test(100)
# 這個1傳給了number2
ret(1) # 這個返回101
ret(100) # 這個返回200
ret(200) # 這個返回300

3.閉包再理解
    內部函數對外部函數做用域裏變量的引用(非全局變量),則稱內部函數爲閉包
    閉包的實際例子:

def line_conf(a, b):

    def line(x):
        return a*x + b
    return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)

print(line1(5))
print(line2(5))

    這個例子中,函數line與變量a,b構成閉包。在建立閉包的時候,咱們經過line_conf的參數a,b說明了這兩個變量的取值,這樣,咱們就肯定了函數的最終形式(y = x + 1和y = 4x + 5)。咱們只須要變換參數a,b,就能夠得到不一樣的直線表達函數。由此,咱們能夠看到,閉包也具備提升代碼可複用性的做用

裝飾器

1.裝飾器
    在有兩個重名的函數中,Python解釋器會調用最後定義的那重名函數,由於在Python裏,第一個函數指向的是一片內存,而後又讓這個函數指向另外一片內存,就會利用第二片內存來執行,全部函數名應儘可能避免相同
    寫代碼要遵循開放封閉原則,雖然在這個原則是用的面向對象開發,可是也適用於函數式編程,簡單來講,它規定已經實現的功能代碼不容許被修改,但能夠被擴展,即:
    ①封閉:已實現的功能代碼塊
    ②開放:對擴展開發
    ③實例:

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

@w1 # 裝飾器
def f1():
    print('f1')

@w1  # 裝飾器
def f2():
    print('f2')

........

    對於上述代碼,也是僅僅對基礎平臺的代碼進行修改,就能夠實如今其餘人調用函數 f一、 f2 、f3 、f4 以前都進行【驗證】操做,而且其餘業務部門無需作任何操做

2.裝飾器的功能
    ①引入日誌
    ②函數執行時間統計
    ③執行函數前預備處理    
    ④執行函數後清理功能
    ⑤權限校驗等場景
    ⑤緩存
    ⑥若是是有多個裝飾器的狀況,通常是先裝飾最下面的一個,而後依次往上,@w1類比於f1 = w1(f1)

3.裝飾有參數的函數:在傳遞參數的時候,須要在閉包裏面定義一個形參,閉包裏面的調用的函數也要定義一個形參,不然會致使兩部分函數調用失敗

4.裝飾不定長的參數的函數:在傳遞參數的時候,須要在閉包裏面定義一個*args和**kwargs,閉包裏面的調用的函數也要定義一個*args和**kwargs,這樣就能夠在調用的時候傳遞任意長度的參數,增長代碼的可複用性

5.裝飾帶返回值的函數:須要在閉包裏面進行一個接收,也就是ret = test(),而後再把接收到的ret return出去,這樣在裝飾的test才能返回出當前須要返回的東西,不然只會返回None

6.通用的裝飾

def w1(func):
    print("-----正在裝飾-----")
    def inner(*args,**kwargs):
        print("---正在驗證權限---")
        print("----記錄日誌----")
        ret = func(*args,**kwargs) 
        return ret 
    return inner

    帶有參數的裝飾器:也就是在原來包含一個閉包的函數外面再給他套一個函數,用來傳遞裝飾器的參數

def func_arg(arg):
    def w1(func):
        print("---記錄日誌---")
        def inner(*args,**kwargs):
            func(*args,**kwargs) 
        return inner
    return w1

@func_arg("heihei") 
def f1():
    print("----f1----")               

# 1.先執行func_arg("heihei")函數,這個函數return的結果是
# 2.@w1
# 3.使用@w1對f1進行裝飾
# 做用:帶有參數的裝飾器,可以起到在運行時,有不一樣的功能

python是動態語言

1.Python是動態語言
    動態編程語言是高級程序設計語言的一個類別,在計算機科學領域已被普遍應用。它是一類在運行時能夠改變其結構的語言:例如新的函數、對象、甚至代碼能夠被引進,已有的函數能夠被刪除或是其餘結構上的變化。這種動態語言的應用就比如是在沒有更新app的狀況下,它的界面在後臺也能夠被開發者更改,由於它是動態的,能夠把新增的動態程序放置在文本,只要加載一遍便可

2.運行的過程當中給對象綁定(添加)屬性:也就是說給對象綁定一個實例屬性(這個屬性是初始化以外的額外屬性),只有這個建立對象的屬性如laozhao.addr = "北京"

3.運行的過程當中給類綁定(添加)屬性:若是須要全部的一個類的實例加上一個屬性怎麼辦呢? 答案就是直接給這個類綁定屬性,如Person.sex = "male"

4.運行的過程當中給類綁定(添加)方法: 若是是對這個類綁定一個實例方法,那麼就要先import types,而後如對象.方法名 = types.MethodType(函數名, 對象),把run這個方法綁定到P對象上。若是是靜態方法和類方法,就直接用類名.方法名=函數名

5.運行的過程當中刪除屬性、方法:
    ①del 對象.屬性名 
    ②delattr(對象, "屬性名")

     __slots__的做用

1.動態語言:能夠在運行的過程當中,修改代碼

2.靜態語言:編譯時已經肯定好代碼,運行過程當中不能修改

3.爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的__slots__變量,來限制該class實例能添加的屬性,如__slots__ = ("name","age"),就能夠達到限制name和age的屬性,若是發現有添加其餘屬性的程序就會發生異常

4.使用__slots__要注意,__slots__定義的屬性僅對當前類實例起做用,對繼承的子類是不起做用的

生成器

1.什麼是生成器
    經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了 
    ①while的列表推導            

list.append(i)

    ②for的列表推導,range與切片很相似

for i in range(10,78):

    ③第一個i是元素的值,後面的for是循環的次數,若是第一個i=11,那麼全部的元素都是11  

a=[i for i in range(1,18)]

    ④for控制循環的次數,for和if的嵌套       

c = [i for i in range(10) if i%2==0]

    ⑤每執行第一個for循環都要執行第二個for循環的全部次數   

d = [i for i in range(3) for j in range(2)]

    ⑥每執行第一個for循環都要執行第二個for循環的全部次數

d = [(i,j) for i in range(3) for j in range(2)]

     例題:找出100之內能被3整除的正整數

aiquot = []               
for n in range(1,100)
    if n%3 ==0:
        aiquot.append(n)

range(3,100,3) # 很簡潔

2.建立生成器方法1
    要建立一個生成器,有不少種方法。第一種方法很簡單,只要把一個列表生成式的[ ]改爲( )
    如L = [ x*2 for x in range(5)]和G = ( x*2 for x in range(5)),L是一個列表,而G是一個生成器,能夠經過next(G)函數得到生成器的下一個返回值,不斷調用 next()實在是太變態了,正確的方法是使用for循環,由於生成器也是可迭代對象

3.建立生成器方法2
    fib函數變成generator,只須要把print(b)改成yield b就能夠了,循環過程當中不斷調用yield ,就會不斷中斷。固然要給循環設置一個條件來退出循環,否則就會產生一個無限數列出來,當循環到沒有元素的時候,將會生成異常,這時候就要用try和exception來檢測異常,#print自動檢測異常並中止,可是next()就要用try ,在建立生成器的時候須要接收函數的返回值
    #1.next(返回函數名)

    #2.返回函數名.__next__()是同樣的方法來獲取下一個返回值
    總結:生成器是這樣一個函數,它記住上一次返回時在函數體中的位置。對生成器函數的第二次或第 n 次調用跳轉至該函數中間,而上次調用的全部局部變量都保持不變,生成器不只“記住”了它數據狀態;生成器還“記住”了它在流控制構造(在命令式編程中,這種構造不僅是數據值)中的位置

4.生成器的特色
    ①節約內存
    ②迭代到下一次的調用時,所使用的參數都是第一次所保留下的,便是說,在整個全部函數調用的參數都是第一次所調用時保留的,而不是新建立的

5.send用法
    ①若是在在程序中有個變量等於yield,不是說把yield的值給了這個變量,而是接下來在下一次調用執行一次的時候能夠傳一個值,t.send("haha")和t.__next__()均可以讓生成器繼續執行,不一樣的是send能夠傳遞一個值,可是不能在程序剛開始執行就用send傳值,有兩種方法,要麼先用__next__調用一次,再send一個值,或者t.send(None)
    ②生成器:完成多任務,控制多個任務執行的狀況

元類

1.元類
    ①類也是對象
    ②在大多數編程語言中,類就是一組用來描述如何生成一個對象的代碼段,類一樣也是一種對象

2.動態的建立類
    由於類也是對象,你能夠在運行時動態的建立它們,就像其餘任何對象同樣

def choose_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo     # 返回的是類,不是類的實例
    else:
        class Bar(object):
            pass
        return Bar

MyClass = choose_class('foo') # 當你使用class關鍵字時,Python解釋器自動建立這個對象

2.使用type建立類
    ①type還有一種徹底不一樣的功能,動態的建立類,type能夠像這樣工做:
    ②type(類名, 由父類名稱組成的元組(針對繼承的狀況,能夠爲空),包含屬性的字典(名稱和值)) 

Test2 = type("Test2",(),{}) #定了一個Test2類

3.使用type建立帶有屬性的類

Foo = type('Foo', (), {'bar':True})

4.使用type建立帶有方法的類

FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) # 這是添加實例方法echo_bar    
Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic": testStatic}) # 添加靜態方法
Foochild = type('Foochild',(Foo,),{"echo_bar":echo_bar, "testStatic":testStatic, "testClass":testClass}) # 添加類方法

5.到底什麼是元類
    元類就是用來建立類的東西,元類就是用來建立這些類 (對象) 的,元類就是類的類,元類又由元類建立,Python中全部的東西,注意,我是指全部的東西——都是對象。這包括整數、字符串、函數以及類

6.__metaclass__屬性

class Foo(object):
   __metaclass__ = something…

    若是這麼作了,Python就會用元類來建立類Foo,這裏面有些技巧。首先寫下class Foo(object),可是類Foo尚未在內存中建立。Python會在類的定義中尋找__metaclass__屬性,若是找到了,Python就會用它來建立類Foo,若是沒有找到,就會用內建的type來建立這個類

GC垃圾回收

1.GC垃圾回收
    ①小整數對象池:Python爲了優化速度,使用了小整數對象池, 避免爲整數頻繁申請和銷燬內存空間。Python 對小整數的定義是[-5, 257) 這些整數對象是提早創建好的,不會被垃圾回收
    ②大整數對象池:每個大整數,均建立一個新的對象
    ③intern機制:假如要建立n個對象的是同樣的字符串,那麼python只會建立一個內存空間來存儲,其餘對象都是引用,但若是字符串中出現空格或其餘符號就表示爲不一樣的對象

2.GC(Garbage collection)垃圾回收
    Python裏也同Java同樣採用了垃圾收集機制,不過不同的是: Python採用的是引用計數機制爲主,標記-清除和分代收集兩種機制爲輔的策略

3.引用計數機制的優勢
    ①簡單
    ②實時性:一旦沒有引用,內存就直接釋放了。不用像其餘機制等到特定時機。實時性還帶來一個好處:處理回收內存的時間分攤到了平時

4. 引用計數機制的缺點
    ①維護引用計數消耗資源
    ②循環引用

5.GC系統所承擔的工做遠比"垃圾回收"多得多。實際上,它們負責三個重要任務:
    ①爲新生成的對象分配內存
    ②識別那些垃圾對象
    ③從垃圾對象那回收內存

6.垃圾回收機制:Python中的垃圾回收是以引用計數爲主,分代收集爲輔
    ①致使引用計數+1的狀況:
        對象被建立,例如a=23
        對象被引用,例如b=a
        對象被做爲參數,傳入到一個函數中,例如func(a)
        對象做爲一個元素,存儲在容器中,例如list1=[a,a]
    ②致使引用計數-1的狀況:
        對象的別名被顯式銷燬,例如del a
        對象的別名被賦予新的對象,例如a=24
        一個對象離開它的做用域,例如f函數執行完畢時,func函數中的局部變量(全局變量不會)
        對象所在的容器被銷燬,或從容器中刪除對象

7.查看一個對象的引用計數

import sys
a = "hello world"
sys.getrefcount(a)

    ①能夠查看a對象的引用計數,可是比正常計數大1,由於調用函數的時候傳入a,這會讓a的引用計數+1
    ②有三種狀況會觸發垃圾回收:
        調用gc.collect()
        當gc模塊的計數器達到閥值的時候
        程序退出的時候

8.gc模塊的自動垃圾回收機制:

    ①必需要import gc模塊,而且is_enable()=True纔會啓動自動垃圾回收。
    ②這個機制的主要做用就是發現並處理不可達的垃圾對象。
    ③垃圾回收 = 垃圾檢查 + 垃圾回收
    ④在Python中,採用分代收集的方法。把對象分爲三代,一開始,對象在建立的時候,放在一代中,若是在一次一代的垃圾檢查中,該對象存活下來,就會被放到二代中,同理在一次二代的垃圾檢查中,該對象存活下來,就會被放到三代中
    ⑤gc模塊裏面會有一個長度爲3的列表計數器,能夠經過gc.get_count()獲取,gc.set_threshold(threshold0[, threshold1[, threshold2]) 設置自動執行垃圾回收的頻率,例如(700,10,10)每一次計數器的增長,gc模塊就會檢查增長後的計數是否達到閥值的數目,700表示閾值,10表示沒清理10次零代就清理一次二代,第二個10表示每清理10次一代鏈表就清理二代一次
    注意點:
        gc模塊惟一處理不了的是循環引用的類都有__del__方法,因此項目中要避免定義__del__方法

內建屬性

    經常使用專有屬性             說明                                    觸發方式
    __init__                  構造初始化函數                  建立實例後,賦值時使用,在__new__後
    __new__                生成實例所需屬性               建立實例時
    __class__              實例所在的類                       實例.__class__
    __str__                  實例字符串表示,可讀性     print(類實例),如沒實現,使用repr結果
    __repr__               實例字符串表示,準確性      類實例 回車 或者 print(repr(類實例))
    __del__                 析構                                     del刪除實例
    __dict__                實例自定義屬性                    vars(實例.__dict__)
    __doc__                類文檔,子類不繼承            help(類或實例)
    __getattribute__    屬性訪問攔截器                    訪問實例屬性時
    __bases__             類的全部父類構成元素         類名.__bases__

def __getattribute__(self,obj):
    if obj == 'subject1':
        print('log subject1')
        return 'redirect python'
    else:  # 測試時註釋掉這2行,將找不到subject2
        return object.__getattribute__(self,obj)

    __getattribute__的做用能夠用來打印Log日誌
    __getattribute__的坑:

class Person(object):
    def __getattribute__(self,obj):
        print("---test---")
        if obj.startswith("a"):
            return "hahha"
        else:
            return self.test    
    def test(self):
        print("heihei")

t.Person()
t.a #返回hahha
t.b #會讓程序死掉
# 緣由是:當t.b執行時,會調用Person類中定義的__getattribute__方法,可是在這個方法的執行過程當中if條件不知足,因此 程序執行else裏面的代碼,即return self.test  問題就在這,由於return 須要把self.test的值返回,那麼首先要獲self.test的值,由於self此時就是t這個對象,因此self.test就是t.test 此時要獲取t這個對象的test屬性,那麼就會跳轉到__getattribute__方法去執行,即此時產生了遞歸調用,因爲這個遞歸過程當中 沒有判斷何時推出,因此這個程序會永無休止的運行下去,又由於每次調用函數,就須要保存一些數據,那麼隨着調用的次數愈來愈多,最終內存吃光,因此程序崩潰
# 注意:之後不要在__getattribute__方法中調用self.xxxx

調試

1.調試:pdb是基於命令行的調試工具,很是相似gnu的gdb(調試c/c++)
    執行時調試
    程序啓動,中止在第一行等待單步調試  

python -m pdb xxx.py

2.調試方法     ①n(next)執行下一步       ②l(list)顯示當前執行進度      ③c(continue)繼續執行代碼      ④b(break)添加斷點      ⑤q(quit)停止並退出      ⑥clear num刪除指定斷點      ⑦p(print)打印變量的值      ⑧a(args)打印全部的形參數據      ⑨s(step)進入到一個函數      r執行代碼直到從當前函數返回

相關文章
相關標籤/搜索