[寒假學習筆記](二)Python初學

CoyoteWaltz發表於2019-01-20

Python 學習

python的自學從幾個月前斷斷續續地進行,想好好利用這個寒假,好好地學一學。

回顧

已學習:基本操作、函式
已有C++的一定基礎,只要注意python中比較特殊的部分就行

進入正題

lambda表示式

1. 語法

lambda _args: _expression   
            
lambda函式是匿名的:所謂匿名函式,通俗地說就是沒有名字的函式。lambda函式沒有名字。

lambda函式有輸入和輸出:輸入是傳入到引數列表_args的值,輸出是根據表示式_expression計算得到的值。

lambda函式一般功能簡單:單行_expression決定了lambda函式不可能完成複雜的邏輯,只能完成非常簡單的功能。由於其實現的功能一目瞭然,甚至不需要專門的名字來說明。

2. 一些小例子

lambda x: x ** 2
lambda x, y: x * y
lambda *args: sum(args)
lambda **kwargs: 1
  • 在變數名前加*表示可以傳入任意數量個引數
  • 在變數名前有**表示用“關鍵字=值”的方式傳遞一個字典給函式
    • def func(**args):
          d = {}
          for key, value in args.items():
              d[key] = value
          print(d)
      
      func(year=`2019`, month=`1`)
    • {`year`: `2019`, `month`: `1`}

3. 使用方法

1. 直接將lambda函式賦值給一個變數,讓這個變數具有函式的功能,類似於C++中的仿函式(functor) 
e.g. square = lambda x: x * x

2. 按照字典的值(value)進行排序,得到key的有序序列
e.g sorted(a_dict, key=lambda x:x[1]) 

從CSDN上看到的

例如,為了把標準庫time中的函式sleep的功能遮蔽(Mock),我們可以在程式初始化時呼叫:time.sleep=lambda x:None。這樣,在後續程式碼中呼叫time庫的sleep函式將不會執行原有的功能。例如,執行time.sleep(3)時,程式不會休眠3秒鐘,而是什麼都不做.

函式的返回值也可以是函式。例如return lambda x, y: x+y返回一個加法函式。這時,lambda函式實際上是定義在某個函式內部的函式,稱之為巢狀函式,或者內部函式。對應的,將包含巢狀函式的函式稱之為外部函式。內部函式能夠訪問外部函式的區域性變數,這個特性是閉包(Closure)程式設計的基礎,在這裡我們不展開。


將lambda函式作為引數傳遞給其他函式。

filter函式。此時lambda函式用於指定過濾列表元素的條件。例如filter(lambda x: x % 3 == 0, [1, 2, 3])指定將列表[1,2,3]中能夠被3整除的元素過濾出來,其結果是[3]。

sorted函式。此時lambda函式用於指定對列表中所有元素進行排序的準則。例如sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))將列表[1, 2, 3, 4, 5, 6, 7, 8, 9]按照元素與5距離從小到大進行排序,其結果是[5, 4, 6, 3, 7, 2, 8, 1, 9]。

map函式。此時lambda函式用於指定對列表中每一個元素的共同操作。例如map(lambda x: x+1, [1, 2,3])將列表[1, 2, 3]中的元素分別加1,其結果[2, 3, 4]。

reduce函式。此時lambda函式用於指定列表中兩兩相鄰元素的結合條件。例如reduce(lambda a, b: `{}, {}`.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])將列表 [1, 2, 3, 4, 5, 6, 7, 8, 9]中的元素從左往右兩兩以逗號分隔的字元的形式依次結合起來,其結果是`1, 2, 3, 4, 5, 6, 7, 8, 9`。

一些小東西

  • 列表的分片: s[:], s[:-1], s[n:m]。分片區間[n, m),當n或m為負值的時候,實際值為len(s)+n。
  • 列表的pop(): 不要和stack的pop搞起來了,列表pop是刪除最後一個元素!
  • 列表的remove(value): 可以直接將列表中的value這個值刪掉
  • .sort()與sorted(sth): s.sort()是永久排序,sorted排序一個物件,返回的有序的一個列表,不會對原始物件做改變
  • .reverse(): 永久逆序列表
  • 生成列表的小技巧: squares = [x ** 2 for x in range(11)],此時squares的內容是0~10的平方數(且有序)。這個很實用
  • 複製列表:copydeepcopy,涉及到python中變數儲存的方式,下文會談,這邊先提到一下。(自己思考思考)
  • 字典刪除鍵-值對: 用del關鍵字。del d[`key`]
  • 字典新增鍵-值對: 直接d[`new_key`] = new_value 即可
  • 遍歷字典: for key, value in d.items():

函式

只記錄一些比較新的點
  1. 函式的引數傳遞:
    1. 有預設值的情況和C++一樣(順序,預設值的位置)
    2. 可以用關鍵字傳遞形參,e.g. fun(name=`abc`),此時不關乎順序
    3. 形參表接受任意個引數,在形參名前加*
      e.g.
             def square_sum(*args):
                 res = sum([x ** 2 for x in args])
                 return res

    這裡的args其實是一個元組(tuple)

    1. 使用任意數量的關鍵字實參,上面提到過
      e.g.
             def add_info(user_info, **new_info):
                 for key, value in new_info.items():
                     user_info[key] = value

    這裡的new_info接受到的只一個字典

  2. 函式的命名法則
    這個看個人喜好吧,只要表達清楚,看得懂就行,我採用下劃線命名法

終於到了物件導向的東西了,看看和C++的差別有多少呢
  1. 編碼風格:
    1. python中的類名稱規定是首字母大寫的大駝峰法命名
    2. 例項名和模組名用下劃線命名法,類之間用兩個空行分開
    3. 在class Name() 的括號中,到底要不要加object呢?網上看了一下說python2最好加object,暫時我先不加了,遇到問題再說
  2. 成員函式:
    1. 建構函式 __init__(self):
    2. 所有成員函式的形參列表都要加上self,類似於c++類中的this指標,只不過python用的是顯式但不用真正傳遞,因此,每一個成員資料或者函式在類內使用的時候都要加上self.
  3. 關於public和private:
    好像在python的類中是沒有明確說明有這兩種以及protected情況的。經過我一番搜尋,發現三種屬性可以用下劃線來解決。
    • 沒有下劃線的變數,如self.public是public屬性
    • 有一個下劃線的,如self._protected是protected屬性(只有子類可以訪問,且不能通過import匯入)
    • 有兩個下劃線的,如self.__private是private屬性
  4. 繼承:
    1. 語法:在子類的括號中加上父類的名稱
    2. 特殊函式super():寫在子類的建構函式中
      e.g.
             class Child(Father):
                 def __init__(self, sth):
                 """初始化父類"""
                     super().__init__(sth)

    類似C++中在初始化行構造父類

    1. 覆蓋父類的函式/方法:只要子類的函式名和父類有的函式重複了就會override
    2. python的繼承順序,簡要提一下:python2是深度優先,python3是廣度優先,具體可以參考:Python類的繼承
  5. 其他:
    暫時沒有學到什麼東西了,碰到了再深入下去。

Python特殊的引用變數

  • 其實python中每個變數名所擁有的內容其實是一個引用(指標)指向的是一個靜態池中的常量
  • 所以當變數給變數賦值的時候,給的值並不是他所對應的常量,而是將自己的指標的地址給了另一個變數,導致了這兩個變數同時共享這一個常量
  • 如果改變一個變數中的值,即改變了這個常量的值,那麼另一個變數的值也隨之改變。
  • 因此在列表賦值的時候,不要直接用=,而是用a = s[:]的方式,因為s[:]是s的一份拷貝,新的列表,這就是上面所提到的一種copy,這種方法等效於a = s.copy()
    e.g.
>>> a = [1, 2, [3, 4]]
>>> b = a.copy()
>>> b[0] = 3
>>> b
    [3, 2, [3, 4]]
>>> a
    [1, 2, [3, 4]]
#a沒有受到影響,拷貝成功
  • 但是,注意b這裡是二維的,如果改變了第二位的列表中的值,a會受到影響嗎。
    e.g.
>>> b[2][0] = 123
>>> b
    [3, 2, [123, 4]]
>>> a
    [1, 2, [123, 4]]
#a受到影響了!
  • 可以想象到,在copy的過程中是將a列表中每個元素的值重新拷貝了一份新的引用給b,但是,中間巢狀的列表[2, 3, 4](看做一個元素)的值其實是一個引用(這裡要好好想一下哦),把一個一樣的值(也就是地址一樣的指標)給了b,那麼其實b這個位置的元素和a這個位置的元素共享的是一個地址,會受影響。
  • 為了解決這個問題,import copy,使用copy.deepcopy()
    e.g.
>>> b = copy.deepcopy(a)
#此時b和a不會相互影響,自己做一下實驗吧
  • 根據我自己的理解解釋一下(下次去查一下官方說明),deepcopy所做的事情其實是遞迴copy,層層深入copy
  • 還有一件比較重要的事情:根據上述所說,operator =賦值的都是引用,因此函式在形實結合之後,函式體內改變形參同樣會改變實參,如果不想這樣,怎麼做上面寫了幾個方法。有點像c++中預設傳遞T&(引用)型別。

python中的多檔案

  • 據我現在的理解,就是將函式,類寫在別的.py檔案,用的時候import即可。

相關文章