來源: ApacheCN『USF MSDS501 計算數據科學中文講義』翻譯項目原文:Programming Patterns in Python
譯者:飛龍
協議:CC BY-NC-SA 4.0
現在我們已經了解了計算機如何組織數據,并進行一些低級編程操作,現在讓我們看一些常見的高級編程模式。 每一個這些操作都有一個使用條件和循環模式的實現,我們可以使用 python 語法很容易地表達。我們也可以使用現有的庫函數來實現相同的功能,我們也將探索它們。
當我們進行時,你會發現程序設計和編程是一個單詞關聯的游戲。 高級操作(例如
map
)應該在您的腦海中觸發偽代碼(類似英語的“代碼”)的循環模板,然后應該觸發
for-each
Python 代碼模板。
請記住 ,我們通過選擇和應用操作來設計程序,而不是特定的代碼序列。編程是設計過程中的最后一步,我們在這里獲得可執行文檔。因此,直觀地思考如何操作數據列表,或從數據中提取信息。我經常把事情畫出來或者把它們放到電子表格中,來幫助我想象。在紙上手動移動一些數據有助于我理解要執行的操作。
在設計了高級操作的序列或組合之后,我們可以用偽代碼或直接使用 Python 語法來寫出東西。隨著您獲得更多經驗,從操作直接轉到代碼將更容易,但我們仍然在使用操作而不是代碼來設計程序。對于復雜的問題,盡管有 35 年以上的編程經驗,我仍然寫出偽代碼。
編程模式
選擇程序的整體計劃時,程序員從一組模式或模板中提取。個別行動本身也是如此。程序員有一個常見操作目錄,他們在選擇計劃步驟時依賴他。
毫無疑問,您熟悉以下操作:
- 求和列表中的數字
- 計數列表中的數字
但我們可以將這些進一步抽象為:
- 查找滿足條件的列表中的所有值
- 對列表中的每個元素應用操作
- ......
操作越抽象,它就越廣泛適用。我們使用的操作類型部分取決于程序員的風格,但很大程度上受編程語言的能力及其預先存在的功能庫的影響。
數據練習
在我們瀏覽這些材料時,我們將做幾個小練習。 請將此 Python 代碼剪切并粘貼到您正在使用的筆記本中。
請嘗試在不查看練習描述正下方的解決方案的情況下進行練習。
UnitPrice = [38.94, 208.16, 8.69, 195.99]
Shipping = [35, 68.02, 2.99, 3.99, 5.94, 4.95, 7.72, 6.22]
names=['Xue', 'Mary', 'Bob']
Oscars = [
[1984, "A Soldier's Story", 0],
[1984, 'Places in the Heart', 0],
[1984, 'The Killing Fields', 0],
[1984, 'A Passage to India', 0],
[1984, 'Amadeus', 1],
[1985, "Prizzi's Honor", 0],
[1985, 'Kiss of the Spider Woman', 0],
[1985, 'Witness', 0],
[1985, 'The Color Purple', 0],
[1985, 'Out of Africa', 1]
]
Quantity = [6, 49, 27, 30, 19, 21, 12, 22, 21]
A = [
[1, 3, 10],
[4, -9, 0],
[2, 5, 8]
]
B = [
[7, 4, 0],
[4, 3, 1],
[1, 6, 8]
]
first=['Xue', 'Mary', 'Robert']
last=['Li', 'Smith', 'Dixon']
累積
讓我們從一個非常簡單但非常常見的模式開始,稱為累積。該模式遍歷一系列元素并累積一個值。例如,為了對序列中的數字求和,我們使用帶有
+
運算符的累加器運算。當我們遍歷序列時,我們更新一個初始化為 0 的流動總和:
在 Excel 中,這就像在單元格中使用
sum(...)
。在 Python 中,這種模式的實現有一個初始化步驟和一個循環:
sum = 0
for q in Quantity:
sum += q # same as: sum = sum + q
print(sum)
# 207
我們可以使用我們想要的任何其他算術運算符,例如
*
。實際上,我們可以使用任何帶有兩個操作數并返回新值的函數。 對于求和,函數的兩個“輸入”數字是先前的累計值和序列中的下一個值。該函數的結果是新的累計值。
+
和
*
是最常見的運算符。
您還將在 Hadoop 和 Spark 的分布式計算世界中,看到這個名為
reduce
(歸約)的操作,就像
map/reduce
那樣。
計數器
是累加器的一種特殊情況,它計算序列中元素的數量。它還使用
+
運算符,但兩個“輸入”數字是先前的累計值和固定的值 1,而不是序列中的下一個元素。
我們可以更新多個流動的累計值,而不只是一個。例如,假設我們想要計算序列中偶數和奇數值的數量。我們需要兩個累加器,從零開始,但操作是相同的:
+1
表示每一步應用的“向累加值加 1”的操作,但僅在當前值為偶數或奇數時才應用。在 Python 代碼中,我們將執行以下操作:
even = 0
odd = 0
for q in Quantity:
if q % 2 == 0: even += 1 # % is mod operator
else: odd += 1
print(even, odd)
# 4 5
映射
也許最常見的操作是,將一個序列映射到另一個序列,將運算符或函數應用于每個元素。(它就像一個累加器,它累積了一個項目列表,而不是一個單獨的值。)例如,使用電子表格創建一個新列,包含 5% 折扣的單價,在電子表格中如下所示:
我們可以使用列表字面值,在時間 0 處表示兩個列表:
UnitPrice = [38.94, 208.16, 8.69, 195.99]
Discounted = [] # empty list
換句話說:
從高級操作到偽代碼到代碼,我們頭腦中的翻譯過程是 單詞關聯 游戲。當你的大腦看到將一列數據映射到另一個數據的操作時,請考慮“映射”。當你的大腦聽到“映射”,它應該生成適當的偽代碼循環,適當地填充片段。當你的大腦聽到“對于每個等等等等”時,請考慮“哦,for-each 循環”并使用適當的編程模式:
Discounted = [] # empty list
for price in UnitPrice:
Discounted.append(price * 0.95)
print(Discounted)
# [36.992999999999995, 197.75199999999998, 8.2555, 186.1905, 20.691, 6.308, 6.935, 40.62199999999999, 131.23299999999998]
請注意,我已將空列表的初始化包含在此代碼段中。原因是我們真的想在心理上,將初始化與此代碼模式相關聯。
即使在微觀層面,也要考慮將操作映射到代碼。例如,當我想到“將
x
添加到列表
y
”時,我的大腦會將其轉換為
y.append(x)
。
用于映射操作的列表推導式
這是一種常見的模式,Python 有一個顯式結構,使事情變得更容易。它被稱為 列表推導式 ,它實際上只是簡寫,看起來更像數學集符號:
Discounted = [price * 0.95 for price in UnitPrice] # a list comprehension
print(Discounted)
# [36.992999999999995, 197.75199999999998, 8.2555, 186.1905, 20.691, 6.308, 6.935, 40.62199999999999, 131.23299999999998]
練習
在不查看我們剛才所做的代碼的情況下,嘗試使用列表推導來為映射操作編寫代碼,該列表推導將價格除以 2,再次將值放在
Discounted
中。
不要剪切/粘貼。輸入它!
Discounted = [price/2 for price in UnitPrice] # a list comprehension
print(Discounted)
# [19.47, 104.08, 4.345, 97.995]
練習
給定一個名稱列表,
['Xue', 'Mary', 'Robert']
,用代碼實現一個映射操作,將名稱轉換為
namelens
列表,它包含名稱長度。提示:函數調用
len('Xue')
返回 3。
不要剪切/粘貼。輸入它!
names = ['Xue', 'Mary', 'Robert']
namelens = [len(name) for name in names]
print(namelens)
# [3, 4, 6]
組合
讓我們看一下代碼模式,一次遍歷兩個列表,將結果放在第三個列表中。例如,要計算銷售交易的成本,我們將數量乘以單位價格。在電子表格中,如下所示:
將該公式拖到“成本”列中,將公式應用于以下行,從而填充新列。
以編程方式,我們正在做的是將兩個不同的序列的 第 i 個 元素相乘,并將結果放在輸出序列的 第 i 個 位置:
一開始,我們有以下數據:
Quantity = [6, 49, 27, 30, 19, 21, 12, 22, 21]
UnitPrice = [38.94, 208.16, 8.69, 195.99, 21.78, 6.64, 7.3, 42.76, 138.14]
遍歷多個列表時,我們通常需要使用索引循環而不是 for-each 循環:
Cost = []
for i in range(len(Quantity)): # from 0 to length of Quantity-1, inclusively
Cost.append( Quantity[i] * UnitPrice[i] )
print(Cost)
# [233.64, 10199.84, 234.63, 5879.700000000001, 413.82000000000005, 139.44, 87.6, 940.7199999999999, 2900.9399999999996]
用于組合操作的列表推導式
或者,更好的是,使用列表推導式:
Cost = [Quantity[i] * UnitPrice[i] for i in range(len(Quantity))]
print(Cost)
# [233.64, 10199.84, 234.63, 5879.700000000001, 413.82000000000005, 139.44, 87.6, 940.7199999999999, 2900.9399999999996]
格式化技巧:
f"{3.14159:.2f}"
# '3.14'
[f"{c:.2f}" for c in Cost]
'''
['233.64',
'10199.84',
'234.63',
'5879.70',
'413.82',
'139.44',
'87.60',
'940.72',
'2900.94']
'''
[round(c,2) for c in Cost]
# [233.64, 10199.84, 234.63, 5879.7, 413.82, 139.44, 87.6, 940.72, 2900.94]
請注意,您可能想在列表推導中使用兩個
for
循環,但是您可能獲得數量的每個值和價格的每個值的叉乘。這不是我們想要的,正如你在這里看到的:
print( [q*p for q in Quantity for p in UnitPrice] ) # WRONG!
'''
[233.64, 1248.96, 52.14, 1175.94, 130.68, 39.839999999999996, 43.8, 256.56, 828.8399999999999, 1908.06, 10199.84, 425.81, 9603.51, 1067.22, 325.35999999999996, 357.7, 2095.24, 6768.86, 1051.3799999999999, 5620.32, 234.63, 5291.7300000000005, 588.0600000000001, 179.28, 197.1, 1154.52, 3729.7799999999997, 1168.1999999999998, 6244.8, 260.7, 5879.700000000001, 653.4000000000001, 199.2, 219.0, 1282.8, 4144.2, 739.8599999999999, 3955.04, 165.10999999999999, 3723.8100000000004, 413.82000000000005, 126.16, 138.7, 812.4399999999999, 2624.66, 817.74, 4371.36, 182.48999999999998, 4115.79, 457.38, 139.44, 153.29999999999998, 897.9599999999999, 2900.9399999999996, 467.28, 2497.92, 104.28, 2351.88, 261.36, 79.67999999999999, 87.6, 513.12, 1657.6799999999998, 856.68, 4579.5199999999995, 191.17999999999998, 4311.780000000001, 479.16, 146.07999999999998, 160.6, 940.7199999999999, 3039.08, 817.74, 4371.36, 182.48999999999998, 4115.79, 457.38, 139.44, 153.29999999999998, 897.9599999999999, 2900.9399999999996]
'''
zip
函數
我們也可以使用
zip
,這是一個非常有用的函數,它從每個列表中提取一個值,以便在每次迭代時生成一個元組:
Cost = []
for q,u in zip(Quantity,UnitPrice):
Cost.append( q * u )
print(Cost)
# [233.64, 10199.84, 234.63, 5879.700000000001, 413.82000000000005, 139.44, 87.6, 940.7199999999999, 2900.9399999999996]
或者,使用列表推導式:
Cost = [q * u for q,u in zip(Quantity,UnitPrice)]
print(Cost)
# [233.64, 10199.84, 234.63, 5879.700000000001, 413.82000000000005, 139.44, 87.6, 940.7199999999999, 2900.9399999999996]
分割
組合的反面是分割,我們將流拆分為兩個或更多個新的流。 例如,我經常需要將列表中的全名拆分為它們的名字和姓氏。 在電子表格中,我們創建一個空白列:
然后按空格字符拆分(在 Excel 中,使用
Data > Text to Columns
)獲得兩個新列:
我們可以使用 組合 操作與字符串連接運算符“撤消”此拆分,該操作符將名字和姓氏組合成一個包含全名的新流。
拆分的另一個常見用途是接受表示一串數字的字符串,并將其拆分為包含這些數字的列表:
'1 2 3'.split(' ')
# ['1', '2', '3']
'1 2 3'.split(' ')
# ['1', '2', '', '', '', '3']
values = '1 2 3'.split(' ')
[int(v) for v in values]
# [1, 2, 3]
我們甚至可以遍歷列表來獲得矩陣(列表的列表)
rows = [
'1 2 3',
'3 4 5'
]
matrix = [[int(v) for v in row.split(' ')] for row in rows]
matrix
# [[1, 2, 3], [3, 4, 5]]
from lolviz import *
objviz(matrix)
切片
到目前為止,我們檢查過的大多數操作,都會產生與輸入序列大小相同的列表或序列,但是有許多操作會產生數據的子集。第一個這樣的操作是
slice
,它提取列表的子集。(同樣,在這里我明確使用術語“列表”,來指示切片通常作用在適合內存的數據結構上。)
切片操作是兩個值的函數,列表中的開始和結束位置。
names=['Xue', 'Mary', 'Bob']
print(f"Length {len(names)}")
print(names[0:1])
print(names[0:2])
print(names[0:3])
print(names[2:3])
'''
Length 3
['Xue']
['Xue', 'Mary']
['Xue', 'Mary', 'Bob']
['Bob']
'''
警告 :大多數語言和庫假設結束切片位置是排除的,這意味著在這種情況下切片從位置 0 到位置 3。 為了使事情變得更復雜,Python 而不是 R,從 0 開始計數而不是 1。在這方面很難在 Python 和 R 之間來回切換,因此最好在這里強調一下,以便記住它。
過濾
用于從列表或序列中提取數據的最常用操作稱為
過濾
。 例如,使用 Excel 的過濾機制,我們可以對
Shipping
列過濾少于 10 美元的值:
過濾操作類似于映射操作,因為計算應用于輸入流的每個元素。映射將一個函數應用于序列的每個元素,并創建一個相同大小的新序列。過濾用特定條件測試每個元素,如果為真,則將該元素添加到新序列。
過濾操作只是一個映射,有條件地將元素添加到目標列表:
Shipping = [35, 68.02, 2.99, 3.99, 5.94, 4.95, 7.72, 6.22]
Shipping2 = []
for x in Shipping:
if x < 10:
Shipping2.append(x)
print(Shipping2)
# [2.99, 3.99, 5.94, 4.95, 7.72, 6.22]
用于過濾操作的列表推導式
但是,我們應該使用推導式理解的變體,我們在映射操作模式中看到它:
Shipping2 = [x for x in Shipping if x < 10]
print(Shipping2)
# [2.99, 3.99, 5.94, 4.95, 7.72, 6.22]
我們也可以過濾一列,但將每行中的數據保持在一起。這是一個使用 Excel 的例子,它從被提名者列表中過濾奧斯卡獲獎者(條件是
winner
等于 1
):
Oscars = [
[1984, "A Soldier's Story", 0],
[1984, 'Places in the Heart', 0],
[1984, 'The Killing Fields', 0],
[1984, 'A Passage to India', 0],
[1984, 'Amadeus', 1],
[1985, "Prizzi's Honor", 0],
[1985, 'Kiss of the Spider Woman', 0],
[1985, 'Witness', 0],
[1985, 'The Color Purple', 0],
[1985, 'Out of Africa', 1]
]
objviz(Oscars)
循環結構的代碼看起來像(直接跳到列表推導式形式):
Oscars2 = [movie for movie in Oscars if movie[2]==1]
print(Oscars2)
objviz(Oscars2)
# [[1984, 'Amadeus', 1], [1985, 'Out of Africa', 1]]
輸出是列表的列表,每個電影為一行的過濾表。
注意我們如何測試
movie [2]
行中的一個列值,但是將整行添加到新表中。 如果我們只是將
winner
列值添加到新列表中,我們最終會得到一個列表:
1, 1, 1, ..., 1
。
練習
將
Oscars
列表中,所有具有 3 個單詞的標題的電影,過濾為
Oscars2
。 要將字符串分成單詞列表,請使用
title.split('')
。如果該列表的長度“len()”為3,則將整行復制到
Oscars2
。
Oscars2 = [movie for movie in Oscars if len(movie[1].split(' '))==3]
print(Oscars2)
# [[1984, "A Soldier's Story", 0], [1984, 'The Killing Fields', 0], [1985, 'The Color Purple', 0], [1985, 'Out of Africa', 1]]
練習
我們還可以在列表推導中使用表達式,而不僅僅是引用迭代值
x
。如果少于 10 美元,請使用列表推導的過濾變體,將
Shipping
中的運費加倍。將結果放在列表
Shipping2
中。
Shipping2 = [x*2 for x in Shipping if x < 10]
print(Shipping2)
# [5.98, 7.98, 11.88, 9.9, 15.44, 12.44]
練習
給出列表
names=['Xue', 'Mary', 'Bob']
,對于那些以
X
開頭的名字,將列表過濾為
names2
。 回想一下
name [i]
在
name
中產生第
i
個字符。或者使用
name.startswith(...)
。
names=['Xue', 'Mary', 'Bob']
names2 = [name for name in names if name.startswith('X')]
print(names2)
# ['Xue']
搜索
過濾操作查找序列中滿足特定條件的所有元素,但通常我們想知道哪個元素首先(或最后)滿足條件。(或者,我們通常只需要知道是否存在特定元素。)這將我們帶到
搜索
操作。最常見的是,搜索返回序列中的第一個(或最后一個)位置,而不是該位置的值。如果我們有位置,通常稱為
索引
,我們總是可以請求序列中該位置的值。如果未找到該元素,則搜索返回無效索引
-1
。
在某種意義上,搜索是過濾的變體。不同之處在于,我們在找到條件為真的元素時所做的事情。搜索不再將該元素添加到目標列表中,而是退出循環。
first=['Xue', 'Mary', 'Robert'] # our given input
target = 'Mary' # searching for Mary
index = -1
for i in range(len(first)): # i is in range [0..n-1] or [0..n)
if first[i]==target:
index = i
break
print(index)
# 1
無論循環類型如何,
break
語句都會打破閉合的循環。
搜索操作甚至可以在字符串(字符列表)中使用,來找到感興趣字符的位置。 例如,要將全名切割為名字和姓氏,我們可以將搜索空格字符與兩個切片操作組合在一起。 給定全名
Xue Li
,搜索空格字符將返回第四個位置或索引 3。要提取第一個名稱,我們將從索引 0 切片到索引 3,僅排除右側。 為了獲得姓氏,我們從索引 4 切換到 6,僅排除右側。
這就是 python 的樣子:
name = 'Xue Li'
# SEARCH
index = -1
for i in range(len(name)):
if name[i]==' ':
index = i
break
print(f"Index of space is {index}")
# SLICE
print(f"First: {name[0:index]}")
print(f"Last: {name[index+1:]}") # or name[index+1:len(name)]
'''
Index of space is 3
First: Xue
Last: Li
'''
為了確定字符串結尾的索引,程序員傾向于使用字符串的長度。長度可以是一個索引,其值是字符串末尾的后一個值,這是使用排除性右索引時,我們想要用于切片的。
嵌套循環
我們有時需要重復重復的指令,我們稱之為 嵌套循環 。在分析領域,嵌套循環非常重要,因為我們使用嵌套循環來處理矩陣,圖像和數據表。
處理矩陣
回想一下,雖然我們人類可以同時查看整個矩陣,但計算機會逐個檢查每個元素。 看看這個 3x3 矩陣:
我們可以使用列表的列表在 python 中表示:
A = [
[1, 3, 10],
[4, -9, 0],
[2, 5, 8]
]
因為這不是一維數據結構,所以我們不能使用簡單的“對于矩陣中的每個元素”循環來檢查每個元素。 迭代
nrows x ncols
矩陣的所有元素的最常見模式如下所示:
for i in 0..nrows-1:
for j in 0..ncols-1:
do something with matrix[i,j]
其中
matrix[i, j]
訪問行
i
和列
j
的元素。 這樣的嵌套循環給出了
i
和
j
的所有可能組合,這是我們在矩陣上操作時所需要的。考慮以下該模板到 Python 的轉換,打印出所有二維索引:
nrows = 3 # or len(A)
ncols = 3 # or len(A[0]) length of first row
for i in range(nrows):
for j in range(ncols):
print( i, j )
'''
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2
'''
注意列
j
值如何比行
i
值變化得更快。我們可以通過改變循環次序來反轉這種遍歷順序:
for j in range(ncols):
for i in range(nrows):
print(i, j)
'''
0 0
1 0
2 0
0 1
1 1
2 1
0 2
1 2
2 2
'''
j
循環在外部,它的變化速度比內部
i
循環慢。
用于獲得所有組合的雙重
for
列表推導
列表推導中的雙重循環給出了所有組合,在這種情況下是我們想要的。以下代碼示例創建坐標的字符串表示的列表:
coords = [f"{i},{j}" for i in range(nrows) for j in range(ncols)]
print(coords)
# ['0,0', '0,1', '0,2', '1,0', '1,1', '1,2', '2,0', '2,1', '2,2']
現在,讓我們使用累加器來求和 3x3 矩陣的所有元素,我們讓
nrows = 3
和
ncols = 3
并使用加法運算:
sum = 0
for i in range(nrows):
for j in range(ncols):
sum = sum + A[i][j]
print(sum)
# 24
處理圖像
在這個課程的圖像項目中,我們為圖像做了很多有趣的事情。 圖像只不過是二維矩陣,其條目是從 0 到 255 的像素灰度值。像素值 0 是黑色而 255 是白色。 例如,如果我們放大圖像,我們會看到二維矩陣的各個元素(稱為像素):
圖像和矩陣之間的唯一區別是,我們通常使用
x
和
y
坐標而不是行和列來訪問圖像的元素。訪問圖像的每個元素的嵌套循環的模板如下所示:
for x in 0..width-1:
for y in 0..height-1:
process pixel[x,y]
由于
y
坐標的變化快于
x
坐標,因此內部循環遍歷垂直條形。外部循環將
x
移動到右邊的下一個垂直條紋。
練習
作為一個更現實的例子,讓我們將兩個矩陣
A
和
B
相加為
C
。關鍵操作是將
A[i,j]
加上
B[i,j]
來獲得
C[i,j]
。 在視覺上,它看起來像這樣:
寫出嵌套的 Python 索引循環,將兩個矩陣相加,
C = A + B
。這里有一些定義可以幫助您入門:
nrows = ncols = 3
A = [
[1, 3, 10],
[4, -9, 0],
[2, 5, 8]
]
B = [
[7, 4, 0],
[4, 3, 1],
[1, 6, 8]
]
# Use list comprehension to init list of lists
C = [[0]*ncols for i in range(nrows)]
A = [
[1, 3, 10],
[4, -9, 0],
[2, 5, 8]
]
B = [
[7, 4, 0],
[4, 3, 1],
[1, 6, 8]
]
# Use list comprehension to init list of lists
C = [[0]*ncols for i in range(nrows)]
for i in range(nrows):
for j in range(ncols):
C[i][j] = A[i][j] + B[i][j]
print(C)
# [[8, 7, 10], [8, -6, 1], [3, 11, 16]]
import numpy as np
np.array(A) + np.array(B)
'''
array([[ 8, 7, 10],
[ 8, -6, 1],
[ 3, 11, 16]])
'''
警告:不要這樣做:
D=[[0]*ncols]*nrows
objviz(D)
D[0][1] = 3
D
# [[0, 3, 0], [0, 3, 0], [0, 3, 0]]
# compare to this:
objviz([[0]*ncols for i in range(nrows)])
練習
我們也可以使用嵌套 for-each 循環,而不是使用索引循環(這非常適合于矩陣),像我們迄今為止所做的那樣。 試試吧。
給出一個名字列表,
first=['Xue', 'Mary', 'Robert']
和姓氏列表,
last=['Li', 'Smith', 'Dixon']
,寫 一個嵌套的 Python 循環,用于打印名字和姓氏的每個組合。
first=['Xue', 'Mary', 'Robert']
last=['Li', 'Smith', 'Dixon']
for f in first:
for l in last:
print(f+' '+l)
'''
Xue Li
Xue Smith
Xue Dixon
Mary Li
Mary Smith
Mary Dixon
Robert Li
Robert Smith
Robert Dixon
'''
練習
現在使用雙重循環列表推導式重復該練習。
print([f+' '+l for f in first for l in last])
# ['Xue Li', 'Xue Smith', 'Xue Dixon', 'Mary Li', 'Mary Smith', 'Mary Dixon', 'Robert Li', 'Robert Smith', 'Robert Dixon']
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
