【學習筆記】python

Curryxin發表於2022-03-31

基礎

  • print()每執行一次預設在最後加上了\n,也就是預設會換行;
  • break可以立即退出迴圈語句,包括else;pass是用來在迴圈或者判斷中進行佔位的(比如暫時沒想好功能該怎麼寫,就先佔位);
- == != 比較的是物件的值是否相等;
- is  is not 比較的是物件的id是否相等(也就是比較是否是同一個物件)

序列

序列就是用來儲存一組有序的資料,所有的資料在序列中都有唯一的索引;

  • 可變序列:列表(list);
  • 不可變序列:字串(str),元祖(tuple);
# 遍歷序列
for s in stus:
    print(s)     # 每執行一次將序列中的元素賦給變數s
    
range(3,0,-1) #[3,2,1],都是左閉右開;range就是生成一個自然數的序列;
for i in range(30):
    程式碼塊    #迴圈30次

可變物件

每個物件都儲存了三個資料:

  • id(標識/地址)
  • type(型別)
  • value(值)

列表就是可變物件,可變物件是說其值(value)是可變的;

a[0] = 10
# 這個是在改物件,不會改變變數的指向,只是指向的變數的值變了;
a = [4,5,6]
# 這個是在改變數,會改變變數指向的物件;

列表

  • 列表就是儲存物件的物件;
#列表的切片;
stus[-1]  #從倒數第一個開始;
stus[0:2] #第一個元素和第二個元素;左閉右開;
stus[0:5:-1]  #[起始:結束:步長],if步長是負數表示從後開始;步長不能是0;
s.index['a',1,3]   #獲取a在列表中在[1,3)中首次出現的索引;
s.count['a']   #獲取a在列表中出現的次數;  這兩個都是方法,必須通過xxx.method()來呼叫;

# 列表的替換
# 在給切片進行賦值時,只能使用序列
stus[0:2] = [1,2,3] # 序列的個數可以和切片的個數不一致;
stus[::2] = [1,2]  #當設定了步長時,兩者個數必須相同;
  • 列表的方法
s.append(1)   # 向列表最後新增元素;
s.insert(2,'a') #向列表的2索引處插入a;
s.extend([1,2,3])  #引數必須是序列,擴充套件s序列;
s.pop(2)   #刪除索引為2的元素,有返回值;
s.remove(2) #刪除值為2的元素,if有多個值,只刪第一個;
s.clear()  #清空序列;
s.reverse() #反轉列表;
s.sort()  #對列表排序,預設是升序,可以傳遞引數reverse=true來變成降序;

元組(tuple)

  • 元祖就是不可變的列表
t = ()  # 建立空元組;
t = 1,2,3,4  # 當元組不為空時,括號可以不寫;
t = 1,  # if元組不為空,至少要有一個逗號;
a,b,c,d = t # 元組的解包,這時候a=1,b=2;
# 所以if想要交換兩個元素,其實可以直接
a,b = b,a  # b,a就是一個元組,進行了解包;
# 在對一個元組進行解包時,變數的數量必須和元組中的元素的數量一致
# 也可以在變數前邊新增一個*,這樣變數將會獲取元組中所有剩餘的元素
a , b , *c = t # c = [3,4];
# 解包是對所有序列都可以的,不僅是元組;

字典(dict)

  • 列表儲存資料很好,但是查詢的效能很差;而字典從查詢效率很高;
  • 字典中每個物件都有一個名字(key),而每個物件就是value;這樣一個鍵值對稱為一項(item);
  • 字典的值可以是任意物件;字典的鍵可以是任意的不可變物件(int、str、bool、tuple ...),但是一般我們都會使用str
d = {}   #空字典
d = {'name':a, 'age':18}
print(d['name]')   # a;注意name一定要加引號;
----------------------
# 使用 dict()函式來建立字典
# 每一個引數都是一個鍵值對,引數名就是鍵,引數名就是值(這種方式建立的字典,key都是字串)
d = dict(name='孫悟空',age=18,gender='男') 
len(d)  #獲取字典中鍵值對的個數;
in/not in   #字典中是否有指定的鍵;
-----------------------
d['name']   #獲取字典的值;
d.get('hello',default)  # if hello不存在,會返回預設值;
-----------------------
d['name'] = 'sunwukong' # 修改字典的key-value
setdefault(key, default) 可以用來向字典中新增key-value
#   如果key已經存在於字典中,則返回key的值,不會對字典做任何操作
#   如果key不存在,則向字典中新增這個key,並設定value
d = {'a':1,'b':2,'c':3}
d2 = {'d':4,'e':5,'f':6, 'a':7}
d.update(d2)  #將d2中的鍵值對新增到d,if 有相同的key會進行替代;
-----------------------
del d['name']  # 刪除鍵值對;
result = d.pop('name') #孫悟空,返回value;
d.clear()  # 清空字典;
-----------------------
d1 = d2  # 兩個指向的是同一個物件,修改一個另一個也會變;
d1 = d2.copy # 這是複製品,是相互獨立;

注意,淺複製會簡單複製物件內部的值,如果值也是一個可變物件,這個可變物件不會被複制
d = {'a':{'name':'孫悟空','age':18},'b':2,'c':3}
d2 = d.copy()
d2['a']['name'] = '豬八戒'  # d和d2都會變成豬八戒;
-----------------------
for k in d.keys():
    print(d[k])    #通過keys()遍歷;
for v in d.values():
    print(v)       #通過values()遍歷:
for k,v in d.items():
    print(k, '=', v)  #items()會返回字典中所有的項;   
# 會返回一個序列,序列中包含雙值子序列,[('name',a),('age',18)],這時候賦值其實是解包;

集合(set)

  • 集合和列表非常相似
  • 不同點:
    1.集合中只能儲存不可變物件 ( s= {[1,2,3]},這就是錯誤的,因為列表是可變的)
    2.集合中儲存的物件是無序(不是按照元素的插入順序儲存)
    3.集合中不能出現重複的元素
s = {1,2,3}   #建立集合;
s = set([1,2,1]) # set可以將列表和字典變為集合,字典轉的時候,只有鍵;
len(s)  # 元素數量;
in/not in 
-----------------------
s.add(4)  # 增加元素;
s1.upadte(s2) # 將s2中的元素新增到s1中;
-----------------------
s.pop() # 隨機刪除集合中的一個元素;
s.remove # 刪除集合中的指定元素;
s.clear()
-----------------------
s1 & s2 # 集合的交集;
s1 | s2 # 集合的並集;
s1 - s2 # 集合的差集;
s1 ^ s2 # 集合的異或集:獲取只在一個集合中出現的元素;
s1 <= s2 # s1 是否是s2的子集
s1 < s2 # s1 是否是s2的真子集

函式

  • 函式也是物件;函式可以用來儲存一些可執行的程式碼,並且可以在需要時,對這些語句進行多次的呼叫
def fn(a,b):
    程式碼塊    #fn是函式物件,fn()呼叫函式;
# 函式在呼叫時,解析器不會檢查實參的型別;實參可以傳遞任意型別;比如列表甚至是函式都行(fn(fn1));
def fn1(*a,b):
    程式碼塊  #可變引數,會將所有的實參儲存到一個元組中(裝包)
    # 可變引數不是必須寫在最後,但是注意,帶*的引數後的所有引數,必須以關鍵字引數的形式傳遞

# *形參不能接收關鍵字引數;
# **形參可以接收關鍵字引數,會將這些引數統一儲存到一個字典中;**引數必須在所有引數的最後;
----------------------------
t = (1,2)
fn(*t)     
#傳遞實參時,可以在序列型別的引數前新增星號,這樣他會自動將序列中的元素依次作為引數傳遞(解包),序列的個數必須和引數個數相同;
# 同樣,也可以用**來對字典進行解包,字典的key和引數形參名一樣;
----------------------------
return 後面可以跟任意的值,甚至可以是函式;

# 文件字串
def fn(a:int,b:bool,c:str='hello') -> int:
    #這些裡面的型別沒有任何意義,僅僅是指示說明;
    ···
    可以寫一些說明文字
    ···
----------------------------
if 想在函式內部修改全域性變數的值,要加關鍵字 global;
scope = locals()  # locals() 獲取當前作用域的名稱空間;   
# 名稱空間其實就是一個字典,是一個專門用來儲存變數的字典;所以scope是一個字典,這個字典就是當前作用域中所有的變數;   
scope['c'] = 1000  # 向字典中新增鍵值對就相當於在全域性中建立了一個變數c;   
globals_scope = globals()  #globals()這個函式可以在任意位置獲取全域性的名稱空間;

高階函式

  • 將函式作為引數,或者將函式作為返回值的函式是高階函式;當將一個函式作為引數時,實際上是將指定的程式碼傳遞給了目標函式;
  • 這樣的會就可以定製一些函式,然後將函式作為一種“規則”傳遞給目標函式,然後目標函式根據這種“規則”對引數(原料)做出相應的處理;

1.將函式作為引數傳遞:

filter()

filter()可以從序列中過濾出符合條件的元素,儲存到一個新的序列中

  • 引數:
    1.函式,根據該函式來過濾序列(可迭代的結構)
    2.需要過濾的序列(可迭代的結構)
  • 返回值:
    過濾後的新序列(可迭代的結構)
匿名函式(lambda函式表示式)

有時候一個函式用一次就再也不用了,就可以用lambda表示式;匿名韓式一般都是作為引數使用的;
語法:lambda 引數列表 : 返回值

r = filter(lambda i : i > 5 , l)

2.將函式作為返回值

閉包
  • 將函式作為返回值返回,這就是一種閉包,通過閉包可以建立一些只有當前函式能訪問的變數,可以將一些私有的資料藏到閉包裡;
形成閉包的條件:
1.函式巢狀;
2.將內部函式作為返回值返回;
3.內部函式必須要使用到外部函式的變數;
def fn():

    a = 10

    # 函式內部再定義一個函式
    def inner():
        print('我是fn2' , a)

    # 將內部函式 inner作為返回值返回   
    return inner

# r是一個函式,是呼叫fn()後返回的函式
# 這個函式實在fn()內部定義,並不是全域性函式
# 所以這個函式總是能訪問到fn()函式內的變數
r = fn()
變數是裡面能看到外面的,但是外面看不到裡面的;這樣閉包就可以了;

裝飾器

在寫程式的時候,if我們想擴充套件一個函式,但是我們要儘量不去動原始的函式,而是要遵從開閉原則,即開放對程式的擴充套件,而關閉對程式的修改;

裝飾器其實就是一個函式,這個函式什麼功能呢?它接收一箇舊函式作為引數,然後在裝飾器裡對它進行裝飾包裝,然後以一個新函式作為返回;這樣就可以在不修改原始函式的情況下對函式進行擴充套件;

可以直接在舊函式上宣告@裝飾器函式
可以給函式多個裝飾器,裝飾的時候從內向外;

物件

  • 類用大駝峰來命名:MyClass; 類也是物件,類就是一個用來建立物件的物件,一切皆物件!

  • 如果是函式呼叫,則呼叫時傳幾個引數,就會有幾個實參;但是如果是方法呼叫,預設傳遞一個引數,這個實參是解析器自動傳的,所以方法中至少要定義一個形參(self),這個預設傳遞的實參其實就是呼叫方法的物件本身!if是p1調的,那第一個引數就是p1,if是p2調的,那第一個引數就是p2;所以我們把這個引數命名為self,這個self就是指的是誰呼叫這個方法了,這個誰就是self;self就是當前物件

  • 當我們呼叫一個物件的屬性時,解析器會先在當前物件中尋找是否含有該屬性,
    如果有,則直接返回當前的物件的屬性值,
    如果沒有,則去當前物件的類物件中去尋找,如果有則返回類物件的屬性值,如果類物件中依然沒有,則報錯!

  • 類物件和例項物件中都可以儲存屬性(方法)
    - 如果這個屬性(方法)是所有的例項共享的,則應該將其儲存到類物件中
    - 如果這個屬性(方法)是某個例項獨有,則應該儲存到例項物件中

  • 在python中,xxx 稱為特殊方法,或者稱為魔術方法;這種方法不需要我們自己呼叫,會在一些時刻自動呼叫;

p1 = Person()的執行流程
  1.建立一個變數p1
  2.在記憶體中建立一個新變數,這個變數的型別是Person
  3.__init__(self)方法執行
  4.將物件的id(地址)賦值給變數
  • 使用__開頭的屬性,實際上依然可以在外部訪問,所以這種方式我們一般不用,(因為其實就是python將__name自己改名成了_類名__屬性)
    一般我們會將一些私有屬性(不希望被外部訪問的屬性)以_開頭,一般情況下,使用_開頭的屬性都是私有屬性,沒有特殊需要不要修改私有屬性

  • 繼承,在類的括號裡寫父類,if沒寫,就是繼承自object.

issubclass(a,b)  檢查a是否是b的子類
isinstance(a,A)  檢查a是否是A的例項

父類中所有的方法和屬性都可以被繼承,包括特殊方法__init__,可以重寫初始化方法,if想要增添多個屬性,那可以呼叫super()來呼叫當前類的父類,然後再增添自己特有的就行了


# 父類中的所有方法都會被子類繼承,包括特殊方法,也可以重寫特殊方法
class Dog(Animal):

    def __init__(self,name,age):
        # 希望可以直接呼叫父類的__init__來初始化父類中定義的屬性
        # super() 可以用來獲取當前類的父類,
        #   並且通過super()返回物件呼叫父類方法時,不需要傳遞self
        super().__init__(name)
        self._age = age

python中可以多重繼承:

class C(A,B):
    pass  
# C繼承自兩個父類,AB,ifA,B中有同名的方法,會先呼叫A的,
  • 多型:在函式的引數中,if要傳入一個物件,其實不關注物件是什麼型別的,只關注這個物件是否有某些屬性和方法,只要有某些屬性和方法,那就可以傳遞進去,這樣保證了程式的靈活性;
  • 類屬性可以通過A.屬性和a.屬性訪問,例項屬性只能通過a.屬性訪問,類.屬性無法訪問;在類中以self作為第一個引數的方法是例項方法,例項方法可以通過類和例項呼叫,通過例項呼叫時,會自動將當前物件傳遞給self,但是通過類呼叫時,不會自動傳,所有,A.fangfa(a) = a.fangfa()
    # 類方法    
    # 在類內部使用 @classmethod 來修飾的方法屬於類方法
    # 類方法的第一個引數是cls,也會被自動傳遞,cls就是當前的類物件
    #   類方法和例項方法的區別,例項方法的第一個引數是self,而類方法的第一個引數是cls
    #   類方法可以通過類去呼叫,也可以通過例項呼叫,沒有區別
    @classmethod
    def test_2(cls):
        print('這是test_2方法,他是一個類方法~~~ ',cls)
        print(cls.count)
        
    # 靜態方法
    # 在類中使用 @staticmethod 來修飾的方法屬於靜態方法  
    # 靜態方法不需要指定任何的預設引數,靜態方法可以通過類和例項去呼叫  
    # 靜態方法,基本上是一個和當前類無關的方法,它只是一個儲存到當前類中的函式
    # 靜態方法一般都是一些工具方法,和當前類無關
    @staticmethod
    def test_3():
        print('test_3執行了~~~')
  • 特殊方法也叫魔法方法,一般不需要手動呼叫,在一些特殊的時候會自動執行,以__開始,比如__init__在物件建立時呼叫,__del__會在結束時呼叫,進行垃圾回收

  • 一個py檔案就是一個模組,模組也是一個物件!在每一個模組內部都有一個__name__屬性,通過這個屬性可以獲取到模組的名字__name__屬性值為 __main__的模組是主模組,一個程式中只會有一個主模組,主模組就是我們直接通過 python 執行的模組

  • 一個資料夾就是一個包,一個包裡可以有多個模組,包中必須含有一個__init__.py檔案,一個包裡會有__pycache__資料夾,這是模組的快取檔案, py程式碼在執行前,需要被解析器先轉換為機器碼,然後再執行,所以我們在使用模組(包)時,也需要將模組的程式碼先轉換為機器碼然後再交由計算機執行
    而為了提高程式執行的效能,python會在編譯過一次以後,將程式碼儲存到一個快取檔案中,這樣在下次載入這個模組(包)時,就可以不再重新編譯而是直接載入快取中編譯好的程式碼即可

異常

    try:
        程式碼塊(可能出現錯誤的語句)
    except 異常型別 as 異常名:
        程式碼塊(出現錯誤以後的處理方式)
    else:
        程式碼塊(沒出錯時要執行的語句))
    finally:
        程式碼塊(該程式碼塊總會執行) 
  • 異常也是一個物件,比如 : ZeroDivisionError類的物件專門用來表示除0的異常
    NameError類的物件專門用來處理變數錯誤的異常
  • Exception 是所有異常類的父類,所以如果except後跟的是Exception,他也會捕獲到所有的異常;可以在異常類後邊跟著一個 as xx 此時xx就是異常物件
  • 可以使用 raise 語句來丟擲異常,
    raise語句後需要跟一個異常類 或 異常的例項

檔案

  • 開啟檔案,opne(file_name)會返回一個物件,if當前檔案和目標檔案在同一級目錄下,則直接寫名字就可以,其他的時候就必須用路徑了。可以使用..來向上返回一級目錄
  • read()用來讀,會將內容儲存為一個字串返回;
  • 關閉檔案;物件.close(),這時候其實經常是忘記的,所以有了with..as語句
with open(file_name) as file_obj:   #這其實就是和file_obj = open(file_name)一樣,當出去這個with後,會自動關閉;
#開啟file_name並返回一個變數file_obj
    file_obj.read()   
with open(file_name , 'x' , encoding='utf-8') as file_obj:
# 使用open()開啟檔案時必須要指定開啟檔案所要做的操作(讀、寫、追加)
# 如果不指定操作型別,則預設是 讀取檔案 , 而讀取檔案時是不能向檔案中寫入的
# r 表示只讀的
# w 表示是可寫的,使用w來寫入檔案時,如果檔案不存在會建立檔案,如果檔案存在則會截斷檔案
#   截斷檔案指刪除原來檔案中的所有內容
# a 表示追加內容,如果檔案不存在會建立檔案,如果檔案存在則會向檔案中追加內容
# x 用來新建檔案,如果檔案不存在則建立,存在則報錯
# + 為操作符增加功能
#   r+ 即可讀又可寫,檔案不存在會報錯
# seek() 可以修改當前讀取的位置
    seek()需要兩個引數
    第一個 是要切換到的位置
         第二個 計算位置方式
            可選值:
             0 從頭計算,預設值
             1 從當前位置計算
             2 從最後位置開始計算
# tell() 方法用來檢視當前讀取的位置


相關文章