python學習筆記

if-else簡化版:
表達式1  if 表達式  else 表達式2
y = 5
x = "大於0" if y > 0 else "小於0"
print(x)
輸入數值,判斷是工作日還是休息日,或者輸入錯誤。
day = int(input("請輸入數值:"))
content = "工作日" if 1 <= day <= 5 else ("休息日" if day == 6 or day == 7 else "輸入錯誤")

# 斷言的使用assert
# 斷言的語法:
# assert 表達式
# assert 表達式  信息
# 當表達式爲True,則流程繼續進行,否則會產生錯誤。
# 當指定信息時,如果斷言的表達式爲False,則信息也會顯示。

# 斷言與if的不同之處
# 1斷言應該使用在程序的關鍵位置。if往往使用在不是特別關鍵的位置。
# 2斷言可以通過運行時參數-O,來靈活的開啓或者關閉(默認是開啓的)。但是,if總是無條件的執行。

password = input("請輸入密碼:")
password2 = input("請再次輸入密碼:")
# 此處設下斷言,僅當兩次密碼一致時,纔有必要進行後續的註冊工作。
# assert password == password2
assert password == password2, "兩次密碼輸入的不一致"

# range函數  能夠產生一個區間的整數值(可迭代對象)。
# range(end)    產生0 ~ end區間的整數值。包括0,不包括end。[0, end)
# range(start, end) 產生start ~ end 區間的整數值。包括0,不包括end。[start, end)
# range(start, end, step) step指定步長值(增量值)。step默認爲1。

 

#列表的索引也支持負值。-1, -2,表示倒數第幾個元素。最後一個元素爲-1。
#print(li[-1], li[-2])
# 列表中的元素可以不是同一個類型。
li2 = [1, 2.0, False, "abc"]
# 輸出整個列表。
print(li2)
# 通過for循環遍歷列表,比while循環更加簡潔。
for item in li:
    print(item)
# 列表的嵌套:列表中的元素,還是一個列表。
nest = [[1, 2, 3], [4, 5, 6]]
for item in nest:
    for i in item:
        print(i)
# # 列表(序列)的切片操作
# # 語法:列表名[start:end:step]
# # start指定切片起始點,end指定切片的終止點,step指定步長值,默認爲1。(切片區間包含起始點,不包含終止點)
# # start,end,step都是可選的。
li = [5, 3, 8, 6, 10, 9, -2]
print(li[0:3])
# # 步長指定增量值。
print(li[0:6:2])
# # start, end, step也可以是負值。
# # 如果切片的方向與增量的方向相反,則無法獲取任何元素。(得到空列表[])
print(li[-1:-4])
print(li[-1:-4:-1])
print(li[-4:-1])
# # 省略start或end
# # 如果缺失起始點或終止點(或者兩個都缺失),則此時切片沒有方向。具體沿着哪個方向切取,取決於
# # 增量的方向。
print(li[:4])
print(li[4:])
print(li[:4:-1])
print(li[4::-1])
print(li[::1])
# # 通過切片實現對原列表的逆序。
print(li[::-1])
#
# #通過切片修改對應區間的元素。
# # 因爲列表切片返回的是列表類型,因此,賦值時,需要指定一個列表
print(type(li[2:5]))
li[2:5] = [100, 101, 102, 103]  # 相當於替換操作。
# # 相當於刪除列表中指定區間的元素。
li[2:5] = []
# # 相當於插入元素。
li[2:2] = [32, 50]
print(li)
#
# # 對於索引,如果越界,將會產生錯誤。
# # print(li[len(li)])
# # 對於切片,如果越界,不會產生錯誤。
# print(li[-len(li)-1: 3])
# print(li[:len(li) + 100])
# print(li[-10000:10000])
# list(列表)的copy方法實現的是淺拷貝。淺拷貝僅僅拷貝當前的對象,對於對象內部所關聯的其他對象,
# 則不會進行拷貝。
# 列表中元素是可變類型,此時,淺拷貝無法實現期望的效果。如果期望li2的改變不會影響到li,我們可以實現
# 深拷貝操作。深拷貝就是不僅拷貝當前對象本身,當前對象內部所引用的可變對象,依然會實現遞歸的拷貝。
# id函數,返回對象的唯一標識。不同的對象,id函數一定會返回不同的值。
# 在CPython中,id函數返回的是對象的地址。

 

# 元組使用()來定義。
t = (1, 2, 3)
print(type(t))
# 當只有一個元素時,元素後面的逗號(,)不能省略。
# 否則()會被解析爲調節運算符優先級的(),而不會解析爲元組類型。
# 定義元組時,也可以不使用小括號,只要使用逗號分隔即可。
k = 1,
k = (1, 2)
s = (1, 2)
print(k is k)
print(k is s)
print(k == s)
# 將當前字符串中,第1個參數指定的內容,使用第2個參數進行替換。
# print(s.replace("cd", "xy"))
# replace也可以指定第3個參數,表示最多替換的次數。
# print(s.replace("cd", "xy", 3))
# 用來拼接可迭代對象中的所有元素,使用當前str對象作爲分隔符。
li = ["a", "bc", "d", "ef"]
print("-".join(li))
# 去除字符串兩端的空白符。
print("\t\t\t    abcc\t\t\t".strip())
# strip方法也可以指定我們需要去掉的字符。
# strip方法參數指定的是單個字符,並不是作爲一個整體。即strip兩端的字符只要在參數指定的內容當中,則去除。
print("xyzxyzxyxy".strip("yx"))
# 以空白符進行切割,返回切割後元素組成的列表。
print("ab cd ef gh".split())
# split也可以指定切割的字符。
print("abcdefcabckk".split("c"))
當迭代的對象迭代完併爲空時,位於else的子句將執行,而如果在for循環中含有break時則直接終止循環,並不會執行else子句:
s = "01234a58789"
for item in s:
    if 48 <= ord(item) <= 57:
        continue
    else:
        break
else:

 

字典:
# 字典的鍵與值之間使用:進行分隔。
# 鍵值對之間使用,進行分隔。
x = {1: 200, 2: 300, 3: 400}
# 字典中的鍵是不能重複的。
# 字典中的鍵是無序的。(不保證任何順序)
# get方法與x[key]的區別:二者都能夠獲取鍵所綁定的值,不同之處在於:
# 前者在鍵不存在時,不會產生錯誤,返回None,而後者會產生錯誤。
print(x.get(1))
print(x[1])
# pop:刪除與參數指定鍵相同的鍵值對。同時,返回該鍵所綁定的值。
# 如果指定的鍵不存在,則會產生錯誤。
# print(x.pop(1))
# pop可以指定第2個參數(默認值)。如果第1個參數指定的key不存在,則不會
# 產生錯誤,而是返回第2個參數。
# print(x.pop(4, 2))
# print(x)
# 返回字典中所有的鍵key。
# print(x.keys())
# 返回字典中所有的值value。
# print(x.values())
# 返回字典中所有的鍵值對。每個鍵值對爲一個元組。(索引0爲key,索引1爲值。
# print(x.items())
# 刪除字典中所有的鍵值對。
# x.clear()
# 使用參數指定的字典,更新當前的字典。
# 相當於參數y中的每一個k與v,執行x[k] = v。
y = {3:89, 4:2323, 5:"csdf"}
x.update(y)
# 字典的遍歷
for k in x.keys():
    print(k, x[k])
# for t in x.items():
#     #print(t)
for a, b in x.items():
    print(a, b)
集合:
# 如果想要創建空的集合。使用set函數。
x = set()
# 集合的特徵:
# 1 集合中不含有重複元素。
x = {1, 2, 3, 1, 2, 3}
print(x)
# len也可以應用與字典與集合類型。
# 對於字典類型,返回鍵值對的數量。對於集合類型,返回集合中元素的數量。
print(len(x))
print(len(d))
# 2 集合中的元素不保證任何順序。(無序)
# 3 集合中元素的類型必須是可哈希類型。
# x = {[1, 2, 3], 4}
# print(x)

# 集合與字典具有很多共同的特徵,這些不是偶然的。因爲,集合底層就是使用字典來實現的。
# 集合中的元素就會成爲字典的key,然後綁定一個固定的值。
x = {1, 2, 3}
# x = {1:None, 2:None, 3:None}
# 在Python中,任何類型都可以轉換成布爾類型。

格式化:
1.舊格式
# # %d 將顯示的值轉換成有符號十進制數字
# print("我在%d年,我%d歲。" % (year, age))
# 將顯示的值使用str函數轉換成字符串。
print("%s" % "30")
# 使用關鍵字
x = {"name": "xy", "age": 22}
# print("姓名:%s,年齡:%d" % ("xy", 22))
# 指定字典中的鍵,提取該鍵在字典中對應的值。
print("姓名:%(name)s,年齡:%(age)d" %x)
2.新格式:使用str的format方法進行格式化
{[字段名][轉化類型]【格式說明】}
使用{}作爲佔位符
print("{1}-{0}".format("後面的","前面的"))
指定關鍵字,則按照後面同名的關鍵字參數的值進行替換。
print("{back}-{front}".format(back="ba",front="fr"))
使用數字(關鍵字)的索引進行替換
li=[1,2,3]
print("0[1]".format(li))
格式說明:
[[填充符]對齊方式][符號][#][0][最小寬度][分組選項][.精度][轉換類型]
print("{*>10}".format(-123))
3.最新格式化  {[字段名][!轉換類型][:格式說明]}
格式化字符串常量,使用f或F前綴
x=200
print(f"{x}")

 

 

# 函數的概念:
# 函數是具有一定功能的代碼片段(語句體),該代碼片段能夠重複的執行。
# 函數的定義,使用def關鍵字。
# def 函數名(參數列表):
#       函數體
只執行到return,return 後面的不執行
# 函數返回多個值,不能使用多個return,因爲函數遇到return就會終止執行,後面的return無法返回值。
# 我們可以使用元組來返回多個值。(儘管列表也可以,但是元組更加合適,因爲元組不能修改。)
def compute(a, b):
    # return a + b
    # return a - b
    # return a * b
    # return a / b
    # return [a + b, a - b, a * b, a / b]
    return (a + b, a - b, a * b, a / b)

# print(compute(1, 2))
#li = compute(1, 2)
#print(li[1])

# 如果一個參數含有默認值,則該參數就成爲一個可選參數(可有可無)。
# 如果我們顯式傳遞了該參數,則該參數的值就是我們傳遞的值。如果我們沒有顯式
# 傳遞該參數,則該參數取默認值。
def print_star(line, char="*"):
    for i in range(line):
        print(char * line)
# # 含有默認值參數位置的要求:
# # 在定義函數時,含有默認值的參數一定要定義在沒有默認值參數的後面。

# 位置參數必須位於關鍵字參數之前。
 關鍵字可變參數,在參數前面加上**。
# 關鍵字可變參數,可以用來接收任意數量的關鍵字參數。
# 在函數調用過程中,會執行打包操作。關鍵字參數會打包成字典類型。
def p(**k):
    # print(type(kw))
    print(k)

p()
p(a=2, b="asdf", c=[1,2,3,4])
 # 位置可變參數,在參數前面加上*。
# # 位置可變參數可以用來接收任意數量的位置參數。
# # 在函數調用過程中,會執行打包操作。位置可變參數是一個元組類型。
# def s(*a):
#     # print(type(a))
#     sum = 0
#     for item in a:
#         sum += item ** 2
#     return sum
#
#
# print(s())
# print(s(1))
# print(s(1, 2))
# print(s(1, 2, 3, 4, 5))
#位置可變參數與關鍵字可變參數如果存在,則最多隻能定義一個。
def test4(*a, *b, **kw, **kw2):
    pass
test4(1, 2, 3, 4)
# # 在可變位置參數後,定義的位置參數,將會自動的成爲關鍵字參數。
# def test3(*a,  b):
#     pass
#
# test3(1, 2, 3, 4, 5, b=3)
# 可變參數用來接收的是未匹配的參數,如果參數已經匹配,則不會由可變參數接收。
# def test(a, *b):
#     print(a)
#     print(b)
#
# test(1, 2, 3)



# #參數傳遞
# def fun(a):
#     a = 100
#
# b = 20
# fun(b)
# print(b)


def fun2(a):
    a.append(100)

li = [1, 2, 3]
fun2(li)
print(li)

def fun3(a):
    a = []

li = [1, 2, 3]
fun3(li)
print(li)

# 結論:通過參數傳遞,我們可以使形參與實參綁定相同的對象,進而,通過形參可以改變實參所綁定對象的數據。
# 但是,不能通過改變形參所綁定的對象,進而影響實參(實參所綁定的對象不會進行更改。即在函數調用之前,實參
#綁定哪個對象,在函數調用之後,實參依然會綁定原來的那個對象。

 

__init__方法:
既然__init__方法具有在創建對象時自動執行的特徵,我們就可以在該方法中執行對象的初始化,定義屬性就是一件最常用的操作。而__init__方法具有初始化的能力,我們也習慣將其稱爲構造器或構造方法。
與類的對象相關聯的,這種屬性稱爲實例屬性。對於對象,也稱爲實例,例如,創建類的對象,也可以稱爲,創建類的實例。
類屬性既可以通過類訪問,也可以通過對象訪問(類屬性會共享給所有對象)。
當讀取屬性值時,首先嚐試訪問同名的實例屬性,如果實例屬性不存在,則會訪問類屬性(如果類屬性也不存在則會產生錯誤)。
當修改屬性值時,如果實例屬性存在,則修改,如果實例屬性不存在,則新增該實例屬性,即通過對象修改屬性時,操作的屬性一定是實例屬性。
對於面向過程而言,體現的是一種流程設計,即按部就班的完成每一個環節。爲了實現每個環節的可複用性,環節都是通過調用一個個函數來實現的。因此,在面向過程的編程中,通常都是定義大量的函數,按照需求順序依次進行調用。
面向對象與面向過程:
    而面向對象程序設計則不同,在面向對象中,會根據需求,劃分爲若干個類,每個類會創建對象,以對象的方式來進行交互。所以,在面向對象中,我們的程序不再是以函數爲單位,而是以對象爲單位。
    # 面向過程編程中,程序中充滿大量的函數。
    # 面向對象編程中,程序中通過對象,調用對象提供的方法處理。
# 類的定義
# 使用class關鍵字
# 類中可以定義屬性與行爲。
# 屬性通過定義變量來表示。行爲通過方法來表示。
# 其實,方法就是函數,只不過函數是面向過程的稱呼,而方法是面向對象的稱呼。
# 函數不依賴與一個類的對象,而方法依賴與一個類的對象。
對象的初始化:
    # 我們定義__init__方法時,可以爲該方法提供一些參數,來更加靈活的進行初始化,這樣可以
    # 避免千篇一律的創建對象。
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 建議:總是通過類名來訪問類屬性,而不要通過對象訪問類屬性,因爲這會造成不必要的混淆。

# 類屬性與實例屬性,二者名稱相同也沒有問題。
# 通過類名訪問的,永遠是類屬性。通過對象進行訪問的,可能是類屬性,也可能是實例屬性。

    # 類方法,使用@classmethod修飾
    # 類方法的第一個參數是固定的,根據慣例,我們將其命名爲cls(class的簡稱),
    # 但這不是必須的。
    @classmethod
    def cm(cls):
        print("這是類方法")
        print(cls is Person)

在一個類中操作另一個類的屬性的方法見第二個習題

靜態方法使用@staticmethod修飾
    # 可以看出,靜態方法既沒有self,也沒有cls。所以,靜態方法設計的原則:既不訪問當前的類屬性,也不訪問
    # 當前對象的實例屬性。
    # 從結構上講,我們完全可以將靜態方法移除到類之外,靜態方法就真的像函數一樣。
    # 但是,靜態方法定義在類的內部,還是具有一定的意義。因爲有些方法就是完全爲一個類而服務的,處於邏輯上
    # 的關聯性,我們就可以將該方法定義在類之中,成爲靜態方法。

 

面向對象具有三大特徵,分別爲:
Ø封裝
Ø繼承
Ø多態
在程序中,我們可以通過將變量私有化來做到封裝。所謂的變量私有化,就是在類中定義的變量,僅能在當前類(定義變量的類)中訪問,而不能在類的外部訪問。
如果一個屬性名(或方法名)使用兩個下劃線(__)開頭,並且少於兩個下劃線結尾,則這樣的屬性(方法)就稱爲私有屬性(方法)。私有屬性(方法)只能在類的內部訪問。
名稱的私有化,這會給客戶端帶來一些問題。比如,客戶端想要獲取Person對象的名字(__name屬性),或者設置該屬性的值,現在都已經無法做到。

爲了能夠不影響客戶端的正常訪問,我們可以提供公有的訪問方法,一個用來獲取私有屬性值,一個用來設置私有屬性值。

在Python語言中,所謂的私有,不過是一種假象。當我們在類中定義私有成員時,在程序內部會將其處理成_類名 + 原有成員名稱的形式。也就是會將私有成員的名字進行一下僞裝而已,如果我們使用處理之後的名字,還是能夠進行訪問的。

在客戶端訪問時,公有的方法總不如變量訪問那樣簡便,怎樣才能既可以直接訪問變量,又能夠實現很好的封裝,做到信息隱藏呢?
我們可以使用property的兩種方式來實現封裝:
Ø使用property函數
Ø使用@property裝飾器

繼承的語法爲:
class B(A):
    類體

因爲實例屬性是定義__init__方法中,實現與對象(self)的綁定。如果子類沒有定義init方法,就會繼承父類的init方法,從而在創建對象時,調用父類的init方法,會將子類對象傳遞到父類的init方法中,
從而實現子類對象與父類init方法中實例屬性的綁定。但是,如果子類也定義了自己的init方法(重寫),則父類init方法就不會得到調用,這樣,父類init方法中定義的實例屬性就不會綁定到子類對象中。

雖然父類的__init__不完全適合子類,但是也並非完全不適合子類。因爲兩個類還是存在相同的屬性的,因此,我們應該充分利用現有的功能,不要重複的實現。
實現方式就是,我們在子類的構造器中,去調用父類的構造器,完成公共屬性的初始化,然後在子類構造器中,再對子類新增屬性進行初始化。我們可以這樣來調用父類的構造器:
super().__init__(父類構造器參數)

當子類繼承了父類,子類就可以繼承父類的成員。然而,父類的成員未必完全適合於子類(例如鳥會飛,但是鴕鳥不會飛),此時,子類就將父類中的成員進行調整,以實現適合子類的特徵與功能。我們將父類中的成員在子類中重新定義的現象,稱爲重寫。

# 使用property可以定義一個屬性(僞裝一個屬性)。
# 通過property既可以提供訪問的便利性,同時也能夠提供很好的封裝性。
# 定義property有兩種形式:
# 1 使用property函數。
# 2 使用@property裝飾器
class Computer:
    def __init__(self):
        self.__memory = 1024

    # property的4個參數:
    # 1 get方法 在讀取property值的時候,會調用該方法。
    # 2 set方法 在修改property值的時候,會調用該方法。
    # 3 del方法 在刪除property值的時候,會調用該方法。
    # 4 doc 給出該property的說明文檔。
    def get_memory(self):
        print("get方法得到調用")
        return self.__memory

    def set_memory(self, memory):
        print("set方法得到調用")
        self.__memory = memory

    def del_memory(self):
        print("del方法得到調用")
        del self.__memory

    memory = property(get_memory, set_memory, del_memory, "電腦的內存大小")
    # 如果不提供第2,3個參數,則該屬性就是隻讀屬性了。
    #memory = property(get_memory)

c = Computer()
print(c.memory)
c.memory = 2000
del c.memory
help(Computer.memory)

# 子類可以繼承父類中定義的類屬性,實例屬性,類方法,實例方法,靜態方法。

又繼承又增加:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
class Student(Person):
    def __init__(self, name, age, id):
        #調用父類的__init__方法
        super().__init__(name, age)
        self.id = id

# 關於私有成員的繼承
# 實際上,私有成員也是可以被子類繼承的。只不過,子類繼承的不是私有成員本來的名字,
# 而是私有成員僞裝之後的名字。
class Father:
    # _Father__m
    def __m(self):
        print("私有方法")


class Son(Father):
    def p(self):
        # 錯誤
        #self.__m()
        # 正確,通過僞裝之後的名字訪問。
        self._Father__m()

 

# 模塊
# 模塊就是一個文件,只是文件是從物理角度來講的,而模塊是從邏輯角度來講的。

# 定義模塊的優勢(好處):
# 1 定義模塊有利於將項目按照功能進行劃分,每個人負責一個或多個部分(模塊),
# 從而能夠進行協作式開發。
# 2 模塊提供了獨立的命名空間(全局命名空間),能夠命名衝突。
# 3 模塊可以供多人使用,提高了程序的複用性。

# 模塊的別名:
# 使用as可以爲模塊或者模塊內的成員名稱取一個別名。
# 別名的好處:
# 1 別名可以有效的解決命名衝突。
# 2 當名稱特別長時,我們可以取一個相對簡短的別名,減少輸入量。

# 返回a(第1個參數) - b(第2個參數)之間的隨機整數,[a, b](包括兩個端點)。
# print(random.randint(1, 3))

# 返回a(第1個參數) - b(第2個參數)之間的隨機整數,[a, b)(包括起始點,不包括終止點)。
# print(random.randrange(1, 3))

# 包類似於操作系統中的文件夾(目錄,路徑)。
# 包的作用:
# 1 包提供對模塊的分類管理。
# 2 包提供獨立的命名空間,可以解決模塊的命名衝突。

time模塊:
#time.localtime()將一個時間戳轉換成當前時區的struct_time
print(time.localtime())
#time.gmtime()和time.localtime()類似,time.gmtime()是將一個時間戳轉換爲UTC時區(0時區)的strcut_time
print(time.gmtime())

#time.asctime()把一個表示時間的元組或者struct_time表示爲這種形式: Wed Dec  5 14:58:17 2018
print(time.asctime())

#time.ctime()把一個時間戳(按秒計算的浮點數)轉化爲time.asctime()的形式
print(time.ctime())

#把一個代表時間的元組或者struct_time轉化爲格式化的時間字符串,如果t未指定,將傳入time.localtime()
print(time.strftime("%a",time.localtime()))

#把一個格式化時間字符串轉化爲struct_time。實際上它和strfttime()是逆操作
time.strptime("2018-01-01",'%Y-%m-%d')

 

可迭代對象:從簡單的角度講,這些可以用在for循環中,進行遍歷的對象,我們稱其爲可迭代對象。
生成器不會預先準備好所有的數據,而是在需要時,每次僅生成一個數據。這樣,在處理大量數據時,也不會佔用大量的內容空間。
閉包是指在函數體內部,訪問到其外圍函數中定義的變量。
裝飾器,用來處理被其所裝飾的函數,然後將該函數返回。從實現的角度講,裝飾器本身也是一個函數,其參數用來接收另外一個函數,然後返回一個函數,返回的函數與參數接收的函數可以相同,也可以不同。

# 生成器表達式非常類似於列表推導式,只是,將列表推導式的[]改成()。生成器表達式會返回一個生成器對象。
# 列表推導式
li = [i ** 2 for i in range(1, 101)]
print(type(li))
print(li)
# 生成器表達式表達式
li = (i ** 2 for i in range(1, 101))
print(type(li))
# 對於列表,我們可以看到列表中所有元素的值,但是對於生成器,我們看不到生成器元素的值。
# 因爲生產器是一種惰性計算的方式。僅當我們請求時,纔會計算一個數據。不會一次性計算所有的數據。
print(li)

# 我們可以通過for循環來獲取生成器中的數據。
for item in li:
    print(item)

# 生成器函數與普通函數的區別:
# 1 對於普通函數,當調用函數時,就會執行函數體。對於生成器函數,當調用函數時,不會執行函數體,而是會
# 返回一個生成器對象。當我們調用生成器對象的__next__等方法時,纔會執行函數體。
# 2 對於普通函數,每次調用,都會重新進行初始化。對於生成器函數,當調用__next__等方法執行時,遇到
# yield就會暫停執行,將yield後面的值返回給調用端(作爲__next__等方法的返回值)。暫停執行時,生成器
# 函數內部的變量等狀態都會得到保存,當再次調用生成器對象的__next__等方法時,會從剛纔yield暫停的位置,
# 繼續執行。

# 生成器函數與普通函數:就看函數體中有沒有yield,如果存在yield,則爲生成器函數,否則爲普通函數。
# 生成作爲特殊的迭代器,當迭代器無法進行迭代時(沒有元素可迭代),會產生StopIteration異常,
# 因此,生成器也有這個特徵。當生成器函數體執行完畢時,就會產生StopIteration異常。

# 當調用生成器對象的__next__方法時,生成器函數體就會得到執行。除此之外,當調用
# 生成器對象的send方法時,也會執行生成器的函數體。send方法的返回值也是yield所
# 產生的值。(這點與__next__是相同的。)

# __next__與send不同之處在於:send方法除了可以獲取生成器對象產生的值,同時,
# 也可以向生成器對象傳遞值。傳遞的值將作爲yield表達式的值。

# yield表達式的值取決於客戶端調用的方法。如果客戶端調用的是__next__方法,則
# yield表達式的值爲None,如果客戶端調用的是send方法,則yield表達式的值爲send
# 方法傳遞的參數值。

# 閉包
# 閉包指的是內部函數引用(訪問)外圍函數中定義的變量,對於內部函數來說,就是閉包。
# 閉包發生在函數嵌套的場合中。

# 閉包的作用:
# 1 當內部函數執行結束時,內部函數所引用的外圍函數中定義的變量狀態(值)依然可以保留下來。
# 當下一次調用內部函數時,外圍函數中的變量依然是內部函數上一次所離開時的值。
# 2 內部函數的名稱處於局部命名空間中,這樣就不會對外面的全局命名空間造成影響。即使名稱相同也沒關係。

# 裝飾器本質上就是一個函數(或類),能夠在不修改現有函數代碼的情況下,對現有函數的功能實現擴充。
# 語法:@裝飾器名  來修飾需要進行功能擴展的函數。

from datetime import datetime
# 函數經過裝飾器裝飾後,返回的函數已經不再是裝飾之前的函數,因此,原函數的元信息(名字,說明文檔,註解等)
# 都會丟失。

from functools import wraps


def check_in(fun):
    # 保留原函數的元信息
    # 功能:將wraps參數指定的函數的元信息複製給@wraps所修飾的函數。
    # 以本程序爲例,將fun函數的元信息複製給inner函數。
    @wraps(fun)
    def inner(*args, **kwargs):
        print(datetime.now())
        return fun(*args, **kwargs)
    return inner



@check_in
# on_duty = check_in(on_duty)
def on_duty(name: str) -> None:
    """
    這是上班函數。
    """
    print(f"{name}上班了")


on_duty("Bill")
# 返回函數的名字
print(on_duty.__name__)
# 返回函數的說明文檔
print(on_duty.__doc__)
# 返回函數的註解
print(on_duty.__annotations__)

# 離異常產生越近的信息,會在越下面顯示。因此,我們以後分析異常時,應該從下向上進行查看。
常見的異常類型
# BaseException Python中所有異常類的根類。
# Exception BaseException的子類。用戶異常的根類,我們自定義的異常應該繼承該類。
# ZeroDivisionError 當0做除數時,會產生該異常
# print(5 / 0)
# AssertionError 斷言的異常。當斷言的條件不滿足時,會產生該異常。
# assert 5 > 10
# ModuleNotFoundError 當使用import導入的模塊不存在時,會產生該異常。
# import asdfadf
# IndexError 索引異常。當索引越界時,會產生該異常。
# li = [1, 2]
# print(li[2])
# KeyError 鍵訪問異常。當鍵存在時(字典),會產生該異常。
# d = {"a": "sdfds"}
# print(d["b"])
# NameError 名稱異常。當訪問名稱不存在時,會產生該異常。
# print(a)
# RecursionError 遞歸異常。當函數調用層次達到了最大層次限制時(通常但不絕對是遞歸),會產生該異常。
# def x():
#     x()
# x()
# StopIteration 終止迭代異常。當迭代器已經沒有元素時,會產生該異常。
# li = [1]
# it = li.__iter__()
# it.__next__()
# it.__next__()
# SyntaxError 語法錯誤異常。當我們編寫的代碼不符合Python的語法規定時,會產生該異常。
# if 5 > 3:
# ValueError  值異常。當對值處理錯誤時,就會產生該異常。
# int("ab")

# 當異常產生時,異常之後的語句不會得到執行,這會影響程序的正常流程。

try中產生了異常,except能夠處理該異常。
# try中產生異常,則此時會創建相關異常類型的對象,try中異常之後的語句將不會得到執行(不論except能否
# 捕獲該異常)。然後,會使用該異常對象與各個except分支捕獲的異常類型進行匹配。匹配原則:
# isinstance(異常對象, except捕獲的異常類型),如果匹配成功(函數返回值爲True),則執行對應的except
# 分支。匹配順序:從上到下(最多隻會執行一個except分支)。
# 如果能夠匹配,則表示成功捕獲異常(異常被消滅了),try-except之後的語句可以正常得到執行。

# 當進行多個except捕獲異常時,如果多個except分支的類型存在繼承關係,
# 我們需要將子類放在前面,父類放在後面。

# 在進行異常處理時,except分支可以不指定類型。
# 表示會處理所有try中產生的異常類型。
# 不指定異常類型的except必須作爲最後一個except分支。並且,不指定異常類型的except只能具有一個。

# else 語句的作用:
# try中的語句應該是可能產生異常的語句,對於不會產生異常的語句,我們就不應該(不適合)
# 放入try中。else就可以將try中不產生異常的語句從try中分離,令程序結構變得更加清晰。

# 考慮到finally總是會執行的特徵,我們可以在finally當中進行一些清理的工作,是非常合適的。

def register(age):
    if age <= 0:
        # 我們可以在不符合要求時,主動去產生一個異常,這樣,就能夠給客戶端一個
        # 有效的提醒。因爲客戶端不明確處理,程序就會停止。(而if判斷的方式,程序不會停止)
        raise ValueError(f"age值必須大於0,你給的值不正確:{age}")
    # else:
    print("執行註冊的操作")

try:
    register(-3)
except ValueError:
    print("進行恢復措施!")


自定義異常:自己根據可能程序執行過程中可能出現的情況定義一個異常的處理方法,比如定義一個異常捕捉小於0的輸入

 

 

os模塊提供了很多操作目錄與文件的功能。

# 以x模式操作文件,文件必須不存在,如果存在,則產生FileExistsError。

# 在with處打開的文件,可以保證,在with結束後一定能夠得到有效的關閉。(無需我們顯式調用close方法)
# with open("c:/a.txt"):
#     # 對文件進行的操作
#     pass
#
# #with中,使用as來保存文件對象(獲得文件對象的引用)
# with open("c:/a.txt") as f:
#     # 可以通過f操作文件對象。
#     pass

# 當訪問文件結束後,應該要斷開與文件的鏈接。這樣才能夠讓別人進行訪問。
# f.close()

# # 因爲readlines方法會一次性讀取文件的全部內容,當文件內容非常大時,這非常耗費內容空間。
# # 因此readlines方法不適合操作大型文件。

# # 文件對象是迭代器類型。迭代器中的每個元素,就是文件的一行。
with open("d:/a.txt", "rt", encoding="UTF-8") as f:
    for line in f:
        print(line, end="")

# 對於w模式,如果文件不存在,則新建文件,如果文件存在,則覆蓋原有文件。
# with open("c:/a.txt", "wt") as f:
#     # 向文件寫入數據。
#     f.write("我想寫入點內容,修改一下。")

# 對於a模式,如果不存在,則新建文件,如果文件存在,則追加寫入。
# with open("c:/a.txt", "at") as f:
#     f.write("繼續追加一點內容")

with open("c:/a.txt", "rt") as f, open("c:/cp.txt", "wt") as f2:
    # 適合與小文件
    # f2.writelines(f.readlines())
    for line in f:
        f2.write(line)

# 創建參數指定的路徑。如果路徑已經存在,或者路徑的父路徑不存在,則會產生錯誤。
# os.mkdir("c:/abc/def")
# 創建參數指定的路徑。如果路徑所在的父路徑不存在,會連同父路徑一同創建。
# 如果路徑已經存在,則會產生錯誤。我們可以指定exist_ok參數爲True(該參數默認爲False),則存在
# 也沒有問題。
# os.makedirs("c:/abc/def/ghi", exist_ok=True)

# 刪除參數指定的目錄,如果目錄不存在,或者目錄不爲空,則會產生錯誤。
# os.rmdir("c:/abc/def/ghi")
# 刪除參數指定的目錄。子目錄刪除之後,會繼續檢查父目錄,如果父目錄此時也爲空,則會將父目錄一同刪除。
# 直到根目錄爲止。
# os.removedirs("c:/abc/def/ghi")
# 刪除參數指定的文件。如果文件不存在,則產生錯誤。
# os.remove("c:/abc/a.txt")

# 對文件進行重命名。第1個參數:源文件(目錄)。第2個參數:修改之後的文件(目錄)名。
# os.rename("c:/abc/1.txt", "c:/abc/2.txt")
# 與rename相似,只是源文件與目錄文件的路徑可以不一致(rename必須一致),此時就相當於是移動文件。
# 如果移動之後,父目錄成了空目錄,會將父目錄刪除。
# os.renames("c:/abc/2.txt", "c:/def/2.txt")

# 返回參數路徑的絕對路徑。
# print(os.path.abspath(".."))


# 複製參數指定的目錄。包括目錄中所有的子目錄與文件。(遞歸複製)
# 第1個參數:源目錄所在路徑
# 第2個參數:目標目錄所在的路徑。(要求不能存在)
# 返回複製之後目錄所在的路徑。
# print(shutil.copytree("c:/abc", "c:/new_abc"))

# 在複製時,我們還可以指定忽略(排除)哪些文件(類型)。
# ignore接受通配符。
# *表示任意0到多個字符。
# ?表示任意1個字符
# []表示[]內部定義的字符。
# shutil.copytree("c:/abc", "c:/new_abc", ignore=shutil.ignore_patterns("*.ab"))

# 刪除參數指定的目錄。會將該目錄內的子目錄與文件一併刪除(遞歸刪除)。
# shutil.rmtree("c:/new_abc")
# 移動文件或目錄。如果移動目錄,則會將目錄下的所有文件與子目錄一同移動(遞歸移動)。
# 第1個參數:源文件或目錄。
# 第2個參數:移動的位置。
# shutil.move("c:/abc", "c:/def")

# 在寫入csv文件時,最好將newline參數設置爲空""。(否則在excel會產生空行)
# with open("c:/def/test.csv", "wt", newline="") as f:
#     # writer函數返回一個寫入器對象,該對象能夠向參數指定的文件中寫入數據。
#     writer = csv.writer(f)
#     # 一次寫入一行記錄
#     writer.writerow(["張某", 20, "A組"])
#     writer.writerow(["李某", 15, "B組"])
#     # 一次寫入多行記錄。提供一個二維列表。每個元素(一維列表)就是一條記錄。
#     writer.writerows([["張某", 20, "C組"], ["張某", 20, "D組"]])

# 讀取csv文件
# with open("c:/def/test.csv", "rt") as f:
#     # reader函數返回一個讀取器對象,能夠讀取csv文件中的數據內容。
#     # 返回的對象是一個可迭代對象,可以放入for循環中。
#     reader = csv.reader(f)
#     for item in reader:
#         print(item)

# 上下文管理器用於with環境中。上下文管理器類需要定義__enter__與__exit__方法。
# 當進入with環境時,會調用上下文管理器類的__enter__方法(可以在該方法中執行一些
# 初始化的操作),當離開with環境時,就會調用上下文管理器的__exit__方法(可以在該)。
# 方法中執行一些清理的工作)。

#[字符] 匹配[]中的任意一個字符。[]還可以指定一個區間。
# 例如:a-z 匹配所有的小寫字母。A-Z 匹配所有的大寫字母。
# 0-9 匹配所有的阿拉伯數字。
# 如果需要匹配普通的-,],可以使用轉義。除此之外,還可以把-置於[]的兩端。
# ] 也可以置於[]的第一個字符(緊鄰[之後)。

# [^字符]與[字符]是相反的操作。表示匹配任意一個不在[]當中的字符。

# \d 在str模式下,匹配任意的數字字符,包括但不限於(遠遠多於)[0-9]。
# 在bytes模式下,匹配[0-9]。

# \D \d的取反操作,匹配非數字。
# r = re.search(r"a\Db", "3a#bsdf")

# \s 在str模式下,匹配Unicode字符集任意的空白字符,包括但不限於[空格\t\v\r\n\f]。
# \S 對\s 的取反,匹配任意一個非空白字符。

# \w 在str模式下,匹配所有的Unicode字符集中的單詞字符。包括但不限於[a-zA-Z0-9_](字母數字與下劃線)。
# \W \w的取反操作,匹配任意一個非單詞字符。

#findall 查找所有匹配的內容。返回一個列表。
li = re.findall("[0-9]", "a3a4x8i6")
print(li)

空間複雜度:空間複雜度是對一個算法在運行過程中臨時佔用存儲空間大小的量度。
時間複雜度:時間複雜度的計算並不是計算程序具體運行的時間,而是算法執行語句的次數

 

# 線程:線程是進程的基本執行單元。一個進程至少要存在一個線程。
# 進程具有獨立的空間與系統資源,但線程不具有獨立的系統資源,而同一個進程下的多線程共享該進程中的資源。

# 進程之間是獨立的,一個進程想訪問另外一個進程的數據,必須要得到另外一個進程的授權纔可以(不能隨便訪問)。
# 同一個進程下的多個線程可以相互協作(可以相互訪問)。

# Python提供了threading模塊(Thread)類,來實現多線程的功能。
# 可以採用兩種方式來創建線程:
# 1 使用Thread類,提供target參數。
# 2 繼承Thread類,重寫run方法。

# target參數指定線程要執行的任務。當線程運行時,就會執行(調用)該函數。
# args 指定函數的參數。(元組類型)
t = threading.Thread(target=mission, args=(3, 8))
# 僅僅創建了線程的對象,該線程還不能夠得到執行。需要調用start方法來啓動線程。
# 調用start之後,該線程就處於就緒狀態。處於就緒狀態的線程不代表會馬上得到執行。
# 具體何時執行,要取決於操作系統的調度。
t.start()
t2 = threading.Thread(target=mission, args=(10, 19))
t2.start()

t = MyThread()
t2 = MyThread(start=15, end=23)
# 調用start與調用run,二者是不同的。儘管都能指定相同的程序代碼。但是,調用start等於是開啓了一個新的線程,
# 可以與其他線程並行執行。但是,調用run,沒有開啓新的線程,還是串行執行。
t.start()
t2.start()
# t.run()
# t2.run()


# 線程的聲明週期:
# 1 新建。  創建了線程的對象。處於新建狀態的線程不具有執行能力。
# 2 就緒。  調用start方法之後,線程就會處於就緒狀態。一旦獲得CPU的時間片,就可以得到執行。
# 3 運行。  就緒狀態的線程獲得了CPU的時間片,就可以得到執行。
# 4 阻塞。  應爲某些條件沒有滿足,而處於等待過程中。(input)
# 5 死亡。  線程run方法執行完畢。(或run方法中拋出未捕獲的異常)

 

同一時間是執行主進程還是子進程無法預知,完全取決於操作系統

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章