關鍵字:print() type() input() append() pop() range() numpy math.ceil() index() @classmethod @staticmethod self __init__() __str__() split() join() random dir() csv time os smtplib email myqr
第0關 print()函數與變量
1. print()函數print()
函數:告訴計算機,把括號的內容顯示在屏幕上
# 不帶引號 print(1+1) # 讓計算機讀懂括號里的內容,打印最終的結果 # 》》2 # 帶引號 print('hello world!') # 讓計算機無需理解,原樣復述引號中的內容 # 》》hello world! # 帶雙引號 print("Let's play") # 讓計算機無需理解,原樣復述引號中的內容,可以輸出內容中的單引號 # 》》Let's play # 三引號 print(''' へ /| /\7 ∠_/ / │ / / │ Z _,< / /`ヽ │ ヽ / 〉 Y ` / / イ● 、 ● ⊂⊃〈 / () へ | \〈 >ー 、_ ィ │ // / へ / ノ<| \\ ヽ_ノ (_/ │//''') # 原樣打印,實現換行 # 》》 へ /| # /\7 ∠_/ # / │ / / # │ Z _,< / /`ヽ # │ ヽ / 〉 # Y ` / / # イ● 、 ● ⊂⊃〈 / # () へ | \〈 # >ー 、_ ィ │ // # / へ / ノ<| \\ # ヽ_ノ (_/ │//
2. 轉義字符
3. 變量
變量賦值用=
表示,不要跟==搞混
第1關 數據類型與轉換
1. 數據類型
Python里,最常用的數據類型有三種——字符串(str)、整數(int)和浮點數(float)
· 字符串英文string,簡寫str,有層名為【引號】的皮,只要是被【單/雙/三引號】這層皮括起來的內容,不論那個內容是中文、英文、數字甚至火星文。只要是被括起來的,就表示是字符串類型
· 整數英文為integer,簡寫做int,Python世界的整數其實和現實世界數學中定義的一樣:是正整數、負整數和零的統稱,是沒有小數點的數字
· 浮點數的英文名是float,是帶小數點的數字。與【整數運算結果永遠精確】的特點不同,計算機里浮點數的運算是不精確的,因為計算浮點數時會先講數字轉換成二進制數,通過二進制法則運算后,再通過復雜的計算公式,將二進制結果轉成十進制小數
2. 算術運算符
print(2+1) # 加法 # 》》3 print(1-2) # 減法 # 》》-1 print(1*2) # 乘法 # 》》 2 print(1/2) # 除法 # 》》0.5 print(5 % 2) # 取模(做除法返回余數) # 》》1 print(2**3) # 冪(這里是2的3次方) # 》》8 print(11//2) # 取整除(做除法返回商的整數部分) # 》》5
一些列表的相關操作↓
s = ['柯佳嬿', '許光漢', '施柏宇', '顏毓麟', '林鶴軒', '張翰'] print(len(s)) # 打印列表s的長度 # 》》6 print(type(s)) # 打印列表s的類型 # 》》<class 'list'> print(s[3]) # 打印列表s里面的第3個元素 # 》》顏毓麟 print(s[-3]) # 打印列表s里面的倒數第3個元素 # 》》顏毓麟 list1 = [91, 95, 97, 99] list2 = [92, 93, 96, 98] # 列表的合並 list3 = list1+list2 # 第一種方法:+直接拼接 print(list3) # 》》[91, 95, 97, 99, 92, 93, 96, 98] list1.extend(list2) # 第二種方法:extend()方法 print(list1) # 》》[91, 95, 97, 99, 92, 93, 96, 98] # 列表的排序 a = [91, 95, 97, 99, 92, 93, 96, 98] a.sort() # 列表從小到大排序 print(a) # 》》[91, 92, 93, 95, 96, 97, 98, 99] b = [91, 95, 97, 99, 92, 93, 96, 98] b.sort(reverse=True) # 列表從大到小排序 print(b) # 》》[99, 98, 97, 96, 95, 93, 92, 91] c = [91, 95, 97, 99, 92, 93, 96, 98] c.reverse() # 列表反向排序 print(c) # 》》[98, 96, 93, 92, 99, 97, 95, 91]
用冒號來截取列表元素的操作叫作切片(slice),切片可以單拿出來說很長的篇幅,網上搜到一篇講的很清楚的:徹底搞懂Python切片操作
給列表增加元素:列表名.append()
給列表刪除元素:del 列表名[元素的索引]
pop()
函數,用於移除列表中的一個元素(默認最后一個元素),並且返回該元素的值↓
students = ['小明', '小紅', '小剛'] for i in range(3): student1 = students.pop(0) # 運用pop()函數,同時完成提取和刪除。 students.append(student1) # 將移除的student1安排到最后一個座位。 print(students) # 》》》 ['小紅', '小剛', '小明'] # 》》》 ['小剛', '小明', '小紅'] # 》》》 ['小明', '小紅', '小剛']
2. 字典(dict)
字典中的鍵具備唯一性,而值可重復
從字典中提取元素:字典名[字典的鍵]
字典新增鍵值對:字典名[鍵] = 值
↓
scores = {'小明': 95, '小紅': 90, '小剛': 90} print(scores['小明']) # 從字典中提取元素 # 》》95 scores['小紅'] = 88 # 改變字典中的值 print(scores['小紅']) # 》》88 scores['小北'] = 98 # 給字典新增鍵和值 print(scores) # 》》{'小明': 95, '小紅': 88, '小剛': 90, '小北': 98}
字典刪除鍵值對:del 字典名[鍵]
3. 列表和字典的不同點
列表中的元素是有自己明確的“位置”的,所以即使看似相同的元素,只要在列表所處的位置不同,它們就是兩個不同的列表。而字典相比起來就顯得隨和很多,調動順序也不影響。因為列表中的數據是有序排列的,而字典中的數據是隨機排列的。
4. 列表和字典的嵌套
students = { '第一組':['小明','小紅','小剛','小美'], '第二組':['小強','小蘭','小偉','小芳'] } print(students['第一組'][3]) #取出'第一組'對應列表偏移量為3的元素,即'小美' scores = [ {'小明':95,'小紅':90,'小剛':100,'小美':85}, {'小強':99,'小蘭':89,'小偉':93,'小芳':88} ] print(scores[1]['小強']) #先定位到列表偏移量為1的元素,即第二個字典,再取出字典里鍵為'小強'對應的值,即99。
5. 元祖(tuple)
元組的寫法是將數據放在小括號()
中,它的用法和列表用法類似,主要區別在於列表中的元素可以隨時修改,但元組中的元素不可更改
list2 = [('A', 'B'), ('C', 'D'), ('E', 'F')] print(list2[1][1]) # 》》 D # 從代碼里可看出:1.元組內數據的提取也是用偏移量;2.元組也支持互相嵌套。
以上幾種關於收納的數據類型,最常用的還是列表,而對偏移量和切片的使用是寫算法非常重要的技能
第5關 for循環和while循環
1. for...in...循環語句
2. range()
函數
3. while循環
4. 兩種循環對比
第6關 布爾值和四種語句
布爾運算的三種方式:
第一種計算:兩個數值做比較
一共有6種比較方式:==、!=、>、<、>=、<=
print(3 == 3.0) # 》》True print('a' != 'a') # 》》False print(3 > 5) # 》》False print(3 < 5) # 》》True print(100 >= 101) # 》》False print(100 <= 101) # 》》True
第二種計算:直接用數值做運算
第三種計算:布爾值之間的運算
一共有5種計算方式:and、or、not、in、not in
# and計算和or計算 a = 1 b = -1 print(a == 1 and b == 1) # and要求條件都滿足才為True # 》》False print(a == 1 or b == 1) # or只要求其中一個條件滿足就為True # 》》True # not計算 a = True print(not a) # not計算會反轉真假 # 》》False # in計算和not in計算 a = [1, 2, 3, 4, 5] print(0 in a) # in用來判斷一個元素是否在一堆數據之中 # 》》False print(1 in a) # 》》True print(0 not in a) # not in用來判斷一個元素是否不在一堆數據之中 # 》》True print(1 not in a) # 》》False
第7關 項目實操:PK小游戲(1)
import time #調用time模塊 time.sleep(secs) #使用time模塊下面的sleep()函數,括號里填的是間隔的秒數(seconds,簡稱secs) #time.sleep(1.5)就表示停留1.5秒再運行后續代碼
import random # 調用random模塊 a = random.randint(1, 100) # 隨機生成1-100范圍內(含1和100)的一個整數,並賦值給變量a print(a) print(random.choice([5, 7, 9, 11, 13, 15])) # 在給定列表中隨機選擇
取英文變量名推薦網站:CODELF
三種格式化字符串方法↓
a = 5 b = 7.85 print(str(a)+'+'+str(b)+'='+str(a+b)) # 第一種:+拼接字符串和變量 # 》》5+7.85=12.85 print('%d+%s=%.2f' % (a, b, a+b)) # 第二種:格式符+類型碼 %.2f的意思是格式化字符串為浮點型並保留兩位小數 # 》》5+7.85=12.85 print('{}+{}={}'.format(a, b, a+b)) # 第三種:format()函數 # 》》5+7.85=12.85
第8關 編程思維:如何解決問題
拓展程序庫numpy
的簡單用法↓
import numpy as np # 導入 numpy庫,下面出現的 np 即 numpy庫 a = [91, 95, 97, 99, 92, 93, 96, 98] average = np.mean(a) # 求平均數,average是numpy.float64類型 print(average) # 》》95.125 b = np.array(a) # 將列表a轉換為numpy.ndarray類型 print('小於平均數的有:{}' .format(b[b < average])) # 》》[91 95 92 93] c = b.tolist() # 將數組b轉換回列表 print(type(c)) # 》》<class 'list'>
第9關 函數
函數是組織好的、可以重復使用的、用來實現單一功能的代碼
1. 定義和調用函數
# 函數名:1. 名字最好能體現函數的功能,一般用小寫字母和單下划線、數字等組合 # 2. 不可與內置函數重名(內置函數不需要定義即可直接使用) def math(x): # 參數:根據函數功能,括號里可以有多個參數,也可以不帶參數,命名規則與函數名相同 # 規范:括號是英文括號,后面的冒號不能丟 y = x ** 2 + x # 函數體:函數的執行過程,體現函數功能的語句,要縮進,一般是四個空格 return y # return語句:后面可以接多種數據類型,如果函數不需要返回值的話,可以省略 print(math(5)) # 調用math()函數
2. 參數類型
以最熟悉的print()函數為例↓
print(*objects, sep = ' ', end = '\n', file = sys.stdout, flush = False)
可以看到第一個參數objects帶了*號,為不定長參數——這也是為什么print()函數可以傳遞任意數量的參數。其余四個為默認參數,我們可以通過修改默認值來改變參數。
print('www', 'pypypy', 'cn') # objects -- 復數,表示可以一次輸出多個對象。輸出多個對象時,需要用 , 分隔。 # 》》www pypypy cn print('www', 'pypypy', 'cn', sep=".") # sep -- 用來間隔多個對象,默認值是一個空格。 # 》》www.pypypy.cn print('hello', end='~~~~~~') # end -- 用來設定以什么結尾。默認值是換行符 \n,可以換成其他字符串。 print('world') # 》》hello~~~~~~world
print()函數的參數file和flush說起來篇幅比較長,單寫了篇關於print()、sys.stdout、sys.stderr的一些理解
3. 返回多個值
def a(): return 1, 2, 3 b = a() # 變量b接收函數a的返回值 print(b) # 打印返回值 # 》》(1, 2, 3) print(type(b)) # 打印返回值的類型 # 》》<class 'tuple'>
也可以同時定義多個變量,來接收元組中的多個元素
def a(): return 1, 2, 3 x, y, z = a() print(x) # 》》1 print(y) # 》》2 print(z) # 》》3
4. 變量作用域
· 一個在函數內部賦值的變量僅能在該函數內部使用(局部作用域),它們被稱作【局部變量】
· 在所有函數之外賦值的變量,可以在程序的任何位置使用(全局作用域),它們被稱作【全局變量】
如果想將局部變量聲明為全局變量,就要用到global
語句
tfc = 1000 def tvc(): global tvc # global語句一般寫在函數體的第一行,它會告訴Python,“我希望tvc是個全局變量,所以請不要用這個名字創建一個局部變量” vc = 200 x = 10 tvc = vc * x def tc(): print(tfc+tvc) # tc()函數內部現在可以直接使用聲明后的全局變量tvc tvc() tc() # 》》3000
5. 函數的嵌套
tfc = 1000 def tvc(): vc = 200 x = 10 return vc * x def tc(): print(tfc+tvc()) # 在tc()函數中調用了tvc()函數 tc() # 》》3000
6. 遞歸
def digui(num): if (num == 1): return 1 return num * digui(num-1) x = digui(5) print(x) # 》》120
第10關 項目實操:PK小游戲(2)
import math # 人力計算 def estimated_number(size, time): number = size * 80 / time print(number) # 》》1.3333333333333333 print('項目大小為%.1f個標准項目,如果需要在%.1f個工時完成,則需要人力數量為:%d人' % (size, time, math.ceil(number))) # 通過ceil()函數向上取整 # 》》項目大小為1.0個標准項目,如果需要在60.0個工時完成,則需要人力數量為:2人 estimated_number(1, 60)
如果不用ceil()
函數,這個人力計算向上取整也可以通過判斷余數不為零就加一的方法實現
2. index()函數index()
函數用於找出列表中某個元素第一次出現的索引位置
import random punches = ['石頭', '剪刀', '布'] # 猜拳游戲 def punches_game(user_choice): computer_choice = random.choice(punches) if user_choice == computer_choice: print('平局') # 電腦的選擇有3種,索引位置分別是:0石頭、1剪刀、2布。 # 假設在電腦索引位置上減1,對應:-1布,0石頭,1剪刀,皆勝。 elif user_choice == punches[punches.index(computer_choice)-1]: print('你贏了') else: print('你輸了') punches_game(user_choice='布')
第11關 編程思維:如何debug
多行注釋有兩種快捷操作:1) 在需要注釋的多行代碼塊前后加一組三引號'''
2) 選中代碼后使用快捷鍵操作:Mac為cmd+/
異常處理:
# 方式1: try: x = input('請你輸入被除數:') y = input('請你輸入除數:') z = float(x)/float(y) print(x, '/', y, '= %.2f' % z, sep='') except ZeroDivisionError: # 當除數為0時,跳出提示,重新輸入。 print('0是不能做除數的!') except ValueError: # 當除數或被除數中有一個無法轉換成浮點數時,跳出提示,重新輸入。 print('除數和被除數都應該是整值或浮點數!') # 方式2:將兩個(或多個)異常放在一起,只要觸發其中一個,就執行所包含的代碼。 # except(ZeroDivisionError,ValueError): # print('你的輸入有誤,請重新輸入!') # 方式3:常規錯誤的基類,假設不想提供很精細的提示,可以用這個語句響應常規錯誤。 # except Exception: #換成BaseException也可以 # print('你的輸入有誤,請重新輸入!')
關於Python的所有報錯類型,一般可以在這里查閱:https://www.runoob.com/python/python-exceptions.html
異常層次結構需到官網查看https://docs.python.org/zh-cn/3/library/exceptions.html#exception-hierarchy
第12關 類與對象1
Python中的對象等於類(class)和實例(instance)的集合:即類可以看作是對象,實例也可以看作是對象
1. 類的創建和調用
Python里的每個類都有自己獨特的屬性(attribute)和方法(method),是這個類的所有實例都共享的。換言之,每個實例都可以調用類中所有的屬性和方法。
類名的首字母要大寫,以便讓我們輕松地辨認出“哦!這個是類!”
實例方法的創建語句,和函數的定義語句很類似,唯一不同的是:實例方法中有個必須放在首位的參數self
class Computer: # 創建一個類 screen = True def start(self): print('電腦正在開機中……') my_computer = Computer() # 類的實例化 print(type(my_computer)) # <class '__main__.Computer'>驗證了my_computer屬於Computer這個類 # 》》<class '__main__.Computer'> print(my_computer) # 打印出Computer類的一個實例對象(object),后面的一串字符表示這個對象的內存地址(每次實例化都不一樣) # 》》<__main__.Computer object at 0x10e6bad00> # 當實例my_computer一被創建出來,就可以調用類中的屬性和方法。一句話概括就是:類有的實例都會有 print(my_computer.screen) # 實例調用類屬性 # 》》True my_computer.start() # 調用類中的方法(傳參不用管self) # 》》電腦正在開機中……
類中創建的屬性和方法可以被其所有的實例調用,而且,實例的數目在理論上是無限的。我們可以同時“新建”多個實例
因此,類也被稱為“實例工廠”,因其為所有實例提供了一套藍圖(即預先設定好有什么屬性和方法)
除了實例方法,還有兩個比較常見的方法類型,類方法和靜態方法
類方法需使用裝飾器@classmethod
,第一個參數必須是當前類對象,該參數名一般約定為cls
,通過它來傳遞類的屬性和方法
class Computer: screen = '已開機' @classmethod def start(cls): print('電腦的狀態為:', cls.screen, sep='') Computer.start() # 可是是類對象調用 # 》》電腦的狀態為:已開機 my_computer = Computer() my_computer.start() # 也可以是實例對象調用 # 》》電腦的狀態為:已開機
類方法是實例對象和類對象都可以調用,但實例方法只能由實例對象調用,如果非要用類調用,比如繼承重寫時,那就把實例放在括號里傳參
靜態方法使用裝飾器@staticmethod
,參數隨意,實例對象和類對象都可以調用
如果只是存放邏輯性代碼,不涉及類中其他屬性或方法,就可以選擇使用靜態方法
import random class Computer: @staticmethod def start(): return random.randint(1, 99) print(Computer.start())
2. 特殊參數:self
self會接收實例化過程中傳入的數據,當實例對象創建后,實例便會代替 self,在代碼中運行。
class Chinese: name = '周冬雨' # 類屬性name def greeting(self): print('你好啊冬叔') def say(self): print(self.name + '是中國人') # 調用類屬性 self.greeting() # 調用實例方法 person = Chinese() # 創建Chinese的實例person person.say() # 調用實例方法 # 》》周冬雨是中國人 # 》》你好啊冬叔
當最后一行代碼運行時,實例person會像參數一樣傳給self,替換掉self
作用相當於:
class Chinese: name = '周冬雨' # 類屬性name def greeting(person): print('你好啊冬叔') def say(person): print(person.name + '是中國人') # 調用類屬性 person.greeting() # 調用實例方法 person = Chinese() # 創建Chinese的實例person person.say() # 調用實例方法 # 》》周冬雨是中國人 # 》》你好啊冬叔
self的作用相當於先給實例占了個位置,關鍵在於占位,即使叫別的名字也是可以的,只是一般不這樣做而已
綜上,所以我們說self代表的是類的實例本身,方便數據的流轉。對此,需要記住兩點:
第一點:只要在類中用def創建實例方法時,就必須把第一個參數位置留給self,並在調用方法時忽略它(不用給self傳參)
第二點:當在類的方法內部想調用類屬性或其他方法時,就要采用self.屬性名
或self.方法名
的格式
3. 特殊方法:初始化方法(也叫構造函數)
定義初始化方法的格式是def __init__(self)
,是由init加左右兩邊的【雙】下划線組成( initialize “初始化”的縮寫)
初始化方法的作用在於:當每個實例對象創建時,該方法內的代碼無須調用就會自動運行
在編寫習慣上,我們會在初始化方法內部完成類屬性的創建,為類屬性設置初始值,這樣類中的其他方法就能直接、隨時調用
class Chinese: def __init__(self, name, birth, region): self.name = name # self.name = '周冬雨' self.birth = birth # self.birth = '河北' self.region = region # self.region = '石家庄' def born(self): print(self.name + '出生在' + self.birth) def live(self): print(self.name + '居住在' + self.region) person = Chinese('周冬雨', '河北', '石家庄') # 傳入初始化方法的參數 person.born() # 》》周冬雨出生在河北 person.live() # 》》冬雨居住在石家庄
第13關 類與對象2
在Python中,我們的習慣表述是:A類是B類的子類,而B類是A類的父類(或超類)
類的定制,不僅可以讓子類擁有新的功能,還能讓它有權修改繼承到的代碼
所以,當我們談定制時,已經包含了繼承。畢竟,類的定制的前提是繼承,而定制的加入讓類的繼承不僅僅只是單純的復制而已
1. 繼承的基礎語法
class Chinese: eye = 'black' def eat(self): print('吃飯,選擇用筷子') class Cantonese(Chinese): # 通過繼承,Chinese類有的,Cantonese類也有 pass # 驗證子類可以繼承父類的屬性和方法,進而傳遞給子類創建的實例 yewen = Cantonese() # 子類創建的實例,從子類那間接得到了父類的所有屬性和方法 print(yewen.eye) # 子類創建的實例,可調用父類的屬性 # 》》black yewen.eat() # 子類創建的實例,可調用父類的方法 # 》》吃飯,選擇用筷子
函數isinstance()
,可以用來判斷某個實例是否屬於某個類
class Chinese: pass class Cantonese(Chinese): pass gonger = Chinese() yewen = Cantonese() print(isinstance(yewen, Cantonese)) # 判斷yewen是否為Cantonese的實例 # 》》True print(isinstance(yewen, Chinese)) # 判斷yewen是否為Chinese的實例 # 》》True print(isinstance(gonger, Cantonese)) # 判斷gonger是否為Cantonese的實例 # 》》False print(isinstance(gonger, (Cantonese, Chinese))) # 判斷gonger實例是否屬於元組里幾個類中的一個 # 》》True print(isinstance(gonger, object)) # 判斷gonger是否為object的實例 # 》》True print(isinstance(yewen, object)) # 判斷yewen是否為object的實例 # 》》True
理論上,父類可以被無限個子類所繼承
2. 類的繼承之多層繼承
子類創建的實例可調用所有層級父類的屬性和方法
class Earthman: eye_number = 2 class Chinese(Earthman): eye_color = 'black' class Cantonese(Chinese): # Cantonese繼承了Chinese,同時也繼承了Earthman pass yewen = Cantonese() print(yewen.eye_number) # 》》2 print(yewen.eye_color) # 》》black
3. 類的繼承之多重繼承
一個類,可以同時繼承多個類,語法為class A(B,C,D):
括號里的順序是有講究的,和子類更相關的父類會放在更左側,所以A的實例在調用屬性和方法時,會先在左側的父類中找,找不到才會去右側的父類找(可理解為“就近原則”)
class Su: born_city = 'Jiangsu' wearing = 'thick' def diet(self): print('我們愛吃甜') class Yue: settle_city = 'Guangdong' wearing = 'thin' def diet(self): print('我們吃得清淡') class Yuesu(Yue, Su): pass xiaoming = Yuesu() print(xiaoming.wearing) # 先在 Yue類找,找到了,打印出來 # 》》thin print(xiaoming.born_city) # Yue類沒有born_city,才去Su類找 # 》》Jiangsu xiaoming.diet() # 方法調用,和屬性調用一樣,也符合就近原則 # 》》我們吃得清淡
class C0: name = 'C0' class C2(C0): num = 2 class C1: num = 1 class C3: name = 'C3' class C4(C1, C2, C3): pass ins = C4() print(ins.name) # 》》C0 print(ins.num) # 》》1
可以發現就近原則中的一個細節:多重繼承中,若某父類還有父類的話,會先繼續往上找到頂
4. 類的定制,新增代碼
class Chinese: eye = 'black' def eat(self): print('吃飯,選擇用筷子') class Cantonese(Chinese): # 類的繼承 native_place = 'guangdong' # 類的定制 def dialect(self): # 類的定制 print('我們會講廣東話') yewen = Cantonese() print(yewen.eye) # 父類的屬性能用 # 》》black print(yewen.native_place) # 子類的定制屬性也能用 # 》》guangdong yewen.eat() # 父類的方法能用 # 》》吃飯,選擇用筷子 yewen.dialect() # 子類的定制方法也能用 # 》》我們會講廣東話
可見:我們可以在子類下新建屬性或方法,讓子類可以用上父類所沒有的屬性或方法。這種操作,屬於定制中的一種:新增代碼
5. 類的定制,重寫代碼
重寫代碼,是在子類中,對父類代碼的修改
class Chinese: def land_area(self, area): print('我們居住的地方,陸地面積是%d萬平方公里左右' % area) class Cantonese(Chinese): def land_area(self, area, rate=0.0188): # 間接對方法進行重寫 Chinese.land_area(self, area * rate) # 直接繼承父類方法,再調整參數 gonger = Chinese() yewen = Cantonese() gonger.land_area(960) # 》》我們居住的地方,陸地面積是960萬平方公里左右 yewen.land_area(960) # 》》我們居住的地方,陸地面積是18萬平方公里左右
子類繼承父類方法的操作是在def
語句后接父類.方法(參數)
第14關 項目實操:PK小游戲(3)
在Python中,如果方法名形式是左右帶雙下划線的,那么就屬於特殊方法(如初始化方法),有着特殊的功能
如果在類中定義了__str__(self)
方法,那么當使用print打印實例對象的時候,就會直接打印出在這個方法中return的數據
class Book: def __init__(self, name, author, comment, state=0): self.name = name self.author = author self.comment = comment self.state = state def __str__(self): if self.state == 0: status = '未借出' else: status = '已借出' return '名稱:《%s》 作者:%s 推薦語:%s\n狀態:%s ' % (self.name, self.author, self.comment, status) book1 = Book('像自由一樣美麗', '林達', '你要用光明來定義黑暗,用黑暗來定義光明') # 傳入參數,創建實例對象 print(book1) # 直接打印對象即可,不能寫成print(book1.__str__()) # 》》名稱:《像自由一樣美麗》 作者:林達 推薦語:你要用光明來定義黑暗,用黑暗來定義光明 # 》》狀態:未借出
第15關 編碼和文件讀寫
1. 二進制
二進制轉十進制,例如:(11010)2 = 1*24+1*23+0*22+1*21+0*20 = (26)10
兩個二進制位可以表示22,十進制的0,1,2,3,四種狀態
八個二進制位可以表示28,也就是256種狀態
用來存放一位0或1,就是計算機里最小的存儲單位,叫做【位】,也叫【比特】(bit)。8個比特構成一個【字節】(byte),這是計算機里最常用的單位
“流量”就是這么計算的↓
百兆寬帶,下載速度最多只能達到十多兆,是因為運營商的帶寬是以比特每秒為單位的,比如100M就是100Mbit/s
而我們常看到的下載速度KB卻是以字節每秒為單位顯示的,1byte = 8bit,所以運營商說的帶寬得先除以8,百兆寬帶下載速度,也就是十幾兆了
2. 編碼表
UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼,它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度,而當字符在ASCII碼(讀音:/ˈæski/)的范圍時,就用一個字節表示,所以UTF-8還可以兼容ASCII編碼
Unicode與UTF-8這種曖昧的關系一言以蔽之:Unicode是內存編碼的規范,而UTF-8是如何保存和傳輸Unicode的手段
因為二進制是由一堆0和1構成的,過長的數字對於人的閱讀有很大障礙,為了解決這一問題,也減少書寫的復雜性,又引入了八進制和十六進制
為什么偏偏是16或8進制?2、8、16,分別是2的1次方、3次方、4次方。這一點使得三種進制之間可以非常直接地互相轉換
8進制是用0,1,2,3,4,5,6,7;16進制是用0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f來表示。比如說,字母K在ASCII編碼表用不同進制表示的話是這樣的↓
3. 編碼方案在當前的使用情況
第0,計算機是有自己的工作區的,這個工作區被稱為“內存”。數據在內存當中處理時,使用的格式是Unicode,統一標准
在Python3當中,程序處理輸入的字符串是默認使用Unicode編碼的,所以什么語言都可以輸入
第1,數據在硬盤上存儲,或者是在網絡上傳輸時,用的是UTF-8,因為節省空間。不必操心如何轉換UTF-8和Unicode,當點擊保存的時候,程序已經“默默地”做好了編碼工作
第2,一些中文的文件和中文網站,還在使用GBK,和GB2312
4. encode()和decode()
編碼,即將人類語言轉換為計算機語言,就是【編碼】encode();反之,就是【解碼】decode()。它們的用法如下圖所表示↓
print('柯佳嬿'.encode('utf-8')) # 》》b'\xe6\x9f\xaf\xe4\xbd\xb3\xe5\xac\xbf' print('柯佳嬿'.encode('gbk')) # 》》b'\xbf\xc2\xbc\xd1\x8b\xf7' print(b'\xe6\x9f\xaf\xe4\xbd\xb3\xe5\xac\xbf'.decode('utf-8')) # 》》柯佳嬿 print(b'\xbf\xc2\xbc\xd1\x8b\xf7'.decode('gbk')) # 》》柯佳嬿 print(type(b'\xbf\xc2\xbc\xd1\x8b\xf7')) # 》》<class 'bytes'>
將人類語言編碼后得到的結果,最前面都有字母b,代表是bytes(字節)類型的數據;\x是分隔符,用來分隔一個字節和另一個字節
所謂的編碼,其實本質就是把str(字符串)類型的數據,利用不同的編碼表,轉換成bytes(字節)類型的數據
將\x替換為%,就是網址中的搜索條件了,百度搜索“柯佳嬿”↓
https://www.baidu.com/s?wd=%bf%c2%bc%d1%8b%f7(gbk)
https://www.baidu.com/s?wd=%e6%9f%af%e4%bd%b3%e5%ac%bf(utf-8)
用decode()解碼的時候要注意,UTF-8編碼的字節就一定要用UTF-8的規則解碼,其他編碼同理,否則就會出現亂碼或者報錯的情況
print('K'.encode('ASCII')) # 》》b'K'
大寫字母K被編碼后還是K,但這兩個K對計算機來說意義是不同的,前者是字符串,采用系統默認的Unicode編碼,占兩個字節,后者則是bytes類型的數據,只占一個字節,這也驗證了前面所說的編碼就是將str類型轉換成bytes類型
5. 讀寫文件open()
函數的第一個參數是文件路徑,分為兩種:相對路徑和絕對路徑,絕對路徑就是最完整的路徑,相對路徑指的就是【相對於當前文件夾】的路徑,也就是當前編寫的這個py文件所放的文件夾路徑
可以把文件直接拖到終端窗口獲取絕對路徑,還可以用VS Code打開文件夾,在文件點擊右鍵↓
需要注意的是,Windows系統里,常用\來表示絕對路徑,由於\在Python中是轉義字符,所以時常會有沖突。為了避坑,Windows的絕對路徑通常要稍作處理,寫成以下兩種格式↓
open('C:\\Users\\Ted\\Desktop\\test\\abc.txt') # 將'\'替換成'\\' open(r'C:\Users\Ted\Desktop\test\abc.txt') # 在路徑前加上字母r
第二個參數表示打開文件時的模式,這里是字符串 'r',表示 read,表示我們以讀的模式打開了這個文件
第三個參數encoding='utf-8',表示的是返回的數據采用何種編碼,一般采用utf-8或者gbk,注意這里是寫encoding而不是encode
打開文件file1之后,就可以用read()
函數進行讀取的操作
關閉文件,使用的是close()
函數
為啥要關閉文件呢?原因有兩個:1.計算機能夠打開的文件數量是有限制的,open()過多而不close()的話,就不能再打開文件了。2.能保證寫入的內容已經在文件里被保存好了。
open()的第二個參數如果是'w',表示write,會先清空掉文件內容,然后再寫入;不想完全覆蓋掉原文件的話,就要使用'a'模式,表示append,追加的意思write()
函數寫入文本文件的是字符串類型
在'w'和'a'模式下,如果打開的文件不存在,那么open()函數會自動創建一個
圖片和音頻是以二進制的形式保存的,就可以用wb模式,它的意思是以二進制的方式打開一個文件用於寫入
為了避免打開文件后忘記關閉,占用資源或當不能確定關閉文件的恰當時機的時候,我們可以用到關鍵字with
# 普通寫法 file1 = open('abc.txt', 'a') file1.write('張無忌') file1.close() # 使用with關鍵字的寫法 with open('abc.txt', 'a') as file1: # with open('文件地址','讀寫模式') as 變量名: # 格式:冒號不能丟 file1.write('張無忌') # 格式:對文件的操作要縮進 # 格式:無需用close()關閉
6. readlines()和writelines()
file1 = open('scores.txt', 'r', encoding='utf-8') file_lines = file1.readlines() file1.close() print(file_lines) # 》》['羅恩 23 35 44\n', '哈利 60 77 68 88 90\n', '赫敏 97 99 89 91 95 99\n', '馬爾福 100 85 90']
readlines()
是“按行讀取”,readlines() 會從txt文件取得一個列表,列表中的每個字符串就是scores.txt中的每一行,而且每個字符串后面還有換行的\n符號
file1 = open('scores.txt', 'w', encoding='utf-8') file_lines = file1.writelines( ['羅恩 23 35 44\n', '哈利 60 77 68 88 90\n', '赫敏 97 99 89 91 95 99\n', '馬爾福 100 85 90']) file1.close()
writelines()
的參數可以是序列也可以是字符串
7. split()
split()
通過指定分隔符對字符串進行切片
a = 'jjj kkk lll' print(a.split(' ')) # 》》['jjj', 'kkk', 'lll'] b = input('請輸入內容').split(',') print(type(b)) # 》》<class 'list'>
join()
函數,是把字符串合並
a = ['c', 'a', 't'] b = '' print(b.join(a)) # 》》cat c = '-' print(c.join(a)) # 》》c-a-t
join()的用法是str.join(sequence),str代表在這些字符串之中,要用什么字符串連接,上面兩個例子,一個是空字符串,一個是橫杠,sequence代表數據序列,在這里是列表a
第16關 模塊
在模塊中,我們不但可以直接存放變量,還能存放函數,還能存放類
定義變量需要用賦值語句,封裝函數需要用def語句,封裝類需要用class語句,但封裝模塊不需要任何語句
之所以不用任何語句,是因為每一份單獨的Python代碼文件(后綴名是.py的文件)就是一個單獨的模塊
封裝模塊的目的也是為了把程序代碼和數據存放起來以便再次利用。如果封裝成類和函數,主要還是便於自己調用,但封裝了模塊,不僅能自己使用,文件的方式也很容易共享給其他人使用
所以,使用模塊主要有兩種方式,一種是自己建立模塊並使用,另外一種是使用他人共享的模塊
1. import語句
使用import
語句導入一個模塊,最主要的目的並不是運行模塊中的執行語句,而是為了利用模塊中已經封裝好的變量、函數、類
當導入模塊后,要使用模塊中的變量、函數、類,需要在使用時加上模塊.
的格式
# 這是模塊test.py print('正在學習模塊...') a = '我是模塊中的變量a' def hi(): a = '我是函數里的變量a' print('函數“hi”已經運行!') class Go2: a = '我是類Go2中的變量a' def do2(self): print('類Go2中的函數“do2”已經運行!')
# 這是主程序main.py import test # 導入test模塊 print(test.a) # 使用“模塊.變量”調用模塊中的變量 test.hi() # 使用“模塊.函數()”調用模塊中的函數 A = test.Go2() # 使用“變量 = 模塊.類()”實例化模塊中的類 print(A.a) # 實例化后,不再需要“模塊.” A.do2() # 實例化后,不再需要“模塊.”
下面是主程序main.py的執行結果↓
在學習模塊...
我是模塊中的變量a
函數“hi”已經運行!
我是類Go2中的變量a
類Go2中的函數“do2”已經運行!
當然test.py這種為了組織代碼而封裝的模塊是最好不要出現print('正在學習模塊...')這種執行語句,這里寫一下主要是為了體現什么時候需要模塊.
import語句還有一種用法是import…as…
,比如我們覺得import test太長,就可以用import test as t語句,意思是為“test”取個別名為“t”,上面的main.py文件可以寫成這樣↓
# 這是主程序main.py import test as t print(t.a) t.hi() A = t.Go2() print(A.a) A.do2()
當需要同時導入多個模塊時,可以用逗號隔開。比如import a,b,c可以同時導入“a.py,b.py,c.py”三個文件
2. from … import … 語句from … import …
語句可以從模塊中導入一個指定的部分到當前模塊,格式如下↓
當需要從模塊中同時導入多個指定內容,也可以用逗號隔開,寫成from xx模塊 import a,b,c的形式,上面的main.py文件也可以寫成這樣↓
# 這是主程序main.py from test import a from test import hi, Go2 print(a) # 打印變量“a” hi() # 調用函數“hi” A = Go2() # 實例化“Go2”類 print(A.a) # 打印實例屬性“a” A.do2() # 調用實例方法“do2”
對於from … import …語句要注意的是,沒有被寫在import后面的內容,將不會被導入
當需要從模塊中指定所有內容直接使用時,可以寫成【from xx模塊 import *】的形式,*代表“模塊中所有的變量、函數、類”
不過一般情況下,不要為了圖方便直接使用【from xx模塊 import *】的形式
這是因為,模塊.xx的調用形式能通過閱讀代碼一眼看出是在調用模塊中的變量/函數/方法,而去掉模塊.后代碼就不是那么直觀了
3. if __name__ == '__main__'
對於Python和其他許多編程語言來說,程序都要有一個運行入口
在Python中,當在運行某一個py文件,就能啟動程序 ——— 這個py文件就是程序的運行入口,例如剛才的main.py就是運行入口
更復雜的情況,也可以運行一個主模塊,然后層層導入其他模塊
當有了一大堆py文件組成一個程序的時候,為了【指明】某個py文件是程序的運行入口,可以在該py文件中寫出這樣的代碼
# 【文件:xx.py】 代碼塊 ①…… if __name__ == '__main__': 代碼塊 ②……
這句話的意思是這樣的↓
這里的if __name__ == '__main__'
就相當於是 Python 模擬的程序入口。Python 本身並沒有規定這么寫,這是一種程序員達成共識的編碼習慣
4. 借用模塊
之前見過的time模塊和random模塊是Python的系統內置模塊,也就是說Python安裝后就准備好了這些模塊提供使用
如果是第三方編寫的模塊,需要先從Python的資源管理庫下載安裝相關的模塊文件
下載安裝的方式是打開終端,Windows用戶輸入pip install + 模塊名;蘋果電腦輸入:pip3 install + 模塊名,點擊enter即可。(需要預裝python解釋器和pip)
比如爬蟲時會需要用到requests這個庫(庫是具有相關功能模塊的集合),就需要在終端輸入pip3 install requests(Mac用戶)的指令
Python標准庫官方文檔:https://docs.python.org/zh-cn/3.8/library/index.html
借用模塊重點關注的是模塊中的函數和類方法有什么作用,然后把使用案例做成筆記慢慢積累
例如random
模塊的關鍵知識(也就是比較有用的函數和類方法),可以做成這樣的筆記↓
import random # 調用random模塊 a = random.random() # 隨機從0-1之間(包括0不包括1)抽取一個小數 print(a) a = random.randint(0, 100) # 隨機從0-100(包括0和100)之間抽取一個數字 print(a) a = random.choice('abcdefg') # 隨機從字符串,列表等對象中抽取一個元素(可能會重復) print(a) a = random.sample('abcdefg', 3) # 隨機從字符串,列表等對象中抽取多個不重復的元素 print(a) items = [1, 2, 3, 4, 5, 6] # “隨機洗牌”,比如打亂列表 random.shuffle(items) print(items)
可以使用dir()
函數查看一個模塊,看看它里面有什么變量、函數、類、類方法
import random # 調用random模塊 print(dir(random)) # 》》['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', '_Sequence', '_Set', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_accumulate', '_acos', '_bisect', '_ceil', '_cos', '_e', '_exp', '_inst', '_log', '_os', '_pi', '_random', '_repeat', '_sha512', '_sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 'betavariate', 'choice', 'choices', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 'paretovariate', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']
這樣可以把模塊中的函數(函數和類方法)一覽無余地暴露出來。對於查到的結果"__xx__"結構的(如__doc__),它們是系統相關的函數,我們不用理會,直接看全英文的函數名即可
dir(x),可以查詢到x相關的函數,x可以是模塊,也可以是任意一種對象
a = '' # 設置一個字符串 print(dir(a)) # 把字符串相關的函數展示出來 # 》》['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] a = [] # 設置一個列表 print(dir(a)) # 把列表相關的函數展示出來 # 》》['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] a = {} # 設置一個字典 print(dir(a)) # 把字典相關的函數展示出來 # 》》['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
數據分析需要用到pandas和NumPy模塊,網頁開發要用到Django模塊
5. csv模塊csv
是一種文件格式,你可以把它理解成“簡易版excel”。學會了csv模塊,就可以用程序處理簡單的電子表格了
csv模塊的中英文文檔:https://yiyibooks.cn/xx/python_352/library/csv.html#module-csv
新建了一個名為test.csv的文件,里面的內容是這樣↓
csv讀取數據↓
import csv # 參數encoding='utf-8'防止出現亂碼 with open('test.csv', newline='', encoding='utf-8') as f: # 使用csv的reader()方法,創建一個reader對象 reader = csv.reader(f) # 遍歷reader對象的每一行 for row in reader: print(row) # 》》['商品編號', '商品名稱', '單價', '庫存', '銷量'] # 》》['1', '貓零食', '12', '3133', '5164'] # 》》['2', '普通貓糧', '33', '5055', '2231'] # 》》['3', '貓糧四合一', '187', '212', '334']
終端輸出的每一行信息都是一個列表
csv寫入數據
import csv with open('test.csv', 'a', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(['4', '貓砂', '25', '1022', '886']) writer.writerow(['5', '貓罐頭', '18', '2234', '3121'])
import time # 開始計時 start = time.time() # 格式化當前日期和時間 print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) # 倒計時,第一種寫法 for t in range(5, 0, -1): print('\r倒計時 ' + str(t) + ' 秒', end='', flush=True) time.sleep(1) print('') # 倒計時,第二種寫法 for t in range(5, 0, -1): info = '倒計時 ' + str(t) + ' 秒' print(info, end='') print('\b'*(len(info)*2), end='', flush=True) time.sleep(1) print('') # 結束計時 end = time.time() print('開始時間:', end='') # 格式化開始時間 print(time.strftime('%H:%M:%S', time.localtime(start))) print('結束時間:', end='') # 格式化結束時間 print(time.strftime('%H:%M:%S', time.localtime(end))) # 計算持續時間 print('持續了%d秒' % (end-start))
import os # 讀取本地文件test.txt的內容並賦值給變量a with open('test.txt', 'r') as f: a = f.read() # 在a的基礎上添加內容並寫入到test_new.txt with open('test_new.txt', 'w') as new: new.write(a+'Bewear') # 將test_new.txt重命名為test.txt,將其替代 os.replace('test_new.txt', 'test.txt') # os.getcwd() # 返回當前工作目錄 # os.listdir(path) # 返回path指定的文件夾包含的文件或文件夾的名字的列表 # os.mkdir(path) # 創建文件夾 # os.path.abspath(path) # 返回絕對路徑 # os.path.basename(path) # 返回文件名 # os.path.isfile(path) # 判斷路徑是否為文件 # os.path.isdir(path) # 判斷路徑是否為目錄
第17關 項目實操:收發郵件
負責發送郵件的smtplib模塊,和負責構造郵件內容的email模塊,這兩個都是Python內置模塊
1. smtplib模塊
要發送郵件,需要用到smtplib
模塊的以下方法↓
import smtplib server = smtplib.SMTP() server.connect(host, port) server.login(username, password) server.sendmail(sender, to_addr, msg.as_string()) server.quit()
SMTP
(Simple Mail Transfer Protocol)代表“簡單郵件傳輸協議”,SMTP 協議是由源服務器到目的地服務器傳送郵件的一組規則
可以簡單理解為:需要通過SMTP指定一個服務器,這樣才能把郵件送到另一個服務器smtplib.SMTP.connect(host,port)
就是在連接指定的服務器
host是指定連接的郵箱服務器,可以指定服務器的域名;port 是“端口”,一般情況下SMTP默認端口號為25。這兩個參數都可以到郵箱設置中找到
以QQ郵箱為例,SMTP服務器地址是:smtp.qq.com,端口是465或587
這里有兩種寫法,一是使用默認端口:25
import smtplib server = smtplib.SMTP() server.connect('smtp.qq.com', 25)
二是嘗試設置中的端口,比如465。這時會有些不同,QQ 郵箱采用的加密方式是SSL,需要寫成這樣↓
import smtplib server = smtplib.SMTP_SSL() # 如果端口是用SSL加密,需要這樣寫 server.connect('smtp.qq.com', 465) # 如果出現編碼錯誤UnicodeDecodeError,可以這樣寫:server.connect('smtp.qq.com', 465,'utf-8')
然后就要登錄郵箱了,login()
方法需要輸入兩個參數:登錄郵箱和授權碼
username = 'xxx@qq.com' password = '你的授權碼數字' server.login(username, password) # username:登錄郵箱的用戶名 # password:登錄密碼/授權碼
這個授權碼需要到這里獲取:打開https://mail.qq.com/,登錄郵箱,然后點擊位於頂部的【設置】按鈕,選擇【賬戶設置】,然后下拉到這個位置↓
把首個SMTP服務開啟。這時,QQ郵箱會提供一個授權碼,注意保護好這個授權碼↓
接着是發送郵件,sendmail()
方法需要三個參數:發件人,收件人和郵件內容
這里的發件人from_addr與上面的username是一樣的,都是登錄郵箱,所以只用設置一次
server.sendmail(from_addr, to_addr, msg.as_string()) # from_addr:郵件發送地址,就是上面的username # to_addr:郵件收件人地址 # msg.as_string():為一個字符串類型
msg.as_string()是一個字符串類型:as_string()是將發送的信息msg變為字符串類型
msg就需要用到email模塊了,之后說
最后是quit()
退出服務器
server.quit() # 退出服務器,結束SMTP會話
2. email模塊email
模塊是用來寫郵件內容的模塊。這個內容可以是純文本、HTML內容、圖片、附件等多種形式
每種形式對應的導入方式是這樣的↓
from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipartfi
按住command同時點擊mime,會看到一個名為__init__.py的空文件,這說明email其實是一個“包”
模塊(module)一般是一個文件,而包(package)是一個目錄,一個包中可以包含很多個模塊,可以說包是“模塊打包”組成的
Python中的包都必須默認包含一個__init__.py的文件
__init__.py控制着包的導入行為,假如這個文件為空,那么僅僅導入包的話,就什么都做不了,所以直接import email是行不通的,需要使用from … import …語句,從email包目錄下的【某個文件】引入【需要的對象】
關於模塊和包的概念可以看下這篇文章:https://www.cnblogs.com/ymjyqsx/p/6560210.html
還是以QQ郵箱為例發送一封純文本郵件,除了引入了MIMEText
,還要引入為了構建郵件頭的Header
建議代碼結構:①導入模塊 ②定義變量 ③使用方法
# smtplib 用於郵件的發信動作 import smtplib # email 用於構建郵件內容 from email.mime.text import MIMEText # 用於構建郵件頭 from email.header import Header # 發信方的信息:發信郵箱,QQ郵箱授權碼 from_addr = 'xxx@qq.com' password = '授權碼數字' # 收信方郵箱 to_addr = 'xxx@qq.com' # 發信服務器 smtp_server = 'smtp.qq.com' # 郵件正文內容 text = '''寒蟬凄切,對長亭晚,驟雨初歇 都門帳飲無緒,留戀處,蘭舟催發 執手相看淚眼,竟無語凝噎 念去去,千里煙波,暮靄沉沉楚天闊 多情自古傷離別,更那堪,冷落清秋節 今宵酒醒何處?楊柳岸,曉風殘月 此去經年,應是良辰好景虛設 便縱有千種風情,更與何人說? 柳永 《雨霖鈴·寒蟬凄切》 ''' # 實例化一個MIMEText郵件對象,第一個參數為正文內容,第二個參數為文本格式(plain為純文本),第三個參數為編碼 msg = MIMEText(text, 'plain', 'utf-8') # 郵件頭信息 msg['From'] = Header(from_addr) msg['To'] = Header(to_addr) msg['Subject'] = Header('python測試', 'utf-8') # 開啟發信服務,這里使用的是加密傳輸 server = smtplib.SMTP_SSL() server.connect(smtp_server, 465) # 登錄發信郵箱 server.login(from_addr, password) # 發送郵件 try: server.sendmail(from_addr, to_addr, msg.as_string()) print('恭喜,發送成功') except: print('發送失敗,請重試') # 關閉服務器 server.quit()
郵件頭信息的寫法比較特殊,以msg['Subject'] = Header('python測試', 'utf-8')這句來說
等號右邊是實例化了一個Header郵件頭對象,該對象需要寫入兩個參數,分別是郵件主題和編碼
等號左邊的msg['Subject']的變量是一個a['b']的代碼形式,它長得特別像字典根據鍵取值的表達,但是這里的msg是一個MIMEText類的對象,並不是一個字典
其實,字典和類在結構上,有相似之處↓
字典里面的元素是【鍵】和【值】一一對應,而類里面的【屬性名】和【屬性】也是一一對應的。可以根據字典里的【鍵】取到對應的【值】,同樣的,也可以根據類里面的【屬性名】取到【屬性】
所以msg['Subject']就代表着根據MIMEText類里面的Subject的屬性名取到該屬性
需要注意的是,不是每一個類都可以這樣訪問其屬性的,之所以能這樣訪問是因為MIMEText類實現了這個功能
所以,msg['Subject'] = Header('python測試', 'utf-8') 就是在為msg['Subject']這個屬性賦值
3. 報錯解決
報錯信息:ValueError: server_hostname cannot be an empty string or start with a leading dot.
Python 3.7版本之后修改了ssl.py,導致smtplib.SMTP_SSL也連帶產生了問題
解決方法↓
# 改動這句代碼 server = smtplib.SMTP_SSL() # 在括號內加入host參數 server = smtplib.SMTP_SSL(smtp_server)
如果用的是QQ以外的其他郵箱,比如企業郵箱的話,可能會出現報錯信息:smtplib.SMTPException: No suitable authentication method found
解決方案是在登錄(login)之前調用starttls()
方法就可以了
server.starttls()
還有些報錯實在搞不清楚是什么問題的話,可以嘗試將端口參數改一下,使用默認端口25
# 把這兩行代碼改掉 server = smtplib.SMTP_SSL() server.connect(smtp_server, 465) # 改掉port參數,並去掉_SSL server = smtplib.SMTP() server.connect(smtp_server, 25)
4. 群發郵件
將收件人信箱的變量設置成一個可以裝多個內容的列表↓
to_addrs = ['xxx@qq.com', 'xxxxx@qq.com']
需要注意的是,Header接受的第一個參數的數據類型必須要是字符串或者字節,所以在為msg['To']賦值的時候,需要將to_addrs轉換為字符串↓
msg['to'] = Header(",".join(to_addrs))
其他地方用到變量to_addr的地方替換成to_addrs就可以了
5. 制作二維碼-MyQR
第三方庫首先需要安裝,mac電腦在終端輸入pip3 install myqr
· 普通二維碼
from MyQR import myqr # 掃描二維碼后,顯示的內容或是跳轉的鏈接 myqr.run(words='https://www.cnblogs.com/oyster25')
在當前目錄會生成一個叫qrcode.png的圖片,就是我們要的二維碼了
· 藝術二維碼
在當前目錄有一張名為lm.jpg的圖片
只要在剛才的基礎上增加一個picture參數,會默認生成黑白的二維碼,如果想要有顏色,還要增加colorized=True這個參數
from MyQR import myqr myqr.run( words='https://www.cnblogs.com/oyster25', picture='lm.jpg', # 將QR二維碼圖像與圖片相結合 colorized=True # 黑白(False)還是彩色(True),默認是False )


· 動態二維碼
當前目錄的lm.gif圖片

from MyQR import myqr myqr.run( words='https://www.cnblogs.com/oyster25', picture='lm.gif', colorized=True )
會生成一個叫lm_qrcode.gif的動態二維碼myqr.run()
還有一些其他的參數↓
from MyQR import myqr import os myqr.run( words='https://www.cnblogs.com/oyster25', # 掃描二維碼后,顯示的內容或是跳轉的鏈接 version=10, # 設置容錯率,范圍是1-40 level='H', # 控制糾錯水平,范圍是L、M、Q、H,從左到右依次升高,默認為H picture='lm.gif', # 將QR二維碼圖像與圖片相結合 colorized=True, # 黑白(False)還是彩色(True),默認是False contrast=1.0, # 用以調節圖片的對比度,1.0 表示原始圖片。默認為1.0 brightness=1.0, # 用來調節圖片的亮度,用法同上。 save_name='Python.gif', # 控制輸出文件名,格式可以是 .jpg, .png ,.bmp ,.gif,默認輸出文件名qrcode.png或xx_qrcode.png(或.gif) save_dir=os.getcwd() # 控制存儲位置,默認是當前目錄 )
由於將容錯率version調高了,所以生成的會慢一些;然后改了輸出的文件名,會生成一個叫Python.gif的動態二維碼
6. 絕對值abs()和math.fabs()
import math # 內置模塊 math 求絕對值 print(math.fabs(-1.32)) # 》》1.32 # 內置函數 abs() 求絕對值 print(abs(-9)) # 》》9
第18關 編程思維:產品思維
1. 流程圖
所有的流程圖,總結起來有三種類型↓
用到的圖形樣式↓
有個在線畫流程圖的網站還挺好用的,就是只能免費做9張圖,網站鏈接:https://www.processon.com/diagrams
2. 產品設計
3. 滾動的廣告牌
方法一↓
import os, time def main(): content = '歡迎光臨穿着熊快餐車,喵喵喵~' for i in range(16): os.system('clear') print(content) content = content[1:]+content[0] time.sleep(0.25) if __name__ == '__main__': # 也可以直接用在函數上 main()
方法二↓
import time content = '歡迎光臨穿着熊快餐車,喵喵喵~' for i in range(16): print('\r'+content, end='', flush=True) content = content[1:]+content[0] time.sleep(0.25) print('')
第19關 項目實操:畢業項目
一些遍歷字典的方法↓
tv_dict = {'芒果台': '湖南衛視', '荔枝台': '江蘇衛視', '番茄台': '東方衛視'} # 直接遍歷是取字典的鍵 for tv in tv_dict: print(tv, end=' ') print('') # 》》芒果台 荔枝台 番茄台 # 遍歷字典取值 for value in tv_dict.values(): print(value, end=' ') print('') # 》》南衛視 江蘇衛視 東方衛視 # 遍歷字典取鍵值對 for logo, name in tv_dict.items(): print(logo + '是' + name) # 》》芒果台是湖南衛視 # 》》荔枝台是江蘇衛視 # 》》番茄台是東方衛視