主頁 > 後端開發 > 真的不建議學Python,煞筆才學習Python,學Python難?兩個小時足夠搞定

真的不建議學Python,煞筆才學習Python,學Python難?兩個小時足夠搞定

2021-02-10 13:09:27 後端開發

對于Python這么語言,可以當作一門興趣或愛好來學習,但是若是想找到份好的作業還是謹慎為主,這也是為什么不建議你搞Python的原因,

如果說,有些讀者的學歷非常牛逼,然后學習能力也非常強,那么選擇人工智能、機器學習、資料分析,我覺得前途是光明的,既能賺錢,待遇又好,還不可替代,不學 Python 絕對虧,

如果說,有些讀者學歷一般,做程式員僅僅是為了糊口飯吃,那么我覺得可以把 Python 作為第二語言來學,不要當做主語言,搞點范圍許可內的爬蟲,自動化測驗,我就覺得挺好的,況且 Python 這門語言本身是非常優秀的,不然怎么搞人工智能,海量資料分析,對吧?

文章目錄

      • 1、輸入輸出
      • 2、縮進和注釋
      • 3、資料型別和變數
        • 整數
        • 浮點數
      • 4、字串
      • 5、布林值
      • 6、空值
      • 7、變數
      • 8、常量
      • 9、除法計算結果是浮點數
      • 10、Python中的is和==
      • 11、編碼
        • \u則代表unicode編碼,是一個字符
      • 12、Python3注釋
      • 13、運算子
        • 邏輯運算子
        • 位運算子
        • 賦值運算子
        • 算術運算子
        • 比較運算子
        • 身份運算子
        • 成員運算子
      • 14、數字型別轉換
      • 15、list-串列
    • 串列腳本運算子
    • 串列截取與拼接
    • 串列函式&方法
      • 16、元組-tuple
      • 17、if else
      • 再議 input
      • 18、回圈
      • 19、break、continue和pass
      • 20、Dictionary--字典
      • 21、函式
      • python 傳不可變物件實體
      • 傳可變物件實體
      • 命名關鍵字引數
      • 關鍵字引數
      • 命名關鍵字引數
      • 默認引數
        • 可變引數
        • **`*nums`表示把`nums`這個list的所有元素作為可變引數傳進去,這種寫法相當有用,而且很常見,**
      • 匿名函式
      • 語法
      • 實體(Python 2.0+)
      • 回傳多個值
      • 全域變數和區域變數
      • 實體(Python 2.0+)
        • 函式別名
        • Import 與 from import
        • 包的概念 packa包中
      • 22、切片
      • 23、迭代
      • 24、串列生成式
      • 25、生成器
      • 26、迭代器
      • 27、函式式編程
        • 1、高階函式
          • 變數可以指向函式
          • 函式名也是變數
        • 2、map/reduce
        • sorted
        • 3、回傳函式
        • 閉包
        • 4、lambda
        • 5、裝飾器
        • 6、偏函式
      • 28、模塊
          • 私有變數和函式
          • 第三方模塊
      • 模塊搜索路徑
      • 29、類和實體
      • 30、訪問限制
      • 31、繼承和多型
      • 32、獲取物件資訊
        • 使用isinstance()
        • dir()
      • 33、__slots__限制實體系結任何屬性和方法
      • 34、@property @xxx.setter
      • 35、多繼承
      • 36、定制類
      • __str__

1、輸入輸出

  • 輸出

    • print()在括號中加上字串,就可以向螢屏上輸出指定的文字

    • print()函式也可以接受多個字串,用逗號“,”隔開,就可以連成一串輸出;

      • print('The quick brown fox', 'jumps over', 'the lazy dog')
        The quick brown fox jumps over the lazy dog
        

        print()會依次列印每個字串,遇到逗號“,”會輸出一個空格

    • print()也可以列印整數,或者計算結果

      • print('100 + 200 =', 100 + 200)
        
  • 輸入

    • Python提供了一個input(),可以讓用戶輸入字串,并存放到一個變數里

      • name = input()
        

2、縮進和注釋

  • #開頭的陳述句是注釋
  • 每一行都是一個陳述句,當陳述句以冒號:結尾時,縮進的陳述句視為代碼塊;
  • 縮進有利有弊,好處是強迫你寫出格式化的代碼,但沒有規定縮進是幾個空格還是Tab,按照約定俗成的慣例,應該始終堅持使用4個空格的縮進,縮進的另一個好處是強迫你寫出縮進較少的代碼,你會傾向于把一段很長的代碼拆分成若干函式,從而得到縮進較少的代碼,縮進的壞處就是“復制-粘貼”功能失效了,這是最坑爹的地方,當你重構代碼時,粘貼過去的代碼必須重新檢查縮進是否正確,此外,IDE很難像格式化Java代碼那樣格式化Python代碼,
  • Python程式是大小寫敏感的,如果寫錯了大小寫,程式會報錯

3、資料型別和變數

  • 整數

    • Python可以處理任意大小的整數,當然包括負整數,在程式中的表示方法和數學上的寫法一模一樣

    • dec = int(input("輸入數字:"))
      print("十進制數為:", dec)
      print("轉換為二進制為:", bin(dec))
      print("轉換為八進制為:", oct(dec))  # 0o開頭
      print("轉換為十六進制為", hex(dec))  # 0x開
      
  • 浮點數

    • https://www.cnblogs.com/qinchao0317/p/10699717.html

    • 保留小數點位數

      • 使用字串格式化

      • img

        • x = 1234.56789
          print(format(x, '0.2f'))
          1234.57
          
          占位符替換內容
          %d整數
          %f浮點數
          %s字串
          %x十六進制整數
      • 使用round內置函式

        • dec = float(input("輸入的數字"))
          print("dec*dec =",round(dec*dec,2));
          
    • 數值型別轉換

      • int(x [,base ])         將x轉換為一個整數  
        long(x [,base ])        將x轉換為一個長整數  
        float(x )               將x轉換到一個浮點數  
        complex(real [,imag ])  創建一個復數  
        str(x )                 將物件 x 轉換為字串  
        repr(x )                將物件 x 轉換為運算式字串  
        eval(str )              用來計算在字串中的有效Python運算式,并回傳一個物件  
        tuple(s )               將序列 s 轉換為一個元組  
        list(s )                將序列 s 轉換為一個串列  
        chr(x )                 將一個整數轉換為一個字符  
        unichr(x )              將一個整數轉換為Unicode字符  
        ord(x )                 將一個字符轉換為它的整數值  
        hex(x )                 將一個整數轉換為一個十六進制字串  
        oct(x )                 將一個整數轉換為一個八進制字串  
        

4、字串

  • 字串是以單引號'或雙引號"括起來的任意文本;如果'本身也是一個字符,那就可以用""括起來,比如"I'm OK"包含的字符是I'm,空格,OK這6個字符,如果字串內部既包含'又包含"怎么辦?可以用轉義字符\來標識;

    • print("i'm okss")
      print('i\'m \" ok')
      

    轉義字符\可以轉義很多字符,比如\n表示換行,\t表示制表符,字符\本身也要轉義,所以\\表示的字符就是\

    print('i\nam\nok')
    
    

    Python還允許用r''表示''內部的字串默認不轉義

    print(r'xxjis\nosk\t')
    
    

    如果字串內部有很多換行,用\n寫在一行里不好閱讀,為了簡化,Python允許用'''...'''的格式表示多行內容

    print('''zzxsx
    zxsaxa
    sasads
    s''')
    
    

5、布林值

  • 個布林值只有TrueFalse兩種值,要么是True,要么是False

  • and、 or、 not

  • print(True and True)
    print(not 1>2)
    
    

6、空值

  • None表示空值,它是一個特殊 Python 物件, None的型別是NoneType

    • print(type(None))  #<class 'NoneType'>
      
      

7、變數

  • 變數在程式中就是用一個變數名表示了,變數名必須是大小寫英文、數字和_的組合,且不能用數字開頭

  • 變數本身型別不固定的語言稱之為動態語言,與之對應的是靜態語言,靜態語言在定義變數時必須指定變數型別,如果賦值的時候型別不匹配,就會報錯,例如Java是靜態語言

    • int a = 123; // a是整數型別變數
      a = "ABC"; // 錯誤:不能把字串賦給整型變數
      
      

8、常量

  • 所謂常量就是不能變的變數,比如常用的數學常數π就是一個常量,在Python中,通常用全部大寫的變數名表示常量:

    • PI = 3.14159265359
      
      

      但事實上PI仍然是一個變數,Python根本沒有任何機制保證PI不會被改變,所以,用全部大寫的變數名表示常量只是一個習慣上的用法,如果你一定要改變變數PI的值,也沒人能攔住你

  • 賦值

    • a,b,c=1,2,3
      a,b=b,a+b
      print(a,b)
      
      

9、除法計算結果是浮點數

  • print(10/3)
    print(10//3)
    
    

    /除法計算結果是浮點數,即使是兩個整數恰好整除,結果也是浮點數

    還有一種除法是//,稱為地板除,兩個整數的除法仍然是整數

    %為取余計算

10、Python中的is和==

  • ==是python標準運算子中的比較運算子,用來比較判斷兩個物件的value(值)是否相等

  • is是比較參考地址是否相等

  • Python中,萬物皆物件!

    每個物件包含3個屬性,id,type,value

    id就是物件地址,可以通過內置函式id()查看物件參考的地址,

    type就是物件型別,可以通過內置函式type()查看物件的型別,

    value就是物件的值,

11、編碼

  • Python 3版本中,字串是以Unicode編碼的

  • 對于單個字符的編碼,Python提供了ord()函式獲取字符的整數表示,chr()函式把編碼轉換為對應的字符:

    • print(ord('a'))
      print(chr(97))
      
      
    • \u則代表unicode編碼,是一個字符

      • print('\u4e2d\u6587')  #表示中文的意思
        
        
  • Python對bytes型別的資料用帶b前綴的單引號或雙引號表示

    • print('ABC'.encode('ascii'))  //b'ABC'
      
      

      區分'ABC'b'ABC',前者是str,后者雖然內容顯示得和前者一樣,但bytes的每個字符都只占用一個位元組,

  • >>> b'ABC'.decode('ascii')
    'ABC'
    >>> '中文'.encode('utf-8')
    b'\xe4\xb8\xad\xe6\x96\x87'
    >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
    '中文'
    
    

    如果我們從網路或磁盤上讀取了位元組流,那么讀到的資料就是bytes,要把bytes變為str,就需要用decode()方法:

    >>> b'ABC'.decode('ascii')
    'ABC'
    >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
    '中文'
    
    

    如果bytes中只有一小部分無效的位元組,可以傳入errors='ignore'忽略錯誤的位元組:

    >>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
    '中'
    
    

    要計算str包含多少個字符,可以用len()函式:

    >>> len('ABC')
    3
    >>> len('中文')
    2
    
    
  • 由于Python源代碼也是一個文本檔案,所以,當你的源代碼中包含中文的時候,在保存源代碼時,就需要務必指定保存為UTF-8編碼,當Python解釋器讀取源代碼時,為了讓它按UTF-8編碼讀取,我們通常在檔案開頭寫上這兩行:

    • #!/usr/bin/env python3
      # -*- coding: utf-8 -*-
      或者
      #!/usr/bin/env python3
      #coding=utf-8
      
      

12、Python3注釋

  • # 這是一個注釋
  • 多行注釋用三個單引號 ‘’’ 或者三個雙引號 “”" 將注釋括起來

13、運算子

  • 邏輯運算子

    • 運算子邏輯運算式描述實體
      andx and y布爾"與" - 如果 x 為 False,x and y 回傳 False,否則它回傳 y 的計算值,(a and b) 回傳 20,
      orx or y布爾"或" - 如果 x 是 True,它回傳 x 的值,否則它回傳 y 的計算值,(a or b) 回傳 10,
      notnot x布爾"非" - 如果 x 為 True,回傳 False ,如果 x 為 False,它回傳 True,not(a and b) 回傳 False
  • 位運算子

    • &按位與運算子:參與運算的兩個值,如果兩個相應位都為1,則該位的結果為1,否則為0(a & b) 輸出結果 12 ,二進制解釋: 0000 1100
      |按位或運算子:只要對應的二個二進位有一個為1時,結果位就為1,(a | b) 輸出結果 61 ,二進制解釋: 0011 1101
      ^按位異或運算子:當兩對應的二進位相異時,結果為1(a ^ b) 輸出結果 49 ,二進制解釋: 0011 0001
      ~按位取反運算子:對資料的每個二進制位取反,即把1變為0,把0變為1,~x 類似于 -x-1(~a ) 輸出結果 -61 ,二進制解釋: 1100 0011, 在一個有符號二進制數的補碼形式,
      <<左移動運算子:運算元的各二進位全部左移若干位,由"<<"右邊的數指定移動的位數,高位丟棄,低位補0,a << 2 輸出結果 240 ,二進制解釋: 1111 0000
      >>右移動運算子:把">>“左邊的運算元的各二進位全部右移若干位,”>>"右邊的數指定移動的位數a >> 2 輸出結果 15 ,二進制解釋: 0000 1111
  • 賦值運算子

    • 運算子描述實體
      =簡單的賦值運算子c = a + b 將 a + b 的運算結果賦值為 c
      +=加法賦值運算子c += a 等效于 c = c + a
      -=減法賦值運算子c -= a 等效于 c = c - a
      *=乘法賦值運算子c *= a 等效于 c = c * a
      /=除法賦值運算子c /= a 等效于 c = c / a
      %=取模賦值運算子c %= a 等效于 c = c % a
      **=冪賦值運算子c **= a 等效于 c = c ** a
      //=取整除賦值運算子c //= a 等效于 c = c // a
      :=海象運算子,可在運算式內部為變數賦值,Python3.8 版本新增運算子在這個示例中,賦值運算式可以避免呼叫 len() 兩次:if (n := len(a)) > 10: print(f"List is too long ({n} elements, expected <= 10)")
  • 算術運算子

    • 運算子描述實體
      +加 - 兩個物件相加a + b 輸出結果 31
      -減 - 得到負數或是一個數減去另一個數a - b 輸出結果 -11
      *乘 - 兩個數相乘或是回傳一個被重復若干次的字串a * b 輸出結果 210
      /除 - x 除以 yb / a 輸出結果 2.1
      %取模 - 回傳除法的余數b % a 輸出結果 1
      **冪 - 回傳x的y次冪a**b 為10的21次方
      //取整除 - 向下取接近商的整數>>> 9//2 4 >>> -9//2 -5
  • 比較運算子

    • 運算子描述實體
      ==等于 - 比較物件是否相等(a == b) 回傳 False,
      !=不等于 - 比較兩個物件是否不相等(a != b) 回傳 True,
      >大于 - 回傳x是否大于y(a > b) 回傳 False,
      <小于 - 回傳x是否小于y,所有比較運算子回傳1表示真,回傳0表示假,這分別與特殊的變數True和False等價,注意,這些變數名的大寫,(a < b) 回傳 True,
      >=大于等于 - 回傳x是否大于等于y,(a >= b) 回傳 False,
      <=小于等于 - 回傳x是否小于等于y,(a <= b) 回傳 True,
  • 身份運算子

    • 運算子描述實體
      isis 是判斷兩個識別符號是不是參考自一個物件x is y, 類似 id(x) == id(y) , 如果參考的是同一個物件則回傳 True,否則回傳 False
      is notis not 是判斷兩個識別符號是不是參考自不同物件x is not y , 類似 id(a) != id(b),如果參考的不是同一個物件則回傳結果 True,否則回傳 False,
  • 成員運算子

    • 運算子描述實體
      in如果在指定的序列中找到值回傳 True,否則回傳 False,x 在 y 序列中 , 如果 x 在 y 序列中回傳 True,
      not in如果在指定的序列中沒有找到值回傳 True,否則回傳 False,x 不在 y 序列中 , 如果 x 不在 y 序列中回傳 True,

14、數字型別轉換

  • int(x) 將x轉換為一個整數,
  • float(x) 將x轉換到一個浮點數,

15、list-串列

  • 序列是Python中最基本的資料結構,序列中的每個元素都分配一個數字 - 它的位置,或索引,第一個索引是0,第二個索引是1,依此類推,

    Python有6個序列的內置型別,但最常見的是串列和元組,

    序列都可以進行的操作包括索引,切片,加,乘,檢查成員,

    此外,Python已經內置確定序列的長度以及確定最大和最小的元素的方法,

  • 串列的資料項不需要具有相同的型別

  • list是一種有序的集合,可以隨時添加和洗掉其中的元素,

  • 訪問串列 可正負

    • list1=['1','zz','333','444']
      print(list1[0])
      print(list1[-1])
      
      
  • list是一個可變的有序表,所以,可以往list中追加元素到末尾:

    • list1.append("zzdc")
      list1.append("zdc2")
      print(list1)
      
      

      也可以把元素插入到指定的位置,比如索引號為1的位置

    • list1.insert(1,222)
      print(list1)
      
      

      要洗掉list末尾的元素,用pop()或pop(i)方法 pop會把刪去的元素回傳

    • print(list1.pop())
      print(list1.pop(0))
      
      

      要把某個元素替換成別的元素,可以直接賦值給對應的索引位置:

    • list1[0]='mzd'
      print(list1)
      
      

      list元素也可以是另一個list

    • list1[0]=['1',2,3,4]
      print(list1)
      
      
      >>> p = ['asp', 'php']
      >>> s = ['python', 'java', p, 'scheme']
      
      
  • 串列腳本運算子

    • Python 運算式結果描述
      len([1, 2, 3])3長度
      [1, 2, 3] + [4, 5, 6][1, 2, 3, 4, 5, 6]組合
      [‘Hi!’] * 4[‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’]重復
      3 in [1, 2, 3]True元素是否存在于串列中
      for x in [1, 2, 3]: print(x, end=" ")1 2 3迭代

      遍歷

      • for var in list1:
            print(var)
        
        
  • 串列截取與拼接

    • 操作:

      Python 運算式結果描述
      L[2]‘Taobao’讀取第三個元素
      L[-2]‘Runoob’從右側開始讀取倒數第二個元素: count from the right
      L[1:][‘Runoob’, ‘Taobao’]輸出從第二個元素開始后的所有元素
    • 變數[頭下標:尾下標]
      變數[頭下標:尾下標:步長]
      list=['123','abc',0,True]
      x=list[1:]
      y=list[:3]
      z=list[2:3]
      print(x)
      print(y)
      print
      
      list=['123','abc',0,True]
      x=list[-3:]
      y=list[:-2]
      z=list[-3:-1]
      print(x)
      print(y)
      print(z)
      
      list=['123','abc',0,True,"12345"]
      x=list[1:4:2]
      print(x)
      
      
      
  • 串列函式&方法

    Python包含以下函式:

    序號函式
    1len(list) 串列元素個數
    2max(list) 回傳串列元素最大值
    3min(list) 回傳串列元素最小值
    4list(seq) 將元組轉換為串列

    Python包含以下方法:

    序號方法
    1list.append(obj) 在串列末尾添加新的物件
    2list.count(obj) 統計某個元素在串列中出現的次數
    3list.extend(seq) 在串列末尾一次性追加另一個序列中的多個值(用新串列擴展原來的串列)
    4list.index(obj) 從串列中找出某個值第一個匹配項的索引位置
    5list.insert(index, obj) 將物件插入串列
    6[list.pop(index=-1]) 移除串列中的一個元素(默認最后一個元素),并且回傳該元素的值
    7list.remove(obj) 移除串列中某個值的第一個匹配項
    8list.reverse() 反向串列中元素
    9list.sort( key=None, reverse=False) 對原串列進行排序
    10list.clear() 清空串列
    11list.copy() 復制串列

16、元組-tuple

  • tuple和list非常類似,但是tuple一旦初始化就不能修改

  • 不可變的tuple有什么意義?因為tuple不可變,所以代碼更安全,如果可能,能用tuple代替list就盡量用tuple,

  • tuple的陷阱:當你定義一個tuple時,在定義的時候,tuple的元素就必須被確定下來

    • t = (1, 2)
      t = ()  #如果要定義一個空的tuple
      
      

      要定義一個只有1個元素的tuple,如果你這么定義

      >>> t = (1)
      >>> t
      1
      
      

      定義的不是tuple,是1這個數!這是因為括號()既可以表示tuple,又可以表示數學公式中的小括號,這就產生了歧義,因此,Python規定,這種情況下,按小括號進行計算,計算結果自然是1

      所以,只有1個元素的tuple定義時必須加一個逗號,,來消除歧義:

      >>> t = (1,)
      >>> t
      (1,)
      
      

      一個“可變的”tuple:

      >>> t = ('a', 'b', ['A', 'B'])
      >>> t[2][0] = 'X'
      >>> t[2][1] = 'Y'
      >>> t
      ('a', 'b', ['X', 'Y'])
      
      

      這個tuple定義的時候有3個元素,分別是'a''b'和一個list,不是說tuple一旦定義后就不可變了嗎?怎么后來又變了?

      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gT70crDv-1612773531303)(1.assets/0.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Qusc6dYV-1612773531305)(1.assets/0-20200713003324698.png)]

表面上看,tuple的元素確實變了,但其實變的不是tuple的元素,而是list的元素,tuple一開始指向的list并沒有改成別的list,所以,tuple所謂的“不變”是說,tuple的每個元素,指向永遠不變,即指向'a',就不能改成指向'b',指向一個list,就不能改成指向其他物件,但指向的這個list本身是可變的!

要創建一個內容也不變的tuple怎么做?那就必須保證tuple的每一個元素本身也不能變

  • 以對元組進行連接組合

  • tup1 = (12, 34.56)
    tup2 = ('abc', 'xyz')
     
    # 以下修改元組元素操作是非法的,
    # tup1[0] = 100
     
    # 創建一個新的元組
    tup3 = tup1 + tup2
    
    

17、if else

  • if 判斷條件:
        執行陳述句……
    else:
        執行陳述句……
    
    

    當判斷條件為多個值時

    if 判斷條件1:
        執行陳述句1……
    elif 判斷條件2:
        執行陳述句2……
    elif 判斷條件3:
        執行陳述句3……
    else:
        執行陳述句4……
    
    

    if判斷條件還可以簡寫,比如寫:

    if x:
        print('True')
    
    

    只要x是非零數值、非空字串、非空list等,就判斷為True,否則為False

    再議 input

    最后看一個有問題的條件判斷,很多同學會用input()讀取用戶的輸入,這樣可以自己輸入,程式運行得更有意思:

    birth = input('birth: ')
    if birth < 2000:
        print('00前')
    else:
        print('00后')
    
    

    輸入1982,結果報錯:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unorderable types: str() > int()
    
    

    這是因為input()回傳的資料型別是strstr不能直接和整數比較,必須先把str轉換成整數,Python提供了int()函式來完成這件事情:

    s = input('birth: ')
    birth = int(s)
    if birth < 2000:
        print('00前')
    else:
        print('00后')
    
    

    再次運行,就可以得到正確地結果,但是,如果輸入abc呢?又會得到一個錯誤資訊:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: invalid literal for int() with base 10: 'abc'
    
    

    原來int()函式發現一個字串并不是合法的數字時就會報錯,程式就退出了,

18、回圈

  • while 判斷條件(condition):
        執行陳述句(statements)……
    
    

回圈使用 else 陳述句

? 在 python 中,while … else 在回圈條件為 false 時執行 else 陳述句塊:

  • count = 0
    while count < 5:
       print count, " is  less than 5"
       count = count + 1
    else:
       print count, " is not less than 5"
    
    

for 回圈陳述句

  • for iterating_var in sequence:
       statements(s)
    
    

在 python 中,for … else 表示這樣的意思,for 中的陳述句和普通的沒有區別,else 中的陳述句會在回圈正常執行完(即 for 不是通過 break 跳出而中斷的)的情況下執行,while … else 也是一樣,

Python 語言允許在一個回圈體里面嵌入另一個回圈,

for iterating_var in sequence:
   for iterating_var in sequence:
      statements(s)
   statements(s)
   
while expression:
   while expression:
      statement(s)
   statement(s)

19、break、continue和pass

  • Python continue 陳述句跳出本次回圈,而break跳出整個回圈,

  • Python pass 是空陳述句,是為了保持程式結構的完整性,

    pass 不做任何事情,一般用做占位陳述句,

    • 在 Python3.x 的時候 pass 可以寫或不寫,

      python2.x:

      def function():
          # 空函式在Python2.x版本中pass是必須的
          pass
      
      

      python3.x

      def function():
          # 在Python3.x的時候pass可以寫或不寫
          pass
      
      

20、Dictionary–字典

  • 字典是另一種可變容器模型,且可存盤任意型別物件,

    字典的每個鍵值 key=>value 對用冒號 : 分割,每個鍵值對之間用逗號 , 分割,整個字典包括在花括號 {} 中 ,格式如下所示:

    d = {key1 : value1, key2 : value2 }
    
    

    如果key不存在,dict就會報錯:

    >>> d['Thomas']
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: 'Thomas'
    
    

    要避免key不存在的錯誤,有兩種辦法,一是通過in判斷key是否存在:

    >>> 'Thomas' in d
    False
    
    

    二是通過dict提供的get()方法,如果key不存在,可以回傳None,或者自己指定的value:

    >>> d.get('Thomas')
    >>> d.get('Thomas', -1)
    -1
    
    

    注意:回傳None的時候Python的互動環境不顯示結果,

    要洗掉一個key,用pop(key)方法,對應的value也會從dict中洗掉:

    >>> d.pop('Bob')
    75
    >>> d
    {'Michael': 95, 'Tracy': 85}
    
    

    請務必注意,dict內部存放的順序和key放入的順序是沒有關系的,

    和list比較,dict有以下幾個特點:

    1. 查找和插入的速度極快,不會隨著key的增加而變慢;
    2. 需要占用大量的記憶體,記憶體浪費多,

    而list相反:

    1. 查找和插入的時間隨著元素的增加而增加;
    2. 占用空間小,浪費記憶體很少,

    所以,dict是用空間來換取時間的一種方法,

    這是因為dict根據key來計算value的存盤位置,如果每次計算相同的key得出的結果不同,那dict內部就完全混亂了,這個通過key計算位置的演算法稱為哈希演算法(Hash),

    要保證hash的正確性,作為key的物件就不能變,在Python中,字串、整數等都是不可變的,因此,可以放心地作為key,而list是可變的,就不能作為key:

21、函式

  • 定義函式

    • 在Python中,定義一個函式要使用def陳述句,依次寫出函式名、括號、括號中的引數和冒號:,然后,在縮進塊中撰寫函式體,函式的回傳值用return陳述句回傳,

    • def my_abs(x):
          if x >= 0:
              return x
          else:
              return -x
      
      

      如果你已經把my_abs()的函式定義保存為abstest.py檔案了,那么,可以在該檔案的當前目錄下啟動Python解釋器,用from abstest import my_abs來匯入my_abs()函式,注意abstest是檔案名(不含.py擴展名):

  • 引數的可變與不可變

    • 在 python 中,strings, tuples, 和 numbers 是不可更改的物件,而 list,dict 等則是可以修改的物件,

      • **不可變型別:**變數賦值 a=5 后再賦值 a=10,這里實際是新生成一個 int 值物件 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,相當于新生成了a,
      • **可變型別:**變數賦值 la=[1,2,3,4] 后再賦值 la[2]=5 則是將 list la 的第三個元素值更改,本身la沒有動,只是其內部的一部分值被修改了,

      python 函式的引數傳遞:

      • **不可變型別:**類似 c++ 的值傳遞,如 整數、字串、元組,如fun(a),傳遞的只是a的值,沒有影響a物件本身,比如在 fun(a)內部修改 a 的值,只是修改另一個復制的物件,不會影響 a 本身,
      • **可變型別:**類似 c++ 的參考傳遞,如 串列,字典,如 fun(la),則是將 la 真正的傳過去,修改后fun外部的la也會受影響

      python 中一切都是物件,嚴格意義我們不能說值傳遞還是參考傳遞,我們應該說傳不可變物件和傳可變物件,

  • python 傳不可變物件實體

    • #!/usr/bin/python
      # -*- coding: UTF-8 -*-
       
      def ChangeInt( a ):
          a = 10
       
      b = 2
      ChangeInt(b)
      print b # 結果是 2
      
      
  • 傳可變物件實體

    • !/usr/bin/python
      # -*- coding: UTF-8 -*-
       
      # 可寫函式說明
      def changeme( mylist ):
         "修改傳入的串列"
         mylist.append([1,2,3,4])
         print "函式內取值: ", mylist
         return
       
      # 呼叫changeme函式
      mylist = [10,20,30]
      changeme( mylist )
      print "函式外取值: ", mylist
      
      
  • 命名關鍵字引數

    • 關鍵字引數和函式呼叫關系緊密,函式呼叫使用關鍵字引數來確定傳入的引數值,

      使用關鍵字引數允許函式呼叫時引數的順序與宣告時不一致,因為 Python 解釋器能夠用引數名匹配引數值

      • #!/usr/bin/python
        # -*- coding: UTF-8 -*-
         
        #可寫函式說明
        def printme( str ):
           "列印任何傳入的字串"
           print str
           return
         
        #呼叫printme函式
        printme( str = "My string")
        
        
  • 關鍵字引數

    **可變引數允許你傳入0個或任意個引數,這些可變引數在函式呼叫時自動組裝為一個tuple,而關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict,**請看示例:

    def person(name, age, **kw):
        print('name:', name, 'age:', age, 'other:', kw)
    
    

    函式person除了必選引數nameage外,還接受關鍵字引數kw,在呼叫該函式時,可以只傳入必選引數:

    >>> person('Michael', 30)
    name: Michael age: 30 other: {}
    
    

    也可以傳入任意個數的關鍵字引數:

    >>> person('Bob', 35, city='Beijing')
    name: Bob age: 35 other: {'city': 'Beijing'}
    >>> person('Adam', 45, gender='M', job='Engineer')
    name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
    
    

    關鍵字引數有什么用?它可以擴展函式的功能,比如,在person函式里,我們保證能接收到nameage這兩個引數,但是,如果呼叫者愿意提供更多的引數,我們也能收到,試想你正在做一個用戶注冊的功能,除了用戶名和年齡是必填項外,其他都是可選項,利用關鍵字引數來定義這個函式就能滿足注冊的需求,

    和可變引數類似,也可以先組裝出一個dict,然后,把該dict轉換為關鍵字引數傳進去:

    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, city=extra['city'], job=extra['job'])
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
    
    

    當然,上面復雜的呼叫可以用簡化的寫法:

    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, **extra)
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
    
    

    **extra表示把extra這個dict的所有key-value用關鍵字引數傳入到函式的**kw引數,kw將獲得一個dict,注意kw獲得的dict是extra的一份拷貝,對kw的改動不會影響到函式外的extra

  • 命名關鍵字引數

    對于關鍵字引數,函式的呼叫者可以傳入任意不受限制的關鍵字引數,至于到底傳入了哪些,就需要在函式內部通過kw檢查,

    仍以person()函式為例,我們希望檢查是否有cityjob引數:

    def person(name, age, **kw):
        if 'city' in kw:
            # 有city引數
            pass
        if 'job' in kw:
            # 有job引數
            pass
        print('name:', name, 'age:', age, 'other:', kw)
    
    

    但是呼叫者仍可以傳入不受限制的關鍵字引數:

    >>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)
    
    

    **如果要限制關鍵字引數的名字,就可以用命名關鍵字引數,例如,只接收cityjob作為關鍵字引數,**這種方式定義的函式如下:

    def person(name, age, *, city, job):
        print(name, age, city, job)
    
    

    和關鍵字引數**kw不同,命名關鍵字引數需要一個特殊分隔符**后面的引數被視為命名關鍵字引數,

    呼叫方式如下:

    >>> person('Jack', 24, city='Beijing', job='Engineer')
    Jack 24 Beijing Engineer
    
    

    如果函式定義中已經有了一個可變引數,后面跟著的命名關鍵字引數就不再需要一個特殊分隔符*了:

    def person(name, age, *args, city, job):
        print(name, age, args, city, job)
    
    

    命名關鍵字引數必須傳入引數名,這和位置引數不同,如果沒有傳入引數名,呼叫將報錯:

    >>> person('Jack', 24, 'Beijing', 'Engineer')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: person() takes 2 positional arguments but 4 were given
    
    

    由于呼叫時缺少引數名cityjob,Python解釋器把這4個引數均視為位置引數,但person()函式僅接受2個位置引數,

    命名關鍵字引數可以有預設值,從而簡化呼叫:

    def person(name, age, *, city='Beijing', job):
        print(name, age, city, job)
    
    

    由于命名關鍵字引數city具有默認值,呼叫時,可不傳入city引數:

    >>> person('Jack', 24, job='Engineer')
    Jack 24 Beijing Engineer
    
    

    使用命名關鍵字引數時,要特別注意,如果沒有可變引數,就必須加一個*作為特殊分隔符,如果缺少*,Python解釋器將無法識別位置引數和命名關鍵字引數:

    def person(name, age, city, job):
        # 缺少 *,city和job被視為位置引數
        pass
    
    
  • 默認引數

    • 呼叫函式時,默認引數的值如果沒有傳入,則被認為是默認值,下例會列印默認的age,如果age沒有被傳入:

    • #!/usr/bin/python
      # -*- coding: UTF-8 -*-
       
      #可寫函式說明
      def printinfo( name, age = 35 ):
         "列印任何傳入的字串"
         print "Name: ", name
         print "Age ", age
         return
       
      #呼叫printinfo函式
      printinfo( age=50, name="miki" )
      printinfo( name="miki" )
      
      

      定義默認引數要牢記一點:默認引數必須指向不變物件!

      def add_end(L=[]):
          L.append('END')
          return L
      >>> add_end()
      ['END']
      >>> add_end()
      ['END', 'END']
      >>> add_end()
      ['END', 'END', 'END']
      
      

      很多初學者很疑惑,默認引數是[],但是函式似乎每次都“記住了”上次添加了'END'后的list,

      原因解釋如下:

      Python函式在定義的時候,默認引數L的值就被計算出來了,即[],因為默認引數L也是一個變數,它指向物件[],每次呼叫該函式,如果改變了L的內容,則下次呼叫時,默認引數的內容就變了,不再是函式定義時的[]了,

  • 可變引數

    在Python函式中,還可以定義可變引數,顧名思義,可變引數就是傳入的引數個數是可變的,可以是1個、2個到任意個,還可以是0個,

    我們以數學題為例子,給定一組數字a,b,c……,請計算a2 + b2 + c2 + ……,

    要定義出這個函式,我們必須確定輸入的引數,由于引數個數不確定,我們首先想到可以把a,b,c……作為一個list或tuple傳進來,這樣,函式可以定義如下:

    def calc(numbers):
        sum = 0
        for n in numbers:
            sum = sum + n * n
        return sum
    
    

    但是呼叫的時候,需要先組裝出一個list或tuple:

    >>> calc([1, 2, 3])
    14
    >>> calc((1, 3, 5, 7))
    84
    
    

    如果利用可變引數,呼叫函式的方式可以簡化成這樣:

    >>> calc(1, 2, 3)
    14
    >>> calc(1, 3, 5, 7)
    84
    
    

    所以,我們把函式的引數改為可變引數:

    def calc(*numbers):
        sum = 0
        for n in numbers:
            sum = sum + n * n
        return sum
    
    

    定義可變引數和定義一個list或tuple引數相比,僅僅在引數前面加了一個*,在函式內部,引數numbers接收到的是一個tuple,因此,函式代碼完全不變,但是,呼叫該函式時,可以傳入任意個引數,包括0個引數

    >>> calc(1, 2)
    5
    >>> calc()
    0
    
    

    如果已經有一個list或者tuple,要呼叫一個可變引數怎么辦?可以這樣做:

    >>> nums = [1, 2, 3]
    >>> calc(nums[0], nums[1], nums[2])
    14
    
    

    這種寫法當然是可行的,問題是太繁瑣,所以Python允許你在list或tuple前面加一個*號,把list或tuple的元素變成可變引數傳進去

    >>> nums = [1, 2, 3]
    >>> calc(*nums)
    14
    
    

    *nums表示把nums這個list的所有元素作為可變引數傳進去,這種寫法相當有用,而且很常見,

    def change(*numbers):
        sum=0;
        for i in numbers:
            sum+=i;
        return sum;
    print(change(1,2,3))
    st=(1,2,3,4,5,6,7)
    print(change(*st))
    
    

    在Python中定義函式,可以用必選引數、默認引數、可變引數和關鍵字引數,這4種引數都可以一起使用,或者只用其中某些,但是請注意,引數定義的順序必須是:必選引數、默認引數、可變引數和關鍵字引數,

    def func(a, b, c=0, *args, **kw):
        print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
    >>> func(1, 2)
    a = 1 b = 2 c = 0 args = () kw = {}
    >>> func(1, 2, c=3)
    a = 1 b = 2 c = 3 args = () kw = {}
    >>> func(1, 2, 3, 'a', 'b')
    a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
    >>> func(1, 2, 3, 'a', 'b', x=99)
    a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
    
    
  • 匿名函式

    • python 使用 lambda 來創建匿名函式,

      • lambda只是一個運算式,函式體比def簡單很多,
      • lambda的主體是一個運算式,而不是一個代碼塊,僅僅能在lambda運算式中封裝有限的邏輯進去,
      • lambda函式擁有自己的命名空間,且不能訪問自有引數串列之外或全域命名空間里的引數,
      • 雖然lambda函式看起來只能寫一行,卻不等同于C或C++的行內函式,后者的目的是呼叫小函式時不占用堆疊記憶體從而增加運行效率,

      語法

      lambda函式的語法只包含一個陳述句,如下:

      lambda [arg1 [,arg2,.....argn]]:expression
      
      

      如下實體:

      實體(Python 2.0+)

      #!/usr/bin/python # -- coding: UTF-8 -- # 可寫函式說明 sum = lambda arg1, arg2: arg1 + arg2 # 呼叫sum函式 print "相加后的值為 : ", sum( 10, 20 ) print "相加后的值為 : ", sum( 20, 20 )

      以上實體輸出結果:

      相加后的值為 :  30
      相加后的值為 :  40
      
      
  • 回傳多個值

    • import math
      
      def move(x, y, step, angle=0):
          nx = x + step * math.cos(angle)
          ny = y - step * math.sin(angle)
          return nx, ny
          
      x, y = move(100, 100, 60, math.pi / 6)
      
      
  • 全域變數和區域變數

    定義在函式內部的變數擁有一個區域作用域,定義在函式外的擁有全域作用域,

    區域變數只能在其被宣告的函式內部訪問,而全域變數可以在整個程式范圍內訪問,呼叫函式時,所有在函式內宣告的變數名稱都將被加入到作用域中,如下實體:

    實體(Python 2.0+)

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
     
    total = 0 # 這是一個全域變數
    # 可寫函式說明
    def sum( arg1, arg2 ):
       #回傳2個引數的和."
       total = arg1 + arg2 # total在這里是區域變數.
       print "函式內是區域變數 : ", total
       return total
     
    #呼叫sum函式
    sum( 10, 20 )
    print "函式外是全域變數 : ", total
    
    

    默認引數一定要用不可變物件,如果是可變物件,程式運行時會有邏輯錯誤!

    要注意定義可變引數和關鍵字引數的語法:

    *args是可變引數,args接收的是一個tuple;

    kw是關鍵字引數,kw接收的是一個dict,

    以及呼叫函式時如何傳入可變引數和關鍵字引數的語法:

    可變引數既可以直接傳入:func(1, 2, 3),又可以先組裝list或tuple,再通過*args傳入:func(*(1, 2, 3))

    關鍵字引數既可以直接傳入:func(a=1, b=2),又可以先組裝dict,再通過**kw傳入:func(**{'a': 1, 'b': 2})

    **使用*args**kw是Python的習慣寫法,當然也可以用其他引數名,但最好使用習慣用法,

    命名的關鍵字引數是為了限制呼叫者可以傳入的引數名,同時可以提供默認值,

    定義命名的關鍵字引數在沒有可變引數的情況下不要忘了寫分隔符*,否則定義的將是位置引數,

  • 函式別名

    • 函式名其實就是指向一個函式物件的參考,完全可以把函式名賦給一個變數,相當于給這個函式起了一個“別名

    • >>> a = abs # 變數a指向abs函式
      >>> a(-1) # 所以也可以通過a呼叫abs函式
      1
      
      
  • Import 與 from import

    • >>> import datetime
       
      >>> print(datetime.datetime.now()
      
      
      >>> from datetime import datetime
       
      >>> print(datetime.now())
      
      
  • 包的概念 packa包中

    • from packa.a import a
      from packa.b import b
      a()
      b()
      
      

22、切片

lis=[1,2,3,4,5,6,7,8]
print(lis[0:3])
print(lis[:4])
print(lis[-5:-3])
print(lis[-5:])

23、迭代

  • 在Python中,迭代是通過for ... in來完成的

    • for num in lis:
          print(num)
      
      
  • 默認情況下,dict迭代的是key,如果要迭代value,可以用for value in d.values(),如果要同時迭代key和value,可以用for k, v in d.items()

    • d = {'a': 1, 'b': 2, 'c': 3}
      for k in d:
          print(k)
      for v in d.values():
          print(v)
      for k,v in d.items():
          print('k=',k,'v=',v)
      
      
  • 字串也是可迭代物件

    • for ch in 'ABCD':
          print(ch)
      
      
  • 如何判斷一個物件是可迭代物件呢?方法是通過collections模塊的Iterable型別判斷:

    • >>> from collections import Iterable
      >>> isinstance('abc', Iterable) # str是否可迭代
      True
      >>> isinstance([1,2,3], Iterable) # list是否可迭代
      True
      >>> isinstance(123, Iterable) # 整數是否可迭代
      False
      
      
  • 如果要對list實作類似Java那樣的下標回圈怎么辦?Python內置的enumerate函式可以把一個list變成索引-元素對,這樣就可以在for回圈中同時迭代索引和元素本身:

    • for i,v in enumerate(lis):
          print(i,v)
      
      

24、串列生成式

  • 串列生成式即List Comprehensions,是Python內置的非常簡單卻強大的可以用來創建list的生成式,

  • python range() 函式可創建一個整數串列,一般用在 for 回圈中,

  • print(list(range(1,20)))
    
    l=[]
    for i in range(1,20):
        l.append(i*i)
    print(l)
    
    print([x * x for x in range(1,5)]) #串列生成式
    
    print([x * x for x in range(1, 10) if x%2==1])
    
    print([d for d in os.listdir('/')])  #import os
    
    

    寫串列生成式時,把要生成的元素x * x放到前面,后面跟for回圈,就可以把list創建出來,十分有用,多寫幾次,很快就可以熟悉這種語法,

    for回圈后面還可以加上if判斷

    還可以使用兩層回圈,可以生成全排列

    print([x+y for x in 'abc' for y in 'bdf'])
    
    
  • 串列生產式也可以同時使用多個變數

    • d = {'x': 'A', 'y': 'B', 'z': 'C'}
      print([k+'='+v for k,v in d.items()])
      
      
  • 串列生成式中的if…else

    • print([[x if x % 2 == 0 else -x for x in range(1, 11)]])
      
      print([x if x>2 else 100 for x in range(1,5)])
      
      

      在一個串列生成式中,for前面的if ... else是運算式,而for后面的if是過濾條件,不能帶else

25、生成器

  • 通過串列生成式,我們可以直接創建一個串列,但是,受到記憶體限制,串列容量肯定是有限的,而且,創建一個包含100萬個元素的串列,不僅占用很大的存盤空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了,

    所以,如果串列元素可以按照某種演算法推算出來,那我們是否可以在回圈的程序中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間,在Python中,這種一邊回圈一邊計算的機制,稱為生成器:generator,

    要創建一個generator,有很多種方法,第一種方法很簡單,只要把一個串列生成式的[]改成(),就創建了一個generator

    g=(x*x for x in range(10));
    print(next(g))
    print(next(g))
    print(next(g))
    for i in g:
        print(i)
    
    

    generator保存的是演算法,每次呼叫next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤,

    我們創建了一個generator后,基本上永遠不會呼叫next(),而是通過for回圈來迭代它,并且不需要關心StopIteration的錯誤,

  • generator非常強大,如果推算的演算法比較復雜,用類似串列生成式的for回圈無法實作的時候,還可以用函式來實作

    • def fib(max):
          n, a, b = 0, 0, 1
          while n < max:
              yield b
              a, b = b, a + b
              n = n + 1
          return 'done'
      
      

      如果一個函式定義中包含yield關鍵字,那么這個函式就不再是一個普通函式,而是一個generator;

      函式是順序執行,遇到return陳述句或者最后一行函式陳述句就回傳,而變成generator的函式,在每次呼叫next()的時候執行,遇到yield陳述句回傳,再次執行時從上次回傳的yield陳述句處繼續執行,

      def odd():
          print('step 1')
          yield 1
          print('step 2')
          yield(3)
          print('step 3')
          yield(5)
      
      

      但是用for回圈呼叫generator時,發現拿不到generator的return陳述句的回傳值,如果想要拿到回傳值,必須捕獲StopIteration錯誤,回傳值包含在StopIterationvalue中:

      >>> g = fib(6)
      >>> while True:
      ...     try:
      ...         x = next(g)
      ...         print('g:', x)
      ...     except StopIteration as e:
      ...         print('Generator return value:', e.value)
      ...         break
      ...
      g: 1
      g: 1
      g: 2
      g: 3
      g: 5
      g: 8
      Generator return value: done
      
      

26、迭代器

  • 一類是集合資料型別,如listtupledictsetstr等;

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

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

    可以使用isinstance()判斷一個物件是否是Iterable物件

  • 而生成器不但可以作用于for回圈,還可以被next()函式不斷呼叫并回傳下一個值,直到最后拋出StopIteration錯誤表示無法繼續回傳下一個值了,

    可以被next()函式呼叫并不斷回傳下一個值的物件稱為迭代器:Iterator

  • 生成器都是Iterator物件,但listdictstr雖然是Iterable,卻不是Iterator

    listdictstrIterable變成Iterator可以使用iter()函式:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    
    

    你可能會問,為什么listdictstr等資料型別不是Iterator

    這是因為Python的Iterator物件表示的是一個資料流,Iterator物件可以被next()函式呼叫并不斷回傳下一個資料,直到沒有資料時拋出StopIteration錯誤,可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函式實作按需計算下一個資料,所以Iterator的計算是惰性的,只有在需要回傳下一個資料時它才會計算

    Iterator甚至可以表示一個無限大的資料流,例如全體自然數,而使用list是永遠不可能存盤全體自然數的,

  • 凡是可作用于for回圈的物件都是Iterable型別;

    凡是可作用于next()函式的物件都是Iterator型別,它們表示一個惰性計算的序列;

    集合資料型別如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函式獲得一個Iterator物件,

    Python的for回圈本質上就是通過不斷呼叫next()

27、函式式編程

1、高階函式

  • 變數可以指向函式
  • 函式名也是變數
  • 既然變數可以指向函式,函式的引數能接收變數,那么一個函式就可以接收另一個函式作為引數,這種函式就稱之為高階函式

  • f=abs
    print(f)
    abs=10  #abs本來是內嵌函式
    print(abs)
    
    def absAdd(x,y,f):
        return f(x)+f(y)
    print(absAdd(-1,-2,f))
    
    

2、map/reduce

  • map

    • def pow(x):
          return x*x
      # 回傳的是一個Iterator  因此通過list()函式讓它把整個序列都計算出來并回傳一個list,
      lis = map(pow, [1, 2, 3, 4, 5])
      print(list(lis))
      
      print(list(map(str,[1,2,3,4,5])))   #回傳字串
      
      
  • reduce

    • from functools import reduce
      def add(x,y):
          return x+y
      print(reduce(add,[1,2,3,4,5]))
      
      

      reduce把一個函式作用在一個序列[x1, x2, x3, ...]上,這個函式必須接收兩個引數,re**duce把結果繼續和序列的下一個元素做累積計算**,其效果就是:

      reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
      
      
  • filter

    • Python內建的filter()函式用于過濾序列,和map()類似,filter()也接收一個函式和一個序列,和map()不同的是,filter()把傳入的函式依次作用于每個元素,然后根據回傳值是True還是False決定保留還是丟棄該元素,

      • def is_odd(n):
            return n % 2 == 1
        
        list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
        # 結果: [1, 5, 9, 15]
        
        
  • sorted

    • >>> sorted([36, 5, -12, 9, -21])
      [-21, -12, 5, 9, 36]
      
      

      此外,sorted()函式也是一個高階函式,它還可以接收一個key函式來實作自定義的排序,例如按絕對值大小排序:

      >>> sorted([36, 5, -12, 9, -21], key=abs)
      [5, 9, -12, -21, 36]
      
      

      key指定的函式將作用于list的每一個元素上,并根據key函式回傳的結果進行排序,

3、回傳函式

  • def lazy_sum(*args):
        def sum():
            ax = 0
            for n in args:
                ax = ax + n
            return ax
        return sum
    
    f = lazy_sum(1, 3, 5, 7, 9)
    print(f())
    
    

    當我們呼叫lazy_sum()時,回傳的并不是求和結果,而是求和函式:

    在函式lazy_sum中又定義了函式sum,并且,內部函式sum可以參考外部函式lazy_sum的引數和區域變數,當lazy_sum回傳函式sum時,相關引數和變數都保存在回傳的函式中,這種稱為“閉包(Closure)”的程式結構擁有極大的威力,

閉包

  • 當一個函式回傳了一個函式后,其內部的區域變數還被新函式參考

  • 回傳的函式并沒有立刻執行,而是直到呼叫了f()才執行

  • def count():
        fs = []
        for i in range(1, 4):
            def f():
                 return i*i
            fs.append(f)
        return fs
    
    f1, f2, f3 = count()
    在上面的例子中,每次回圈,都創建了一個新的函式,然后,把創建的3個函式都回傳了,
    
    你可能認為呼叫f1(),f2()和f3()結果應該是149,但實際結果是:
    
    >>> f1()
    9
    >>> f2()
    9
    >>> f3()
    9
    
    
    def count():
        fs = []
        for i in range(1, 4):
            def f():
                 return i*i
            fs.append(f)
        return fs
    
    f1, f2, f3 = count()
    
    

    在上面的例子中,每次回圈,都創建了一個新的函式,然后,把創建的3個函式都回傳了,

    你可能認為呼叫f1()f2()f3()結果應該是149,但實際結果是:

    >>> f1()
    9
    >>> f2()
    9
    >>> f3()
    9
    
    

    全部都是9!原因就在于回傳的函式參考了變數i,但它并非立刻執行,等到3個函式都回傳時,它們所參考的變數i已經變成了3,因此最終結果為9

    回傳閉包時牢記一點:回傳函式不要參考任何回圈變數,或者后續會發生變化的變數,

4、lambda

  • f=lambda x:x*x-2;
    f1=lambda x,y:x+y
    print(f(2))
    print(f1(3,4))
    
    

5、裝飾器

  • def log(f):
        def decrotor(*args,**kw):
            print('call %s():' % f.__name__)
            return f(*args, **kw)
        return decrotor
    @log
    def tes():
        print('tes')
    tes()
    
    

    @log放到now()函式的定義處,相當于執行了陳述句:

    now = log(now)
    
    

    由于log()是一個decorator,回傳一個函式,所以,原來的now()函式仍然存在,只是現在同名的now變數指向了新的函式,于是呼叫now()將執行新函式,即在log()函式中回傳的wrapper()函式,

    wrapper()函式的引數定義是(*args, **kw),因此,wrapper()函式可以接受任意引數的呼叫,在wrapper()函式內,首先列印日志,再緊接著呼叫原始函式,

  • 如果decorator本身需要傳入引數,那就需要撰寫一個回傳decorator的高階函式,寫出來會更復雜,比如,要自定義log的文本:

    def log(text):
        def decorator(func):
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    
    

    這個3層嵌套的decorator用法如下:

    @log('execute')
    def now():
        print('2015-3-25')
    
    

    執行結果如下:

    >>> now()
    execute now():
    2015-3-25
    
    

    和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:

    >>> now = log('execute')(now)
    
    

    我們來剖析上面的陳述句,首先執行log('execute'),回傳的是decorator函式,再呼叫回傳的函式,引數是now函式,回傳值最終是wrapper函式,

    以上兩種decorator的定義都沒有問題,但還差最后一步,因為我們講了函式也是物件,它有__name__等屬性,但你去看經過decorator裝飾之后的函式,它們的__name__已經從原來的'now'變成了'wrapper'

    >>> now.__name__
    'wrapper'
    
    

    因為回傳的那個wrapper()函式名字就是'wrapper',所以,需要把原始函式的__name__等屬性復制到wrapper()函式中,否則,有些依賴函式簽名的代碼執行就會出錯,

    不需要撰寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是干這個事的,所以,一個完整的decorator的寫法如下:

    import functools
    
    def log(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    
    

    或者針對帶引數的decorator:

    import functools
    
    def log(text):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    
    

    import functools是匯入functools模塊,模塊的概念稍候講解,現在,只需記住在定義wrapper()的前面加上@functools.wraps(func)即可,

6、偏函式

  • Python的functools模塊提供了很多有用的功能,其中一個就是偏函式(Partial function),

  • nt()函式可以把字串轉換為整數,當僅傳入字串時,int()函式默認按十進制轉換:

    >>> int('12345')
    12345
    
    

    int()函式還提供額外的base引數,默認值為10,如果傳入base引數,就可以做N進制的轉換:

    >>> int('12345', base=8)
    5349
    >>> int('12345', 16)
    74565
    
    

    假設要轉換大量的二進制字串,每次都傳入int(x, base=2)非常麻煩,于是,我們想到,可以定義一個int2()的函式,默認把base=2傳進去:

    def int2(x, base=2):
        return int(x, base)
    
    

    這樣,我們轉換二進制就非常方便了:

    >>> int2('1000000')
    64
    >>> int2('1010101')
    85
    
    

    functools.partial就是幫助我們創建一個偏函式的,不需要我們自己定義int2(),可以直接使用下面的代碼創建一個新的函式int2

    >>> import functools
    >>> int2 = functools.partial(int, base=2)
    >>> int2('1000000')
    64
    >>> int2('1010101')
    85
    
    

    所以,簡單總結functools.partial的作用就是,把一個函式的某些引數給固定住(也就是設定默認值),回傳一個新的函式,呼叫這個新函式會更簡單,

    注意到上面的新的int2函式,僅僅是把base引數重新設定默認值為2,但也可以在函式呼叫時傳入其他值:

    >>> int2('1000000', base=10)
    1000000
    
    

    最后,創建偏函式時,實際上可以接收函式物件、*args**kw這3個引數,當傳入:

    int2 = functools.partial(int, base=2)
    
    

    實際上固定了int()函式的關鍵字引數base,也就是:

    int2('10010')
    
    

    相當于:

    kw = { 'base': 2 }
    int('10010', **kw)
    
    

    當傳入:

    max2 = functools.partial(max, 10)
    
    

    實際上會把10作為*args的一部分自動加到左邊,也就是:

    max2(5, 6, 7)
    
    

    相當于:

    args = (10, 5, 6, 7)
    max(*args)
    
    

    結果為10

28、模塊

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '

__author__ = 'Michael Liao'

import sys

def test():
    args = sys.argv
    if len(args)==1:
        print('Hello, world!')
    elif len(args)==2:
        print('Hello, %s!' % args[1])
    else:
        print('Too many arguments!')

if __name__=='__main__':
    test()

第1行和第2行是標準注釋,第1行注釋可以讓這個hello.py檔案直接在Unix/Linux/Mac上運行,第2行注釋表示.py檔案本身使用標準UTF-8編碼;

第4行是一個字串,表示模塊的檔案注釋,任何模塊代碼的第一個字串都被視為模塊的檔案注釋;

第6行使用__author__變數把作者寫進去,這樣當你公開源代碼后別人就可以瞻仰你的大名;

以上就是Python模塊的標準檔案模板,當然也可以全部刪掉不寫,但是,按標準辦事肯定沒錯,

你可能注意到了,使用sys模塊的第一步,就是匯入該模塊:

import sys

匯入sys模塊后,我們就有了變數sys指向該模塊,利用sys這個變數,就可以訪問sys模塊的所有功能,

sys模塊有一個argv變數,用list存盤了命令列的所有引數,argv至少有一個元素,因為第一個引數永遠是該.py檔案的名稱,例如:

運行python3 hello.py獲得的sys.argv就是['hello.py']

運行python3 hello.py Michael獲得的sys.argv就是['hello.py', 'Michael']

最后,注意到這兩行代碼:

if __name__=='__main__':
    test()

**當我們在命令列運行hello模塊檔案時,Python解釋器把一個特殊變數__name__置為__main__,**而如果在其他地方匯入該hello模塊時,if判斷將失敗,因此,這種if測驗可以讓一個模塊通過命令列運行時執行一些額外的代碼,最常見的就是運行測驗,

私有變數和函式
  • 正常的函式和變數名是公開的(public),可以被直接參考,比如:abcx123PI等;

    類似__xxx__這樣的變數是特殊變數,可以被直接參考,但是有特殊用途,比如上面的__author____name__就是特殊變數,hello模塊定義的檔案注釋也可以用特殊變數__doc__訪問,我們自己的變數一般不要用這種變數名;

    **類似_xxx__xxx**這樣的函式或變數就是非公開的(private),不應該被直接參考,比如_abc__abc等;

  • 類似_xxx__xxx這樣的函式或變數就是非公開的(private),不應該被直接參考,比如_abc__abc等;

    之所以我們說,private函式和變數“不應該”被直接參考,而不是“不能”被直接參考,是因為Python并沒有一種方法可以完全限制訪問private函式或變數,但是,從編程習慣上不應該參考private函式或變數,

在Python中,有以下幾種方式來定義變數:

  • xx:公有變數
  • _xx:單前置下劃線,私有化屬性或方法,類物件和子類可以訪問,from somemodule import *禁止匯入
  • __xx:雙前置下劃線,私有化屬性或方法,無法在外部直接訪問(名字重整所以訪問不到)
  • xx:雙前后下劃線,系統定義名字(不要自己發明這樣的名字)
  • xx_:單后置下劃線,用于避免與Python關鍵詞的沖突

使用不同方法匯入模塊,模塊中私有變數的使用區別

在使用不同方法匯入模塊后,是否能使用模塊中的私有屬性和方法,有以下兩種情況

  • 在使用 from somemodule import * 匯入模塊的情況下,不能匯入或使用私有屬性和方法
  • 在使用 import somemodule 匯入模塊的情況下,能匯入并使用私有屬性和方法
第三方模塊
  • 在Python中,安裝第三方模塊,是通過包管理工具pip完成的,

  • 模塊搜索路徑

    當我們試圖加載一個模塊時,Python會在指定的路徑下搜索對應的.py檔案,如果找不到,就會報錯:

    >>> import mymodule
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ImportError: No module named mymodule
    
    

    默認情況下,Python解釋器會搜索當前目錄、所有已安裝的內置模塊和第三方模塊,搜索路徑存放在sys模塊的path變數中

    >>> import sys
    >>> sys.path
    ['', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', ..., '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']
    
    

    如果我們要添加自己的搜索目錄,有兩種方法:

    一是直接修改sys.path,添加要搜索的目錄:

    >>> import sys
    >>> sys.path.append('/Users/michael/my_py_scripts')
    
    

    這種方法是在運行時修改,運行結束后失效,

    第二種方法是設定環境變數PYTHONPATH,該環境變數的內容會被自動添加到模塊搜索路徑中,設定方式與設定Path環境變數類似,注意只需要添加你自己的搜索路徑,Python自己本身的搜索路徑不受影響,

29、類和實體

    • class Student(object):
      
          def __init__(self, name, score):
              self.name = name
              self.score = score
      
      

class后面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的,繼承的概念我們后面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類,

由于類可以起到模板的作用,因此,可以在創建實體的時候,把一些我們認為必須系結的屬性強制填寫進去,通過定義一個特殊的__init__方法,在創建實體的時候,就把namescore等屬性綁上去

注意到**__init__方法的第一個引數永遠是self,表示創建的實體本身,因此,在__init__方法內部,就可以把各種屬性系結到self,因為self就指向創建的實體本身,**

有了__init__方法,在創建實體的時候,就不能傳入空的引數了,必須傳入與__init__方法匹配的引數,但self不需要傳,Python解釋器自己會把實體變數傳進去

和普通的函式相比,在類中定義的函式只有一點不同,就是第一個引數永遠是實體變數self,并且,呼叫時,不用傳遞該引數,除此之外,類的方法和普通函式沒有什么區別,所以,你仍然可以用默認引數、可變引數、關鍵字引數和命名關鍵字引數,

可以自由地給一個實體變數系結屬性

class Student(object):
    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'


方法就是與實體系結的函式,和普通函式不同,方法可以直接訪問實體的資料;

stu=Student('zdc',29)
def min(a,b):
    if a>b:
        return b
    else:
        return a
print(stu)
stu.min=min
print(stu.min(1,2))
stu.zz='ssd'
print(stu.zz)

實體可以任意添加屬性和方法

30、訪問限制

class Student(object):
    def __init__(self,name,score):
        self.__name=name
        self.__score=score

stu=Student('zdc',29)

print(stu._Student__name)#可以訪問
print(stu.__name)

如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線__,在Python中,實體的變數名如果以__開頭,就變成了一個私有變數(private),只有內部可以訪問,外部不能訪問,所以,我們把Student類改一改:

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

改完后,對于外部代碼來說,沒什么變動,但是已經無法從外部訪問實體變數.__name實體變數.__score了:

>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'

這樣就確保了外部代碼不能隨意修改物件內部的狀態,這樣通過訪問限制的保護,代碼更加健壯,

但是如果外部代碼要獲取name和score怎么辦?可以給Student類增加get_nameget_score這樣的方法:

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

如果又要允許外部代碼修改score怎么辦?可以再給Student類增加set_score方法:

class Student(object):
    ...

    def set_score(self, score):
        self.__score = score

你也許會問,原先那種直接通過bart.score = 99也可以修改啊,為什么要定義一個方法大費周折?因為在方法中,可以對引數做檢查,避免傳入無效的引數:

class Student(object):
    ...

    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')

需要注意的是,在Python中,變數名類似__xxx__的,也就是以雙下劃線開頭,并且以雙下劃線結尾的,是特殊變數,特殊變數是可以直接訪問的,不是private變數,所以,不能用__name____score__這樣的變數名

有些時候,你會看到以一個下劃線開頭的實體變數名,比如_name,這樣的實體變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變數,不要隨意訪問”,

雙下劃線開頭的實體變數是不是一定不能從外部訪問呢?其實也不是,不能直接訪問__name是因為Python解釋器對外把__name變數改成了_Student__name,所以,仍然可以通過_Student__name來訪問__name變數

31、繼承和多型

  • class Animal(object):
        def run(self):
            print('Animal is running...')
    
    class Dog(Animal):
        pass
    
    class Cat(Animal):
        pass
    
    dog = Dog()
    dog.run()
    
    
    

    繼承的第二個好處需要我們對代碼做一點改進,你看到了,無論是Dog還是Cat,它們run()的時候,顯示的都是Animal is running...,符合邏輯的做法是分別顯示Dog is running...Cat is running...,因此,對DogCat類改進如下:

    class Dog(Animal):
    
        def run(self):
            print('Dog is running...')
    
    class Cat(Animal):
    
        def run(self):
            print('Cat is running...')
    
    

    再次運行,結果如下:

    Dog is running...
    Cat is running...
    
    

    當子類和父類都存在相同的run()方法時,我們說,子類的run()覆寫了父類的run(),在代碼運行的時候,總是會呼叫子類的run(),這樣,我們就獲得了繼承的另一個好處:多型,

    def run_twice(animal):
        animal.run()
    
    

32、獲取物件資訊

  • 基本型別都可以用type()判斷

    >>> type(123)
    <class 'int'>
    >>> type('str')
    <class 'str'>
    >>> type(None)
    <type(None) 'NoneType'>
    
    

    如果一個變數指向函式或者類,也可以用type()判斷:

    >>> type(abs)
    <class 'builtin_function_or_method'>
    >>> type(a)
    <class '__main__.Animal'>
    
    

    但是type()函式回傳的是什么型別呢?它回傳對應的Class型別,如果我們要在if陳述句中判斷,就需要比較兩個變數的type型別是否相同:

    >>> type(123)==type(456)
    True
    >>> type(123)==int
    True
    >>> type('abc')==type('123')
    True
    >>> type('abc')==str
    True
    >>> type('abc')==type(123)
    False
    
    

    判斷基本資料型別可以直接寫intstr等,但如果要判斷一個物件是否是函式怎么辦?可以使用types模塊中定義的常量:

    >>> import types
    >>> def fn():
    ...     pass
    ...
    >>> type(fn)==types.FunctionType
    True
    >>> type(abs)==types.BuiltinFunctionType
    True
    >>> type(lambda x: x)==types.LambdaType
    True
    >>> type((x for x in range(10)))==types.GeneratorType
    True
    
    
  • 使用isinstance()

    • 對于class的繼承關系來說,使用type()就很不方便,我們要判斷class的型別,可以使用isinstance()函式,

    • isinstance()就可以告訴我們,一個物件是否是某種型別,先創建3種型別的物件:
      
      >>> a = Animal()
      >>> d = Dog()
      >>> h = Husky()
      然后,判斷:
      
      >>> isinstance(h, Husky)
      True
      >>> isinstance(h, Dog)
      True    #但由于Husky是從Dog繼承下來的,所以,h也還是Dog型別
      
      
  • dir()

    • 如果要獲得一個物件的所有屬性和方法,可以使用dir()函式,它回傳一個包含字串的list,比如,獲得一個str物件的所有屬性和方法:

      >>> dir('ABC')
      ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
      
      

      僅僅把屬性和方法列出來是不夠的,配合**getattr()setattr()以及hasattr()**,我們可以直接操作一個物件的狀態:

      >>> hasattr(obj, 'x') # 有屬性'x'嗎?
      True
      >>> obj.x
      9
      >>> hasattr(obj, 'y') # 有屬性'y'嗎?
      False
      >>> setattr(obj, 'y', 19) # 設定一個屬性'y'
      >>> hasattr(obj, 'y') # 有屬性'y'嗎?
      True
      >>> getattr(obj, 'y') # 獲取屬性'y'
      19
      >>> obj.y # 獲取屬性'y'
      19
      
      

      如果試圖獲取不存在的屬性,會拋出AttributeError的錯誤:

      >>> getattr(obj, 'z') # 獲取屬性'z'
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'MyObject' object has no attribute 'z'
      
      

      可以傳入一個default引數,如果屬性不存在,就回傳默認值:

      >>> getattr(obj, 'z', 404) # 獲取屬性'z',如果不存在,回傳默認值404
      404
      
      

      也可以獲得物件的方法:

      >>> hasattr(obj, 'power') # 有屬性'power'嗎?
      True
      >>> getattr(obj, 'power') # 獲取屬性'power'
      <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
      >>> fn = getattr(obj, 'power') # 獲取屬性'power'并賦值到變數fn
      >>> fn # fn指向obj.power
      <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
      >>> fn() # 呼叫fn()與呼叫obj.power()是一樣的
      81
      
      

33、實體屬性和類屬性

  • class Demo:
        count=100; #類變數 
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
    
    
    d=Demo('zz',22)   
    d2=Demo('dd',11)
    d.count=222    # 給實體系結count屬性  類的count被隱藏了
    print(d2.count)  #100   因為實體并沒有count屬性,所以會繼續查找class的count屬性
    print(d.count)  #222
    print(Demo.count) #100
    del d.count  #洗掉d的實體屬性  此時可以訪問到類屬性
    print(d.count)
    
    

    在撰寫程式的時候,千萬不要對實體屬性和類屬性使用相同的名字,因為相同名稱的實體屬性將屏蔽掉類屬性,但是當你洗掉實體屬性后,再使用相同的名稱,訪問到的將是類屬性

33、__slots__限制實體系結任何屬性和方法

  • 常情況下,當我們定義了一個class,創建了一個class的實體后,我們可以給該實體系結任何屬性和方法,這就是動態語言的靈活性,但是,給一個實體系結的方法,對另一個實體是不起作用的

  • 但是,如果我們想要限制實體的屬性怎么辦?比如,只允許對Student實體添加nameage屬性,

    為了達到限制的目的,Python允許在定義class的時候,定義一個特殊的__slots__變數,來限制該class實體能添加的屬性:

    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定義允許系結的屬性名稱
        
    >>> s.score = 99 # 系結屬性'score'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'score'
    
    

    除非在子類中也定義__slots__,這樣,子類實體允許定義的屬性就是自身的__slots__加上父類的__slots__

  • len

    • __len__
      如果一個類表現得像一個list,要獲取有多少個元素,就得用 len() 函式,
      
      要讓 len() 函式作業正常,類必須提供一個特殊方法__len__(),它回傳元素的個數,
      
      例如,我們寫一個 Students 類,把名字傳進去:
      
      class Students(object):
          def __init__(self, *args):
              self.names = args
          def __len__(self):
              return len(self.names)
      只要正確實作了__len__()方法,就可以用len()函式回傳Students實體的“長度”:
      
      

34、@property @xxx.setter

  • class Person(object):
        @property
        def birth(self):
            return self._birth
    
        @birth.setter
        def birth(self, value):
            self._birth = value
    
        @property
        def age(self):
            return 2015 - self._birth
    
    
    p = Person()
    p.birth = 100
    # p.age=11  #age為只讀
    print(p.birth)
    print(p.age)
    
    

    把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又創建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值

35、多繼承

  • Python允許使用多重繼承

36、定制類

  • str

    我們先定義一個Student類,列印一個實體:

    >>> class Student(object):
    ...     def __init__(self, name):
    ...         self.name = name
    ...
    >>> print(Student('Michael'))
    <__main__.Student object at 0x109afb190>
    
    

    列印出一堆<__main__.Student object at 0x109afb190>,不好看,

    怎么才能列印得好看呢?只需要定義好__str__()方法,回傳一個好看的字串就可以了:

    >>> class Student(object):
    ...     def __init__(self, name):
    ...         self.name = name
    ...     def __str__(self):
    ...         return 'Student object (name: %s)' % self.name
    ...
    >>> print(Student('Michael'))
    Student object (name: Michael)
    
    

    這樣列印出來的實體,不但好看,而且容易看出實體內部重要的資料,

怎么說呢,python 的特性太膠水了,不適合作為專案主力,所以它是生產力技能而不是業務技能,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258434.html

標籤:python

上一篇:Python之turtle庫畫各種有趣的圖及原始碼(更新中)

下一篇:python股票量化交易(13)---使用pyqt5構建股票交易K線形態

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more