本文持續更新…
我的目錄
- 說明
- 第一部分:python基礎知識
- 0 python風格
- 1 字符串,string
- 2 數學運算符
- 3 註釋
- 4 列表
- 4.1 修改,添加,刪除, 彈出元素等
- 4.2 排序,確認長度等
- 4.2.1 永久排序sort()
- 4.2.1 * (補充)numpy排序和倒序
- 4.2.2 臨時排序sorted()
- 4.2.3 倒着打印列表reverse()
- 4.2.4 返回某個數在列表中的數量count()
- 4.2.5 返回元素的索引值index()
- 4.2.6 返回最大值,最小值以及對應的索引max(),index(max())
- 4.2.7 拷貝列表x到y範圍a[x:y]
- 4.2.8 集合去除重複元素set()
- 4.3 列表相加
- 5 元組
- 6 字典
- 7 實參和形參
- 7.1位置實參
- 7.2關鍵字實參
- 7.3 參數設置默認值--默認參數
- 7.4 傳遞任意數量的實參\*星號\*args---建立空元組
- 7.5 傳遞任意數量的鍵值對實參---兩個星號**---建立空字典\*\*kwargs
- 7.6 參數調用常見錯誤
- 錯誤1:SyntaxError: positional argument follows keyword argument
- 錯誤2:non-default argument follows default argument
- 7.7 學會在參數中使用None
- 8類
- 9 文件讀取和異常try-except處理
- 10 os.path路徑問題
- 11 input輸入和print輸出
- 12 @符號--裝飾器decorator和計時器time.time
- 13 矩陣矢量數組等乘法
- 14 文件保存
- 15 time計時
- 16 全局變量和局部變量
- 16python是動態語言
- 17 使用latex標註
- 18, 壓縮解壓文件
- 19, python其它常見函數
- 20 matplotlib畫圖
- 21 插值
- 22 積分
- 23 any()和all()
- 第二部分:python應用項目
- 第三部分:python常見錯誤
- 第四部分:python巧妙應用
- 第五部分:python例題
- 第六部分:python提高運算速度的方法
- 第七部分:數據結構與算法
- 第八部分: 常用模塊
說明
本博文作爲筆者筆記使用,不會很全面(主要課本是:python編程從入門到實踐)
第一部分:python基礎知識
0 python風格
0.1 命名風格
通常python命名只能使用字母,數字和下劃線
- 變量名:1)可以以字母或下劃線開頭,不能以數字開頭。2)變量名中間不能有空格如’my car =’ 3)不能使用python已有的關鍵字和變量名 4)儘量簡短而具有描述性:name比n好,name_length比length_of_persons_name好
- 常量: 爲了區分常量和變量,通常用大寫字母表示常量,如NAME=‘Dylan’,如果有多個單詞,用下劃線分開,如MY_NAME=‘Dylan’
- 類:建議使用駝峯命名法—即單詞首字母大寫,不使用下劃線。如類ElectricCar(),這個類的兩個單詞首字母都大寫,中間不用下劃線。
- 實例,模塊和類下定義的函數/方法:都使用小寫字母格式,並且單詞之間用下劃線。所謂實例即調用函數或者類進行實例化,而模塊即爲.py文件。如一個名爲electric_car.py的模塊(文件)裏面有一個Car()類,類裏面有一個get_car_model()的函數(方法)。
如:模塊electric_car.py文件如下
'''類'''
class ElectricCar(): #駝峯命名法
'''函數/方法'''
def get_car_model(self):
pass
my_car = ElectriCar() #實例
0.2 空行與空格
0.2.1 空格和tab
一定要注意空格與tab的區別,出現unindent does not match any outer indentation level的錯誤提醒是因爲在函數或者類中有四個空格又有tab,這兩者是不能同時出現來混用的(也就是,四個空格不等於一個tab),所以要麼全部用空格,要麼全部用tab
0.2.2 類中的空行問題
在一個類中,通常使用一個空行來分離不同的方法;而在一個模塊中(一個文件,如car.py),通常使用兩個空行來分離不同的類。一般而言,在class Car():類定義完之後,使用’’‘xxx’’‘來描述類的作用,如果不加描述,也空一行再定義初始化’def __init__()’
1 字符串,string
1.1 字符串大小寫
字符串大小寫由 title(),upper(),lower() 函數解決
my_str = "hello world"
print(my_str.title()) #首單詞大寫
print(my_str.upper()) #全部大寫
print(my_str.lower()) #全部小寫
Hello World
HELLO WORLD
hello world
1.2 合併字符串’+’
使用“+”號完成
surname = "Wu"
givename = "DL"
full_name = surname+' '+givename #單引號中間空一格
print(full_name)
Wu DL
1.2 空白
空白只首行空四格(\t, tab),換行(\n)
surname = "Wu"
givename = "DL"
full_name = surname+'\t'+givename
print('hello\n'+full_name)
hello
Wu DL
1.3 刪除空白strip()
使用strip函數(strip:/strɪp/,除去,去掉)
surname = " Wu" #左邊有一個空格
givename = "DL " #右邊有一個空格
full_name = surname+' '+givename
print(full_name)
print(full_name.rstrip())
print(full_name.lstrip())
print(full_name.strip())
Wu DL #左右兩邊都有一個空格(我將圖片展示在下,藍色是字符部分)
Wu DL #左邊有一個空格,右邊沒有
Wu DL #左邊沒有空格右邊有一個空格
Wu DL #左右兩邊都沒有空格
注意:用strip函數刪除空格是沒有改動原始存儲中的字符串的,也就是說當再次print(full_name)的時候空格是依然在的。如果要徹底去掉空格,並將原始的存儲中的空格也去掉,可以重新在定義。
surname = " Wu"
givename = "DL "
full_name = surname+' '+givename
print(full_name.strip())
print(full_name)
full_name = full_name.strip() #重新再定義
print(full_name)
Wu DL #左右都沒有空格
Wu DL #左右都有空格,沒有改變原始存儲
Wu DL #左右都沒有空格
應用:這種去空格方法最常用於在用戶輸入用戶名時,如果電腦想要記住用戶名,那麼需要預處理,預處理即去掉用戶可能輸入的空格。
1.4 轉義字符’'以及路徑中的r
轉義字符“\”
目的:輸出“Hello”,有引號
print(""Hello"") #企圖用4個引號
print("\"Hello\"") #在\後的第一個符號是被轉義了,即第2,3個被轉義了。
print(""Hello"")
^
SyntaxError: invalid syntax #第一種方法語法錯誤
"Hello" #正確
在輸入路徑時,如
data = np.loadtxt(r'\user\my_file.txt')
通常在路徑前面加了r,因爲有時候,特別是windows會將路徑中的斜槓理解爲轉義字符,爲了避免這種衝突,將r加在路徑的最前面。
1.5 字符串拆分成列表split()
sent = 'my name is wudl'
print(sent.split())
['my', 'name', 'is', 'wudl']
按其他字符劃分split(‘字符’),如
xxx.split('\\') 按\\來劃分
應用見下面的第四部分第二節
1.6 .join()合併
參考博文join函數
2 數學運算符
- 加:+
- 減:-
- 乘:*
- 除:/
- 冪:**
- 取整除://
- 取餘數(求模運算符):%
1.7 list()單詞拆分成字母
3 註釋
註釋的意義:強烈建議新手養成寫註釋的習慣,兩方面1)闡述代碼要做什麼 2)闡述如何做(當然建議項目加一個README.txt文件)
- 單行:#
- 多行:前後各3個單/雙引號
4 列表
4.1 修改,添加,刪除, 彈出元素等
修改
names = ['my','name','is','wudl'] #通常列表是有多個元素,因此名爲爲複數形式
names[0] = 'her' #修改列表第一個元素
print(names)
['her', 'name', 'is', 'wudl']
添加append()
names = ['my','name','is','wudl']
names.append('her') #在末尾增加元素
print(names)
['my', 'name', 'is', 'wudl', 'her']
插入insert()
names = ['my','name','is','wudl']
names.insert(0,'her')
print(names)
['her', 'my', 'name', 'is', 'wudl']
刪除del
names = ['my','name','is','wudl']
del names[0]
print(names)
['name', 'is', 'wudl']
根據名字移除remove()
names = ['my','name','is','wudl']
names.remove('name')
print(names)
['my', 'is', 'wudl']
默認情況下,remove刪除第一個期望的數,如要刪除1
值得注意的是,如果要刪除所有的1,則不能用循環再採用remove一個個刪除,因爲remove刪除一個數之後排序會改變(排序減1,因爲已經刪除了一個數了,列表變短,但remove刪除是按照索引順序進行的,這就使得有些數沒被刪(因爲索引變了)
可以看到最後還有個1沒被刪。
我們可以採用先找出1所在的索引,然後再通過np.delete來刪除(np.delete可以刪除行和列,具體看官網),找索引可以看後面的章節,這裏直接給全部代碼
彈出pop()
這個函數在應用中很有幫助,這個函數默認彈出最後一個元素,因此比如在購買東西的時候,可以知道最後一個是什麼,直接彈出來即可;此外還可以直接用彈出的元素,顯示它的一些特徵,如價格等。
names = ['my','name','is','wudl']
last_ele = names.pop()
print(names)
print(last_ele)
['my', 'name', 'is']
wudl
彈出任意位置的元素
names = ['my','name','is','wudl']
last_ele = names.pop(1)
print(names)
print(last_ele)
['my', 'is', 'wudl']
name
4.2 排序,確認長度等
4.2.1 永久排序sort()
按字母正順序
names = ['my','name','is','wudl']
names.sort()
print(names)
['is', 'my', 'name', 'wudl']
值得注意的是,上面names.sort()排完之後names列表自動變成排完之後的順序,原始的順序已經被覆蓋。
- 1, 如果有大小字母,那麼先排序大寫字母,再排小寫字母(因爲大寫字母的ASCII值要小)
names = ['My','Name','is','wudl']
names.sort()
print(names)
['My', 'Name', 'is', 'wudl']
- 2, 如果第一個字母相同,那麼開始對比第二個字母,第三個,。。。
a = ['abd','abc']
a.sort()
>>> a = ['abc', 'abd']
- 3, 如果第一個字母相同,第二個字母一個存在一個不存在,那麼不存在的排前
a = ['ab','abc']
b = ['ab', 'ab '] #空格
a.sort()
b.sort()
>>>['ab', 'abc']
>>>['ab', 'ab ']
按字母反順序,加參數reverse=True(實際上是找到ASCII值,然後從大到小排列)
names = ['my','name','is','wudl']
names.sort(reverse=True)
print(names)
['wudl', 'name', 'my', 'is']
有大寫字母的情況
names = ['My','Name','is','wudl']
names.sort(reverse=True)
print(names)
['wudl', 'is', 'Name', 'My']
4.2.1 * (補充)numpy排序和倒序
排序
np.sort(array)
逆序
在排完序之後,使用[::-1],這個[::-1]的含義請參考[::-1]的含義
4.2.2 臨時排序sorted()
希望排完序之後對原始列表無影響
names = ['my','name','is','wudl']
print(sorted(names))#注意是sorted(names)不是names.sorted()
['is', 'my', 'name', 'wudl']
4.2.3 倒着打印列表reverse()
注意這裏說的是直接將列表反轉,不是按字母順序反排序
names = ['my','name','is','wudl']
names.reverse()
print(names)
['wudl', 'is', 'name', 'my']
4.2.4 返回某個數在列表中的數量count()
返回列表中’wudl‘的數量
names = ['Dylan','wudl','Wudl','WUdl','Asle','Abdul']
print(names.count('wudl'))
1
可以看到這是區分大小寫的,但是如果希望WUdl,wudl, Wudl都是同樣的,那麼可以先將names統一小寫,然後在計數
names = ['Dylan','wudl','Wudl','WUdl','Asle','Abdul']
names_low = [name.lower() for name in names]
print(names_low)
print(names_low.count('wudl'))
['dylan', 'wudl', 'wudl', 'wudl', 'asle', 'abdul']
3
4.2.5 返回元素的索引值index()
返回第一次出現時的索引值
names = ['my','name','is','wudl','name']
print(names.index('name'))
1
可以看到,當一旦找到第一個name之後就結束了,不再找第二個;
返回所有指定的元素的索引
可以藉助enumerate(枚舉)函數,enumerate函數的用法見enumerate的用法
names = ['Dylan','wudl','Willion','Abdul','wudl']
indexs = [index for index,name in enumerate(names) if name=='wudl']
print(indexs)
[1, 4]
如果是numpy類型,更簡單的方法是
- 整型np.where(yourarr, value)
- 浮點型np.where(np.isclose(yourarr,value))
- 這兩個方法同樣適用於高維數組
4.2.6 返回最大值,最小值以及對應的索引max(),index(max())
nums = [9,1,20,8,4,22,40,20]
print(max(nums))
print(min(nums))
print(nums.index(max(nums)))
40
1
6
4.2.7 拷貝列表x到y範圍a[x:y]
通過[x:y]設定拷貝的起始索引x和最後索引y,以下是跟直接賦值的差別。
輸出a,b,c的id
可以看到賦值沒有產生副本,而是關聯到列表中,建立索引聯繫(類似鏈接);而使用[:]拷貝則產生了新的副本,可以對副本進行操作而不影響以前的列表
同樣不使用append,使用改變值的方式
可見對副本列表沒有改變。
下面考慮數組是否有這樣的功能
注意,由於數組沒有append功能,以下我通過改變第一個值替代append
import numpy as np
a = np.array([1,2,3,4])
b = a
c = a[:]
a[0]=9
print(a)
print(b)
print(c)
雖然c的id不一樣,但是依然會改變c副本的值。因此對於數組而言,在使用[x:y]進行拷貝副本時可以先轉爲列表再拷貝,完成之後再轉爲數組,如下:
import numpy as np
a = np.array([1,2,3,4])
b = a
c = list(a)[:]
a[0]=9
print(a)
print(b)
print(c)
此外可以參考下一節的深淺拷貝,可以通過拷貝來實現(爲了防止有多層數組,建議使用深拷貝):
import numpy as np
import copy
a = np.array([1,2,3,4])
b = a
c = copy.deepcopy(a)
a[0]=9
print(a)
print(b)
print(c)
字典沒有a[:]這種功能,不考慮字典
總結:對於a[x:y]產生副本而言,只對list生效
a[x:y]拷貝的應用及常見錯誤
拷貝在函數中的使用,當一個列表在函數中使用,而函數改變了原始列表後,原始列表將不復存在;而我們想要保留原始列表,方便後續使用時,可以使用[:]
拷貝副本,注意,數組則不能如此
def new_list(list_):
new_num = 8
list_.append(new_num)
return(list_)
origin_list = [1,2,3]
print('new_list= ',new_list(origin_list))
print('origin_list= ',origin_list)
可以看到原始列表改動了
def new_list(list_):
new_num = 8
list_.append(new_num)
return(list_)
origin_list = [1,2,3]
print('new_list= ',new_list(origin_list[:])) #輸入到函數中的是列表副本
print('origin_list= ',origin_list)
通過副本的形式沒有改變原始列表。
此外關於拷貝可以參考下一節。
數組不能如此,數組會改變原值,舉例:
a = [1,2,3,4]
b = a[:]
print(id(a))
b[0] = 5
print(a,b,id(a))
168394120
[1, 2, 3, 4] [5, 2, 3, 4] 168394120
而數組爲
a = np.array([1,2,3,4])
b = a[:]
print(id(a))
b[0] = 5
print(a,b,id(a))
168304688
[5 2 3 4] [5 2 3 4] 168304688
常見錯誤:
import numpy as np
def square(a):
a[0] = a[0]+1 #給形參的第一個值+1
return a**2
val = np.array([1,2])
print(square(val))
print(val)
將數組val直接傳遞給函數square想求平方,結果發現val的原始值變了,結果如下
[4 4] #求平方之後
[2 2] #val的值變了
所以要想val的值不變就需要考慮到拷貝的問題,可以使用深拷貝copy.deepcopy(),或者因爲這裏只有一層,所以也可以使用淺拷貝copy.copy(),但是不能使用val[:],因爲這是數組不是列表,會改變原始值
所以上面的內容改爲
val = np.array([1,2])
#print(square(val[:]))這樣的結果會改變原始的val值
print(square(copy.copy(val)))
#也可以使用copy.deepcopy(val)
print(val)
[4 4]
[1 2]
賦值,深淺拷貝copy(),deepcopy()和a[x:y]
關於a[x:y]參考上一節。
詳細內容可以參考這篇博文:Python.可變與不可變類型,傳遞,拷貝的整理:這篇博文在說淺拷貝時不正確,因此深淺拷貝可以參考下面這個博文深淺拷貝
這裏進行簡化和總結:
在說賦值和深淺拷貝之前,需要了解一下可變和不可變類型
可變類型(mutable)包括:列表,數組,字典
不可變類型(unmutable)包括:數字,字符串,元組,bool,None
解釋:
- 可變類型----以列表爲例,a=[1,2,3,4],(數組a=np.array([1,2,3,4])與列表一樣)現在改變元素a[0]=5,則a變爲[5,2,3,4],但是a的地址是不變的(可以通過id(a)來查看地址),變的是裏面的元素,所以叫做可變類型。字典跟列表相同。
- 不可變類型----以字符串爲例,a=Wudl,現在改爲a=Dylan,這裏並不是將Wudl改爲Dylan,而是重新開闢了一個內存空間,空間裏面放的值爲Dylan,地址已經改動了。所以是值不能變,變的是開闢了新的空間
- **補充:**地址id相當於房間號,內存空間相當於房間,對於不可變類型而言,事先爲這類型開闢了空間,所以id已經變了。而對於可變類型而言,是同一個房間,相當於一個房間裏面有四個電話,每個電話又分別聯繫到四個其他房間,這四個房間放的值爲1,2,3,4
賦值: 數據完全共享。將a賦值給b,那麼a和b的值是一樣的,a和b指向同一個內存地址
- 對於不可變類型而言,如下圖所示,當重新給a賦新的值時,b還是以前a的值。
a = 'wudl'
b = a
print(id(a),id(b))
a = 'dylan'
print(a,b)
print(id(a),id(b))
可見一開始a,b的地址是一樣的,當a賦新值之後地址變了,值也變了,因爲a指向了新的值。
- 對於可變類型而言,以列表爲例
a = [1,2,3,4]
b = a
print(id(a),id(b))
a.append(5)
print(a,b)
print(id(a),id(b))
當給a增加一個值時,可以看到a,b都增加了,因爲沒有開闢新的空間,而是在原來的房間上增加了一個新的電話指向5這個值(可以id(a[4])查看這個5的房間號);
同理,數組如下:
import numpy as np
a = np.array([1,2,3,4])
b = a
print(id(a),id(b))
a[0]=8
print(a,b)
print(id(a),id(b))
字典如下:
a = {'a':5,'b':8,'c':9}
b = a
print(id(a),id(b))
a['a']=0
print(a,b)
print(id(a),id(b))
如果是開闢新的空間的話(不在以前的列表/字典/數組上改變)那麼得到的結論是跟不可變類型一樣的,因爲是把列表看成一個整體
a = [1,2,3,4]
b = a
print(id(a),id(b))
a = [5,6,7,8]
print(a,b)
print(id(a),id(b))
深淺拷貝:參考博文深淺拷貝
淺拷貝指僅僅拷貝數據集合的第一層數據,深拷貝指拷貝數據集合的所有層。所以對於只有一層的數據集合來說深淺拷貝的意義是一樣的,比如字符串,數字,還有僅僅一層的字典、列表、元祖等.
淺拷貝copy
解釋上圖:淺拷貝表示將n1拷貝給n3時,第一層都拷貝了,可以理解爲第一層的地址完全拷貝給n3,而第二層以後的都沒有拷貝給n3,僅僅是將值拷貝了。類似於一個n1是一個大房間裏面有3個電話,每個電話指向一個房間,共3個房間,這些房間有的是直接放值的,有的又指向了另外的房間。所以淺拷貝表示另開了一個內存空間,拷貝了第一層的數據,即拷貝了3個電話,這3個電話指向3個房間的,因此表示拷貝了第一層的3個房間。注意n3是另開空間,所以這3個房間的值在n1中改變之後,在n3是不會改的。但後面的層的值都沒有拷貝過來而是直接指向以前的內存地址,所以這些房間的值一旦改變了n3也會改變,舉例如下
import copy
‘’‘
一個房間n1裏面有三個電話k1,k2,k3,這三個電話指向另外三個房間
房間裏的值爲wu,123和指向另外兩個房間,這另外兩個房間的值
爲alex,678
所以淺拷貝只拷貝了第一層的值,剩下的都是直接指向以前的內存地址
’‘’
n1 = {'k1':'wu','k2':123,'k3':['alex',678]}
n3 = copy.copy(n1) #淺拷貝
‘’‘
改變第一個房間k1的值,n3是不會改變的,因爲已經
另開空間拷貝這些值了
’‘’
n1['k1'] = 1
‘’‘
第三個房間K3指向的第一個房間改爲’000‘,
這樣n3會跟着改,因爲n3只是指向它,所以
房間值改了n3也會改
’‘’
n1['k3'][0] = '000'
print(n1,n3)
結果如下(因爲字典是沒有先後順序的,所以順序會改動)
{'k3': ['000', 678], 'k1': 1, 'k2': 123}
{'k3': ['000', 678], 'k1': 'wu', 'k2': 123}
深拷貝:屬於完全備份,開闢了一個新的空間來備份所有的值,所以更改n1對n3不會有任何改動,代碼同上,只將copy.copy()改爲deepcopy()
import copy
n1 = {'k1':'wu','k2':123,'k3':['alex',678]}
n3 = copy.deepcopy(n1)
n1['k1'] = 1
n1['k3'][0] = '000'
print(n1,n3)
結果爲:
{'k3': ['000', 678], 'k1': 1, 'k2': 123}
{'k3': ['alex', 678], 'k1': 'wu', 'k2': 123}
4.2.8 集合去除重複元素set()
集合在某些場合很重要,可以去除重複的元素。比如在計數的時候,重複的元素算一次,則set顯得很重要
4.3 列表相加
列表a,b直接相加表示在列表a末尾添加列表b,
因此不是元素相加,相當於給列表a拓展上列表b,因此也可以使用extend方法:
這跟數組相加不一樣,數組相加表示對應元素相加
5 元組
5.1 元組概況
元組的要點只有兩點:
- 元組裏的元素不可改變
- 但是元組是可以重新賦值的
以下來自元組
5.2 多維元組
1 二維元組
上面都是二維的元組,第一維元組是(1,2),第二維元組是(3,4)以及後來的(3,4,5,6)。當兩個維度一樣長時可以寫出來,如np.shape爲(2,2),但後面那個不一樣長,無法表達出來,就只寫了是(2,)表示二維。
2 三維元組
6 字典
6.1 空字典
空字典及賦值
a = {}
a['color'] = 'green'
print(a)
{'color': 'green'}
在應用中,可以將用戶的信息放在字典裏,如
建立一個用戶字典,輸入名字和性別保存到字典中
users = {} #建立空字典
active = True #建立while的標誌
while active:
name = input('\nWhat is your name? ')
gender = input('\nWhat is your sex/gender? ')
#保存用戶的性別,將性別對應保存到姓名中
users[name] = gender
#驗證信息
repeat = 'Please check your name and gender: '+name+': '+gender
print(repeat)
#檢查是否你是最後一個輸入,如果是則結束
ans = input('\nAre you the last one? (yes/no)')
if ans == 'yes':
active = False
print('\n------results-------')
print(users)
6.2 刪除鍵值對del
6.3 遍歷字典
1鍵值對
items()
函數將鍵值對輸出
a = {'color':'green',
'score':'10'}
for key, value in a.items():
print('\nkey: '+key)
print('\nvalue: ' +value)
>>>
key: score
value: 10
key: color
value: green
2遍歷建
a = {'color':'green',
'score':'10'}
for key in a.keys():
print('\nkey: '+key)
3,遍歷值
使用values()
函數
6.4 將列表變爲字典
x = ['a','b','c']
y = [1,2,3]
mydict = {}
for i in range(len(x)):
mydict[x[i]] = y[i]
print(mydict)
更方便的是直接調用庫函數
x = ['a','b','c']
y = [1,2,3]
mydict = dict(zip(x,y))
print(mydict)
例:有兩個列表,x表示橫座標,y表示縱座標,要求由x可以得到y。
可以將x,y變爲字典,由字典對應的x輸出y。
7 實參和形參
- 在函數定義中,括號()裏的參數是形參----函數完成工作所需要的信息
- 在函數調用中,括號()裏的參數是實參----調用函數時傳遞給函數的信息
7.1位置實參
通過位置一一對應來調用的方式,所以位置順序很重要如下面的代碼
def personal_message(name,gender): #形參
print('my name is '+name+', and my gender is '+gender)
personal_message('wudl','male') #實參的位置跟上面形參的對應,wudl對應name,male對應gender
>>>
>my name is wudl, and my gender is male
7.2關鍵字實參
假如不想對應位置,特別是參數比較多的情況下,則可以實參調用時寫上對應的參數名
def personal_message(name,gender):
print('my name is '+name+', and my gender is '+gender)
personal_message(gender='male',name='wudl')
可以看到,順序錯了也沒有問題
my name is wudl, and my gender is male
7.3 參數設置默認值–默認參數
假如我已經知道所有的人都是男性,那麼我可以不需要每個調用都寫male,而是在形參上直接寫上默認值,如下
def personal_message(name,gender='male'):
print('my name is '+name+', and my gender is '+gender)
personal_message('wudl') #只需要寫名字即可
my name is wudl, and my gender is male
但是上面需要記住,personal_message(name,gender='male')
括號裏面的參數不可以交換順序,因爲實參調用時,默認實參wudl
佔據第一個參數位置。
假如大量男性中出現了一個女性,那麼上面的默認值也是有效的,此時只要重新將實參gender加上即可,如
def personal_message(name,gender='male'):
print('my name is '+name+', and my gender is '+gender)
personal_message('lusy','female')
#or
#personal_message('lusy',gender='female')
my name is lusy, and my gender is female
7.4 傳遞任意數量的實參*星號*args—建立空元組
7.4.1 只有*
星號的形參
如果事先不知道要傳遞多少參數給函數,則可以使用帶星號*
的形參,星號意味着讓python創建一個名爲names的空元組(如下代碼),常用*args表示,結果以元組的形式輸出
def personal_message(*names):
print(names)
personal_message('lusy')
personal_message('lusy','wudl','Daly')
7.4.2 同時有*
星號和位置實參
同時存在位置實參,關鍵字實參和星號形參是比較常見的,也很實用,這個時候,通常建議將*
號參數放在最後,則默認先匹配位置實參和關鍵字實參最後剩下的都收集到星號的形參中
def personal_message(gender,*names):
print('gender: ',gender)
print('names: ',names)
personal_message('male','wudl','Daly','Abdule')
def personal_message(gender,age,*names):
print('gender: ',gender)
print('names: ',names)
print(age)
personal_message('male','18','wudl','Daly','Abdule')
注意:以下是錯誤寫法,我將在下節講解
def personal_message(gender,age,*names):
print('gender: ',gender)
print('names: ',names)
print(age)
personal_message(gender='male',age='18','wudl','Daly','Abdule')
7.4.3 同時有*
星號和位置實參
7.5 傳遞任意數量的鍵值對實參—兩個星號**—建立空字典**kwargs
使用兩個星號**讓python建立一個名爲user_info(如下代碼)的空字典,通常我們使用**kwargs
def build_profile(first,last,**user_info):
#創建空字典,有用戶的所有信息
profile = {}
profile['first_name'] = first
profile['last_name'] = last
for key,value in user_info.items():
profile[key] = value
return(profile)
user_profile = build_profile('deliang','wu',
age='22',
location='Beijing')
print(user_profile)
>>>
{'age': '22', 'location': 'Beijing',
'last_name': 'wu', 'first_name': 'deliang'}
我們輸入的鍵值對age=‘22’,location='Beijing’會傳遞到由兩個星號建立的空字典user_info中。
7.6 參數調用常見錯誤
錯誤1:SyntaxError: positional argument follows keyword argument
SyntaxError: positional argument follows keyword argument
這是常見的錯誤提示,內容爲:位置參數跟隨在關鍵字參數後面,意味着我們不能讓位置參數出現在關鍵字參數後面。舉例如下
def fun_(a,b):
print(a,b)
fun_(a=1,3) #關鍵字參數a=1出現在了位置參數3前面
分析: 我們在調用fun_的時候使用了(a=1,3)即用了關鍵字參數a=1,而剩下的3我們企圖用位置參數,但python解析的時候是無法這樣用的。這是由於關鍵字參數可以不按照順序出現,如fun_(b=1,a=3)這樣的a,b倒置是合法的。因此一旦出現fun_(a=1,3)這樣的寫法,則開始操作時,python不知道a=1該放在a的位置還是b的位置。因此正確的用法是fun_(a=1,b=3)
或者fun_(1,b=3),這種方式是1先定給了a,然後b=3再接着被分配.
def fun_(a,b):
print(a,b)
fun_(3,b=2) #將關鍵字參數放在最後是合法的
>>>
3 2
def fun_(a,b,c):
print(a,b,c)
fun_(3,b=2,4) #放在中間是不合法的,因爲無法知道b和c的位置
def fun_(a,b,c):
print(a,b,c)
fun_(3,2,c=4) #放在最後是合法的
錯誤2:non-default argument follows default argument
錯誤原因:“非默認參數跟隨默認參數”。意味着我們不能讓非默認參數跟隨在默認參數後面,如下所示,意味着非默認參數b不能跟在默認參數a=1後面。這是因爲python是按照順序來的,3默認佔了第一個位置即a=1的位置,但是a=1是默認參數,因此衝突,無法知道a=1還是3.
def fun_(a=1,b):
print(a,b)
fun_(3)
下面是合法的
def fun_(a,b=1): #3賦給第一個位置a,
print(a,b)
fun_(3)
>>>
3 1
7.7 學會在參數中使用None
有時候我們想要設置一個參數,但是這個參數的值我們不知道,沒有設定初始值,而又希望它存在,那麼可以設定爲None
1, 少參數
def fun_(a,b,c):
print(a,b,c)
fun_(3,2) #少一個位置參數c
2,設定爲None
def fun_(a,b,c=None):
print(a,b,c)
fun_(3,2)
>>>
3 2 None
3,改變None的初值
def fun_(a,b,c=None):
print(a,b,c)
fun_(3,2,c=4)
>>>
3 2 4
8類
8.0 類的調用
定義類的時候,一般對於python2而言,有括號,括號中放object,表示繼承了object類,而python3默認繼承object類,所以可以不放,可以參考python2和3類object區別
class Car(object): #python2
xxx
class Car or Car(): #python3
xxx
python3類中可以帶括號也可以不帶,但是調用的時候要有括號
c = Car()
c.xxx #調用Car()下的方法xxx
or
Car().xxx #要有括號
8.0* 函數調用
import numpy as np
def sin():
return np.sin(3)
def sin_f(a=sin()):
b = a*10
return b
print(sin_f())
注意:上面的a=sin()是放在sin_f的括號裏,如果放在函數下如:
def sin_f():
a=sin()
會報錯:a沒有事先聲明就調用
8.1修改類中屬性的三種方法
1,直接修改屬性的值
class User():
def __init__(self, first_name, last_name):
'''主方法,內涵first_name,last_name兩個形參'''
self.first_name = first_name
self.last_name = last_name
self.login_counter = 0 #登錄次數,初始化爲0
def read_login_counter(self):
print('The account has logged '+str(self.login_counter)+' times.')
user_a = User('dl','wu')
#user_a.login_counter = 3 #通過’句點表示法‘訪問並修改屬性的值
user_a.read_login_counter()
將上面註釋去掉之前
The account has logged 0 times.
去掉之後
The account has logged 3 times.
2,通過方法改變屬性的值
在類中定義的函數統稱爲方法,__init__()
也是方法,我們可以新建一個更新參數的方法
class User():
def __init__(self, first_name, last_name):
'''主方法,內涵first_name,last_name兩個形參'''
self.first_name = first_name
self.last_name = last_name
self.login_counter = 0
def read_login_counter(self):
print('The account has logged '+str(self.login_counter)+' times.')
def update_counter(self,num):
'''更新輸入次數的方法'''
self.login_counter = num
user_a = User('dl','wu')
user_a.update_counter(3)
user_a.read_login_counter()
>>>
>The account has logged 3 times.
例題,禁止屬性往回調
比如一輛汽車的里程是無法往回調的,只能往上調。這裏假如輸入次數也是如此,不能往下調。
class User():
def __init__(self, first_name, last_name):
'''主方法,內涵first_name,last_name兩個形參'''
self.first_name = first_name
self.last_name = last_name
self.login_counter = 5 #初始值設置爲5
def update_counter(self,num):
'''禁止屬性往下調'''
if num > self.login_counter: # 3小於5
self.login_counter = num
else:
print('禁止輸入次數下調')
user_a = User('dl','wu')
user_a.update_counter(3)
>>>
>禁止輸入次數下調
8.1.3 對屬性的值遞增
有時需要對屬性值遞增處理,定義一個函數
class User():
def __init__(self, first_name, last_name):
'''主方法,內涵first_name,last_name兩個形參'''
self.first_name = first_name
self.last_name = last_name
self.login_counter = 0
def read_login_counter(self):
print('The account has logged '+str(self.login_counter)+' times.')
def update_counter(self,num):
self.login_counter = num
def increment_counter(self,num):
'''遞增屬性值'''
self.login_counter += num
user_a = User('dl','wu')
user_a.update_counter(3)
user_a.increment_counter(2) #調用遞增
user_a.read_login_counter()
>>>
>The account has logged 5 times.
8.2 類的繼承和調用
類和屬性的調用都採用句點表示法,即中間使用句號。
參考類的繼承和調用
9 文件讀取和異常try-except處理
9.1 文件讀取和寫入–open方法
參考文件讀取
9.2 json讀取和寫入
在9.1節文件讀取中有一定的涉及寫入部分。寫入有多個方法,下面說幾個常用的方法
寫入爲json文件,使用json.dump寫入和json.load()讀取
1,json.dump寫入
import json
name = 'wudl'
filename = 'user.json'
with open(filename,'w') as f_obj:
json.dump(name, f_obj)
寫入的文件有
"wudl" #是str類型
2,json.load()讀取
import json
filename = 'user.json'
with open(filename) as f_obj:
username = json.load(f_obj)
print(username)
讀取一定要小心,這裏的user.json文件中存放的是str類型,如“wudl”,如果存放的是wudl沒有雙引號,則會報錯
9.3 異常
9.4 舉例:1,輸入用戶名驗證是否存在
加入你是遊戲玩家,現在你輸入用戶名登錄,假如你存在,則輸出打招呼語言歡迎回來,如果不存在則註冊並記住用戶名。
import json
def get_stored_user():
filename = 'user.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return(None)
else:
return(username)
def get_new_user():
'''註冊用戶'''
name = input('請輸入新賬號名: ')
filename = 'user.json'
with open(filename,'w') as f_obj: # w表示寫入
json.dump(name, f_obj) #寫入文件
return(name)
def greet_user():
'''查看用戶是否存在,不存在則註冊'''
user_name = get_stored_user()
if user_name:
print(user_name+' ,歡迎回來!')
else:
response = input('沒有此用戶,是否註冊?y/n')
if response == 'y':
user_name = get_new_user()
print('我們已經記住您的用戶名,歡迎進入遊戲!')
greet_user()
如果出現如下錯誤,請看9.2節
10 os.path路徑問題
提示:關於路徑也可以參考之前筆者寫的文章python路徑
10.1 os.path.dirname和os.path.dirname(__file__)返回路徑目錄
只返回路徑,不包括最後的文件(下面的test.py和test都被認爲是文件)
接着我們看一下__file__是什麼,
返回的是當前工作目錄下的絕對路徑,那麼就可以知道os.path.dirname(__file__)表示去掉最後一個文件即components.py後的路徑
10.2 os.path.join()路徑拼接
這裏.join()函數與os.path.join()是有區別的,前者可以參考前面第一部分的內容。
os.path.join()用於路徑的拼接
11 input輸入和print輸出
1, input函數
Input函數輸出的是str類型,而且兩個數直接沒有逗號隔開
因爲上面認爲數字是連着的,所以輸入的時候不能用逗號隔開,這樣對於多輸入的時候很不方便。但是可以使用如下的方法:
方法1,eval函數
實際上eval()函數只是將輸入的字符串變爲了元組,可以查看eval的用法
我們輸入的是字符串,經過eval函數轉變:
方法二: 使用.split()函數分開,再通過map構成我們想要的類型,但需要注意的是,此時要用空格隔開
12 @符號–裝飾器decorator和計時器time.time
@在python中用來表示裝飾器,通常的用法有三種(在矩陣中表示矢量乘積)
可以參考bilibili上的一個視頻,解釋得很詳細bilibili裝飾器(強烈建議去看視頻,作者介紹簡單易懂)
以下簡單講解
1, 函數沒有參數
我們現在來實現累計從[1,10)(不含10)的計算結果
import time
def sum_n():
t_start = time.time()
sum_ = 0
for i in range(10):
sum_ = sum_ + i
t_end = time.time()
print(t_end - t_start)
print(sum_)
sum_n()
從上面的式子看很可讀性很差,因爲計時是放在函數裏面的,現在我們優化函數,使得計時器放在外面。
def display_time(func): #decorator
def wrapper():
t_start = time.time()
func()
t_end = time.time()
print(t_end - t_start)
return wrapper
@display_time # call decorator
def sum_n():
sum_ = 0
for i in range(10):
sum_ = sum_ + i
print(sum_)
sum_n()
上面新定義了一個函數叫做display_time用於顯示時間,這個就是我們的裝飾器,裝飾器本質上是一個函數或類,概括的講,裝飾器的作用就是爲已經存在的對象添加額外的功能。
display_time()括號裏面的函數就是我們要調用的函數。
下面的wrapper就是我們要執行的內容,當我們調用sum_n()函數時(即最後一行)實質上沒有執行sum_n而是先執行sum_n()上面一行的@display_time轉到第一行函數,然後進入wrapper()裏面。執行計算時間,然後func()(注意,這裏func()就是我們的sum_n函數),再執行時間結束
結果如下
45
0.005215883255004883
2 函數帶返回值
3 函數帶有參數
當我們有參數時,只要在對應的wrapper和func裏面放參數即可,上面展示了一種方法,但是使用*args更好,表示sum_n裏面放多少參數則對應的一同放上去
換爲可傳遞字典
13 矩陣矢量數組等乘法
- 1 數組相乘對應點相乘,列表不能如此
- 2 常數n與列表相乘表示列表重複n次,數組直接相乘
問題:如下,如何將a中的1和b的第一部分[4,5,6]相乘,而2和[7,8,9]相乘得到2x3的數組?
分析:顯然不能直接相乘,因爲數組是對應相乘,即14,25,6沒有a的元素相乘,所以報錯。思路:使用矩陣?矩陣不行,因爲a變成矩陣是1x2,b是2x3,所以點乘是1x3。所以可以是用np.multiply
multiply表示的是元素相乘
其他更多的乘法見矩陣數組乘法; 矩陣和數組的區別
14 文件保存
參考筆者另一篇博文python保存文件
以後更新都在上面提到的博文上
15 time計時
見筆者另一篇博文time和timeit
16 全局變量和局部變量
python在函數可以使用全局變量也可以使用局部變量,python執行函數時會默認先尋找局部變量,如果局部變量沒有這個變量名,則在全局變量中找,如果局部和全局都有這個變量名,則函數會默認使用局部變量,只有當局部沒有這個變量,纔會使用全局變量。因此,
1)對於全局變量是不可變類型而言(數字,字符串,元組,bool,None),在函數中可以直接調用但如果修改的話,那麼會認爲是重新定義了一個局部變量,所以沒有修改全局變量的值
a = 4
b = 'name'
c = (1,2)
def func1():
print(a)
print(b)
print(c)
直接使用全局變量
func1()
a = 4
b = 'name'
c = (1,2)
def func1():
對不可變類型而言,在局部變量中改變全局變量的值,
默認是重新定義一個新的變量,只是名字和全局變量相同罷了
所以實際上全局變量沒有改變
a = 1
b = 'dylan'
print(a)
print(b)
print(a)
print(b)
func1()
print(a)
print(b)
注意,如果在局部變量中調用了變量名,則會出錯,這個時候可以聲明是全局變量(見下)
a = 4
b = 'name'
c = (1,2)
def func1():
a = a+1 #報錯,沒有事先聲明a就使用a
b = 'dylan'
print(a)
print(b)
2)如果要改變全局變量的值,可以在局部變量中事先申明變量是全局變量,使用關鍵字global
a = 4
b = 'name'
c = (1,2)
def func1():
global a,b 聲明是全局變量,則變量會改變
a = 1
b = 'dylan'
print(a)
print(b)
print(a)
print(b)
func1()
print(a)
print(b)
a = 4
b = 'name'
c = (1,2)
def func1():
global a,b
a = a+1 #聲明是全局變量,則不會報錯
b = 'dylan'
print(a)
print(b)
print(a)
print(b)
func1()
print(a)
print(b)
3) 可變類型如果只是改變其中元素,則不需要申明global可以直接修改全局變量
import numpy as np
a = [1,2,3]
b = {'a':8,'b':9}
c = np.array([4,5,6])
def func1():
a[0] = 'k'
b['a'] = 'dylan'
c[0]=20
print(a)
print(b)
print(c)
print(a)
print(b)
print(c)
func1()
print(a)
print(b)
print(c)
4)可變類型如果是要修改整個變量,則同樣需要聲明global
import numpy as np
a = [1,2,3]
b = {'a':8,'b':9}
c = np.array([4,5,6])
def func1():
這裏的局部變量a,b,c不是全局變量,只是名字相同罷了
不會影響全局變量的值
a = [3,2,1]
b = 'dylan'
c = np.array([10,11,12])
print(a)
print(b)
print(c)
print(a)
print(b)
print(c)
func1()
print(a)
print(b)
print(c)
5)常見錯誤
num = 3
def printnum():
print(num)
num = 4
print(num)
printnum()
print(num)
這裏會報錯,局部變量在聲明之前就使用了。因爲python優先在函數中找局部變量,發現找到了num=4這個變量,但在這個變量之前卻先print(num),所以出現錯誤
16python是動態語言
這裏參考視頻:python動態語言的三種表現形式
定義見下面的圖
表現形式有三種,建議看上面的視頻
17 使用latex標註
17.1 plt中的latex
在latex中,使用符號如的方法是
'$\\theta$' #轉義
or
r'$\\theta$'
原因是:在python中\是被佔用的,需要使用\來轉義,轉義的方法有兩種,一是用\,二是使用r。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-10,10,100)
y = np.sin(x)
plt.plot(x,y)
plt.xlabel('$\\theta$')
plt.ylabel('$\\beta$')
plt.title('$\\beta=sin(\\theta)$')
plt.show()
17.2 getdist中的latex
嘗試畫triangle plot的時候出現了問題
ValueError:
\\beta=sin(\\theta)
^
Unknown symbol: \ (at char 0), (line:1, col:1)
getdist比較奇葩,它不按照一般套路出牌,上面的兩種方法都不對,需要使用下面的方法:
'\\theta'
or
r'\theta'
實際上跟上面是一樣的,因爲latex必須在$下輸入,而這裏只不過在函數定義的時候已經給我們加了$ :見getdist
labels = ['r','A_s','\\alpha_s','\\beta_s','A_d','\\alpha_d','\\beta_d']使用\\
samples = MCSamples(samples=samps,names = names, labels = labels,ranges={'r':(0,None),
'As':(0,None)})
18, 壓縮解壓文件
18.1 常規方法:
1)使用tarfile
官網:tarfile
官網很全,這裏不一一翻譯,只給幾個常用的方法:
解壓tar文件:
import os, tarfile
def unpack_path_file(pathname):
archive = tarfile.open(pathname,'r:gz') 只讀方式打開gzip壓縮的文件
for tarinfo in archive:
archive.extract(tarinfo,os.getcwd())
archive.close()
unpack_path_file('00.tar.gz') 實例
解釋:
- archive.extract用法爲TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False);其中member是指解壓的文件,path指要解壓到的文件夾路徑,如果沒有,則默認到當前工作目錄
- os.getcwd()獲取當前工作目錄,由於默認到前面工作目錄,所以可以不寫
除上面的解壓文件功能外,還有如下常用功能。
判斷是否是壓縮文件
查看壓縮文件下有多少文件
表示在00這個壓縮文件下還有01.tar.gz, 02.tar.gz等等
獲取壓縮文件下的文件名
顯示壓縮文件下的文件信息(如果verbose=False則類似linux的ls -l命令)
解壓所有文件到指定路徑(我這裏指定的是當前工作目錄下的ext目錄)
添加文件到壓縮包中
需要用可寫方式打開壓縮包(添加lecture_all.pdf到壓縮文件中)
其他用法
例題可以參考官網這裏不述說了。
tarfile在命令行中使用
通過在終端輸入:
python -m tarfile --help
來查看幫助文件
python -m tarfile -c newtest.tar ext test.py 將ext文件夾和test.py壓縮到newtest.tar中
上面的newtest.tar也可以改爲任何壓縮形式,如newtest.tar.gz
python -m tarfile -e monty.tar other-dir/ 將文件提取到指定路徑
python -m tarfile -l monty.tar 顯示tar文件中的文件
2)使用zipfile
18.2 使用patool
參考文獻:https://github.com/wummel/patool,https://wummel.github.io/patool/,https://pypi.org/project/patool/
19, python其它常見函數
19.1 isinstance()函數
參考菜鳥教程
用法
isinstance(object,calssinfo)
- object:表示輸入的實例對象
- classinfo:基本類型,如int,float,bool,complex,str(字符串),list,dict(字典),set,tuple等,用於判斷object輸入的是否和此類型一致;一致則返回True,否則False
舉例:
19.2 assert函數
參考菜鳥教程
20 matplotlib畫圖
20.1 subplot子圖
參考筆者另一博文
21 插值
21.1 numpy線性插值numpy.interp()
21.2 scipy插值, scipy.interpolate()
參考python科學計算(第二版)3.7節。
舉書上的例子
from scipy import interpolate
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10,11)
y = np.sin(x)
plt.plot(x, y, '-ro')
# xnew = [0.2,1.8,3.5,6.8]
xnew = np.linspace(0,10,101)
for kind in ['nearest','zero','slinear','quadratic']:
f = interpolate.interp1d(x,y,kind=kind)
ynew = f(xnew)
plt.plot(xnew,ynew,label=str(kind))
plt.legend(loc='lower right')
上面的zero或者nearnest都是0階B樣插值,linear或者slinear是一階B樣插值,quadratic和cubic分別是二階和三階B樣條曲線。
插值的一個特點(與擬合的區別)是曲線一定會經過原始數據點。對於數據很亂,沒有規律顯然使用插值比擬合更好。
現在數據不是sin函數,而是隨意的值,如下,其他不變(加了‘cubic’),繼續使用上面的插值方法
y = [0, 2, 8, 3, 9, 5, 6, 7, 10, 2, 3, 8]
x = np.arange(len(y))
from scipy import interpolate
import numpy as np
import matplotlib.pyplot as plt
y = [0, 2, 8, 3, 9, 5, 6, 7, 10, 2, 3, 8]
x = np.arange(len(y))
plt.plot(x, y, '-ro')
# xnew = [0.2,1.8,3.5,6.8]
xnew = np.linspace(0, 10, 101)
for kind in ['nearest', 'zero', 'slinear', 'quadratic','cubic']:
f = interpolate.interp1d(x, y, kind=kind)
ynew = f(xnew)
plt.plot(xnew, ynew, label=str(kind))
plt.legend(loc='lower right')
**超出邊界:**如果插入的值xnew超出邊界,則會報錯。比如:
正常情況:插入的值xnew = 1.5,在(0,10)之間
import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np
x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y)
xnew = 1.5
ynew = f(xnew)
plt.plot(x,y,'-ro',xnew,ynew,'ko')
plt.show()
如果插值的值xnew = -1, 小於x[0],則報錯:
爲了解決這種情況,可以有兩種辦法:請查看官網解釋
方法1: 給邊界定值,當xnew<x[0]或者xnew>x[-1]時,給左右兩邊的邊界定值。
爲此,我只需要將bounds_error=False
,並將fill_value
設定爲邊界值。
比如我定義,當xnew<x[0]時,y=1;當xnew>x[-1]時,y=0.05,則程序如下:
import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np
x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y,bounds_error=False, fill_value=(1,0.05))
xnew = [-2,-1,12,15]
ynew = f(xnew)
plt.plot(x,y,'-ro',xnew,ynew,'ko')
plt.show()
方法2:對結果外推
上面的方法1適合在我們知道邊界值的情況,而如果我們想將邊界外推,則只需要將fill_value='extrapolate'
即可
import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np
x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y,bounds_error=False, fill_value='extrapolate')
xnew = [-2,-1,12,15]
ynew = f(xnew)
plt.plot(x,y,'-ro',xnew,ynew,'ko')
plt.show()
22 積分
22.1一重積分
參考官網一重積分
計算
import numpy as np
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
#計算一重積分,使用scipy自帶的函數
print('start quad at: ', ctime())
val2, err2 = quad(lambda x: x**2,0,2) #err2是誤差,使用了匿名函數,x的積分區間是0到2
print(val2)
print('end quad at: ', ctime())
#自定義一重積分
def myquad(startx,endx,stepx):
t = 0
for x in np.arange(startx,endx,stepx):
t = t + x**2*stepx
return t
print('start myquad at: ', ctime())
print(myquad(0,2,0.001))
print('end myquad at: ', ctime())
更一般性的,使用下面的方法:
#計算一重積分,使用scipy自帶的函數
def fun():
return lambda x:x**2
print('start quad at: ', ctime())
val2, err2 = quad(fun(),0,2)
print(val2)
print('end quad at: ', ctime())
間隔stepx越小精度越高;另外當精度一樣時,通過調用函數計算更快
22.2 二重積分
使用dblquad函數,第一個變量x的區間是a到b,第二個變量的區間可以是一個關於x的函數,通過x計算得到y的區間,這樣可以對x-y平面上的任何區間進行二重積分。
計算
import numpy as np
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
#使用scipy帶的積分函數
#第一個0,2表示x的積分上下限,第二個0,2表示y的上下限
#這裏使用了匿名函數lambda
def fun2():
return lambda y, x: (x*y+x**2+y**2)**(1/2)
print('start dblquad at: ',ctime())
val2,err2 = dblquad(fun2(),0,2,0,2)
print(val2)
print('end dblquad at: ',ctime())
# 通過循環自己計算積分
print('start mydblquad at: ', ctime())
def mydblquad(start,end,stepx,stepy):
t = 0
for x in np.arange(start,end,stepx):
for y in np.arange(start,end,stepy):
t = t + (x*y+x**2+y**2)**(1/2)*stepx*stepy
return t
print(mydblquad(0,2,0.001,0.001))
print('end mydblquad at: ', ctime())
從上面顯然可以看到調用函數的優勢,計算二重積分0秒,而循環要17秒。
注意:dblquad中y是第一個參數,x纔是第二個參數:
舉個簡單例子:求積分,x從0到2,y從0到1
上面手動解析積分得到:
在使用dblquad()時,按照上圖關於dblquad的定義,要求表達式func的定義爲:fun(y,x),即y是第一個參數,x是第二個參數,這樣在dblquad中a,b才表示x的範圍,gfun和hfun才表示y的範圍。
例2:存在第三個變量,但這個變量在for循環中,如:
x的積分從0到1,y的積分從0到2,最後得到的是關於的函數
源代碼
from scipy.integrate import dblquad
def test(y,x):
# global l
w = (l+x)**2
return x*y**2*w
k = []
for l in range(2,10):
val, err = dblquad(test, 0, 1, 0, 2)
k.append(val)
print(k)
例3:積分存在分母爲零的情況:爲了簡單,將上面的函數改爲
顯然對循環的時候,當都可以使得分母爲0;這種情況是不能避免的,通常也不會有這樣使得分母爲0的函數,比如可能直接從3開始循環,或者定義當分母時,令;我們採用後者:
from scipy.integrate import dblquad
def test(y,x):
w = l+x
if w == 0: #如果分母=0,直接令分母爲1
w = 1
return y**2/w
k = []
for l in range(1,10):
val, err = dblquad(test, -2, 1, 0, 2)
k.append(val)
print(k)
22.3 多重積分
參考官網,裏面的tplquad, nquad,這裏僅僅給一例加以說明
計算
這裏的範圍分別爲,則寫程序的時候一定要注意順序
def func():
return lambda x0,x1,x2,x3: x0+x1+2*x2+3*x3
print(nquad(func(),[[0,1],[2,4],[-1,5],[-2,3]])) #注意順序
(參數不一定要以等命名,可以是其它字符,如等)
22.4 被積函數含有其它變量
這在積分中很常見,可以參考官網,把它放到args
參數中,這個時候不對args
中的變量積分。如下例子,不會對a,b積分,所以只有一個變量,因此默認對積分,不用匿名函數lambda
以及這個例子
具體可以點上面給的鏈接
22.5 不使用匿名函數求積分
除了22.4節之外,其他節求積分都是通過關鍵字lambda
將被積函數變爲匿名函數;所以受22.4節啓發,可以將被積函數的被積變量作爲參數輸入。現在重新計算前幾節
22.5.1 一重積分
計算
import numpy as np
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
#定義被積函數
def integrand(x):
return x**2
#計算一重積分,使用scipy自帶的函數
print('start quad at: ', ctime())
val2, err2 = quad(integrand,0,2) #err2是誤差,使用了匿名函數,x的積分區間是0到2
print(val2)
print('end quad at: ', ctime())
22.5.2 二重積分
計算
import numpy as np
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
#使用scipy帶的積分函數
#第一個0,2表示x的積分上下限,第二個0,2表示y的上下限
#這裏使用了匿名函數lambda
def fun2(x,y):
return (x*y+x**2+y**2)**(1/2)
print('start dblquad at: ',ctime())
val2,err2 = dblquad(fun2,0,2,0,2)
print(val2)
print('end dblquad at: ',ctime())
22.5.3 被積函數含有多個函數
例1,計算
這裏x的範圍是[-2,2],y的範圍是[0,2],按照二重積分的定義,[a,b]是x的範圍,所以寫被積函數integrand(y,x)要先y再x
import numpy as np
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
def func1(x):
return x
def func2(y):
return y**2
def integrand(y,x): #注意,這裏是先y再x,
return func1(x)*func2(y)
print('start dblquad at: ',ctime())
val2,err2 = dblquad(integrand,-1,2,0,2) #x的範圍是[-1,2],y是[0,2]
print(val2)
print('end dblquad at: ',ctime())
例2,計算
其中是向量,表示點乘,分別如下:
所以上面的結果其實是
import numpy as np
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
def fun1(r):
return r
def fun2(r_prime):
return r_prime
def integrand(y,x):
return np.dot(fun1(np.array([x,y])),fun2(np.array([y,1])))
print('start dblquad at: ',ctime())
val2,err2 = dblquad(integrand,-1,4,1,2)
print(val2)
print('end dblquad at: ',ctime())
23 any()和all()
這一部分寫在此博客
第二部分:python應用項目
1 Web應用程序—Django入門
我是在windows進行這個項目的操作的,
1.1 創建項目
建立虛擬環境
python -m venv 11_env
激活虛擬環境
windows下
11_env\Scripts\activate
linux下
source 11_env/bin/activate
安裝Django
pip install Django
直接用pip而不是python -m pip install是因爲此時的pip已經是虛擬環境中的pip了,可以pip -V查看發現pip是按裝在虛擬環境中的。
在Django中創建項目
linux
django-admin.py startproject learning_log .
注意最後有一個點。將會建立一個新文件夾名爲learning_log
windows
如果使用上面的方法創建失敗,則使用下面命令
python xxxx\django\bin\django-admin.py startproject learning_log
xxx表示django-admin.py的存放路徑
創建數據庫
python manage.py migrate
如果manage.py報錯說不存在,那麼可能你多建了一個learning_log的文件夾,從裏面剪切過來即可,到目前爲止,我們的文件如下:learning_log文件夾下有11_env, learning_log, manage.py和運行上面命令新增加的db.sqlite3
2 python生成動態視頻
此部分參考公衆號量化投資與機器學習
效果是下面的動態形式
源代碼如下:公衆號代碼有小錯誤,下面代碼是修改之後的
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.animation as animation
from IPython.display import HTML
df = pd.read_csv('https://gist.githubusercontent.com/johnburnmurdoch/4199dbe55095c3e13de8d5b2e5e5307a/raw/fa018b25c24b7b5f47fd0568937ff6c04e384786/city_populations',
usecols=['name', 'group', 'year', 'value'])
fig, ax = plt.subplots(figsize=(15, 8))
colors = dict(zip(
['India', 'Europe', 'Asia', 'Latin America',
'Middle East', 'North America', 'Africa'],
['#adb0ff', '#ffb3ff', '#90d595', '#e48381',
'#aafbff', '#f7bb5f', '#eafb50']
))
group_lk = df.set_index('name')['group'].to_dict()
def draw_barchart(year):
dff = df[df['year'].eq(year)].sort_values(by='value', ascending=True).tail(10)
ax.clear()
ax.barh(dff['name'], dff['value'], color=[colors[group_lk[x]] for x in dff['name']])
dx = dff['value'].max() / 200
for i, (value, name) in enumerate(zip(dff['value'], dff['name'])):
ax.text(value-dx, i, name, size=14, weight=600, ha='right', va='bottom')
ax.text(value-dx, i-.25, group_lk[name], size=10, color='#444444', ha='right', va='baseline')
# ax.text(value+dx, i, dff'{value:,.0f}', size=14, ha='left', va='center')
ax.text(value+dx, i,'%.0f'%value, size=14, ha='left', va='center')
ax.text(1, 0.4, year, transform=ax.transAxes, color='#777777', size=46, ha='right', weight=800)
ax.text(0, 1.06, 'Population (thousands)', transform=ax.transAxes, size=12, color='#777777')
ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
ax.xaxis.set_ticks_position('top')
ax.tick_params(axis='x', colors='#777777', labelsize=12)
ax.set_yticks([])
ax.margins(0, 0.01)
ax.grid(which='major', axis='x', linestyle='-')
ax.set_axisbelow(True)
ax.text(0, 1.12, 'The most populous cities in the world from 1500 to 2018',
transform=ax.transAxes, size=24, weight=600, ha='left')
# ax.text(1, 0, 'by QIML', transform=ax.transAxes, ha='right',
# color='#777777', bbox=dict(facecolor='white', alpha=0.8, edgecolor='white'))
plt.box(False)
draw_barchart(2018)
animator = animation.FuncAnimation(fig,draw_barchart, frames=range(1960,2019))
HTML(animator.to_jshtml())
3 將新數組插入原始數組
如下,有原始數組(x,y),即每個x對應一個y,x的範圍是(0,10)間隔爲1,y是sin(x)。現在有兩個新的點,即[1.5,1.8],且它們的值都是1,如何將這兩個新點放入原始數組(x,y)中?要求x是遞增,即1.5要在1,2之間。
import numpy as np
import matplotlib.pylab as plt
x = np.arange(0,10)
y = np.sin(x)
xnew = [1.5,2.8]
ynew = [1,1]
plt.plot(x,y,'-')
plt.show()
我們可以通過將(x,y)生成字典對來解決這個問題,可以參考字典那一節。
import numpy as np
import matplotlib.pylab as plt
x = np.arange(0,10)
y = np.sin(x)
xnew = [1.5,2.8]
ynew = [1,1]
plt.plot(x,y,'-')
plt.show()
x_merg = list(x)+list(xnew) #將x和xnew組合
y_merg = list(y)+list(ynew) #將y和ynew組合
x_y_dic = dict(zip(x_merg,y_merg)) #構成字典
print(x_y_dic)
print(x_y_dic[1.5]) #輸出x=1.5對應的y
print(x_y_dic[2.8]) #輸出x=2.8對應的y
第三部分:python常見錯誤
1函數調用之後原始值變化
1,
2,cmb_map() missing 1 required positional argument: ‘self’
函數調用之後報錯說某某函數缺少一個必要的位置變量。
如在a.py中寫了一個函數
class CMB():
def __init__(self):
pass
def BB(self,nu,T):
return pysm.common.B(nu,T)
def cmb_map(self,config=None):
cmb_Map = hp.read_map(r'../data/psm/components/cmb/cmb_map.fits',
field=(0,1,2))
cmb_I, cmb_Q, cmb_U = cmb_Map[:]
return cmb_I, cmb_Q, cmb_U
現在在b.py中
from a import CMB
output = CMB.cmb_map()
報錯
這是因爲我們 使用的是類,我們在類中如果寫了self參數,在調用類的時候一定要打括號
改正如下:
from a import CMB
output = CMB().cmb_map() #加括號
第四部分:python巧妙應用
1 找出列表/數組/元組中某個字符的index及其拓展
1.1 返回某個字符的index
1.2 拓展:找出某個列表/元組中的某個子元組
如val的值如下,是一個三元組
val = ((1,2,3),({'name':'dylan'},('language','english')),('nside',1024))
假如第二個維度即({‘name’:‘dylan’},(‘language’,‘english’))是很長的一個組分,即
({'name':'dylan'},...('Alan',32)...,('language','english'))
由於組分太長,沒必要打開,或者打開很久,但是我知道里面肯定有(‘Alan’,32)這個元素;而我要在不打開的情況下找到Alan年齡多大(32歲)。
爲了簡單起見,我們只考慮裏面沒有其他多餘的成分,即val如下
1, 首先查看到val是三維數組
2,假如我們已知Alan是在第二組中,找出Alan(用列表解析)
以下可以不看
說這個的原因是在map處理中導入的時候頭文件是有NSIDE的,我們忘記NSIDE的時候可以找到。
NSIDE就在後面的元組中,
2,通過split設置輸出文件名
這部分是筆者筆記,可跳過
問題描述:現有文件數據,放在文件夾中,有兩種類型,small和large,這兩種類型下又分別含有名爲95_150mat.txt, 41_95_150mat.txt等文件,現在要求輸入的文件名對應爲95_150_small.pdf, 95_150_large.pdf等。比如有如下文件:
data_path = r'..\..\data\large\results\three\r_0.01\95_150_220mat.txt'
則輸出的文件名應該對應爲95_150_220_large.pdf
實際上,通過參考第一部分提到的split函數,基本已經可以完成這個任務。
通過下面的方法即可找到對應的字符串
保存的時候如下:
bands_str+'_'+fsky_scale+'.pdf'
完整的python code
from __future__ import print_function
import sys, os
sys.path.insert(0,os.path.realpath(os.path.join(os.getcwd(),'..')))
from getdist import plots, MCSamples
import getdist, IPython
import pylab as plt
print('GetDist Version: %s, Matplotlib version: %s'%(getdist.__version__, plt.matplotlib.__version__))
import numpy as np
def get_diagonal_element(matrix):
# get diagonal elements of a given matrix
mat = matrix
for i in range(np.shape(mat)[0]):
mat[i,i] = mat[i,i]**2
return mat
nsamp = 10000
np.random.seed(10)
data_path = r'..\..\data\large\results\three\r_0.01\95_150_220mat.txt'
data = np.loadtxt(data_path)
#data = np.loadtxt(r'C:\Users\Lenovo\Desktop\recent work\revise paper\data\small\results\two\r_0.01\95_150cov.txt')
data = get_diagonal_element(data)
cov = data
ndim = data.shape[0]
if 'large' in data_path:
mean_ = [0.01,0.91,-2.6,-3.14,315*0.53,-2.54,1.53] # large sky with fsky = 0.71
print('fsky:>>>> large')
else:
mean_ = [0.01,0.081,-2.6,-3.14,20*0.53,-2.54,1.53] # small sky with fsky = 0.05
print('fsky:>>> small')
samps = np.random.multivariate_normal(mean_, cov, size=nsamp)
names = ['r','As','alpha_s','beta_s','Ad','alpha_d','beta_d']
labels = ['r','A_s',r'\alpha_s','\\beta_s','A_d','\\alpha_d','\\beta_d']
samples = MCSamples(samples=samps,names = names, labels = labels,ranges={'r':(0,None),
'As':(0,None),
'Ad':(0,None)})
g = plots.getSubplotPlotter()
samples.updateSettings({'contours':[0.68,0.95,0.99]})
g.settings.num_plot_contours = 3
g.triangle_plot(samples,filled=True)
#g.triangle_plot([samples, samples2], filled=True)
bands_str = data_path.split('\\')[-1].split('m')[0]
fsky_scale = data_path.split('\\')[3]
g.export(bands_str+'_'+fsky_scale+'.pdf')
第五部分:python例題
1, 變位詞判斷
1,逐字對比
import time
def display_time(func):
def wrapper(*args):
t1 = time.time()
func(*args)
t2 = time.time()
print('time cost: %.6f'%(t2-t1))
return wrapper
@display_time
def anagram_solution(word1,word2):
w1_list = list(word1)
w2_list = list(word2)
if len(word1) != len(word2):
print(word1+' and '+word2+' are not anagram')
else:
for i in range(len(word1)):
stillOK = True
for j in range(len(word2)):
if w1_list[i] == w2_list[j]:
w2_list[j] = None
if stillOK == False:
print(word1+' and '+word2+' are not anagram')
if stillOK == True:
print(word1+' and '+word2+' are anagram')
print(w2_list)
a = 'typhon'
b = 'python'
anagram_solution('python','typhon')
2, 先排序,再對比
import time
def display_time(func):
def wrapper(*args):
t1 = time.time()
func(*args)
t2 = time.time()
print('time cost: %.6f'%(t2-t1))
return wrapper
@display_time
def anagram_solution(word1,word2):
if len(word1) != len(word2):
print(word1+' and '+word2+' are not anagram')
else:
w1_list = list(word1)
w1_list.sort()
w2_list = list(word2)
w2_list.sort()
stillOK = True
for i in range(len(w1_list)):
if w1_list[i] != w2_list[i]:
stillOK = False
print(word1+' and '+word2+' are not anagram')
break
if stillOK == True:
print(word1+' and '+word2+' are anagram')
a = 'typhon'
b = 'python'
anagram_solution('python','typhon')
3 通過計數來比較
import time
#import numpy as np
def display_time(func):
def wrapper(*args):
t1 = time.time()
func(*args)
t2 = time.time()
print('time cost: %.6f'%(t2-t1))
return wrapper
@display_time
def anagram_solution(word1,word2):
if len(word1) != len(word2):
print(word1+' and '+word2+' are not anagram')
else:
w1_ = [0]*26
w2_ = [0]*26
w1_list = list(word1)
w2_list = list(word2)
for i in range(len(w1_list)):
w1_[i] = ord(w1_list[i])-ord('a')
w2_[i] = ord(w2_list[i])-ord('a')
if sum(w1_) == sum(w2_):
print(word1+' and '+word2+' are anagram')
else:
print(word1+' and '+word2+' are not anagram')
a = 'typhon'
b = 'python'
anagram_solution('python','tophon')
第六部分:python提高運算速度的方法
參考筆者的另一博文
第七部分:數據結構與算法
1 棧stack
1.1 python實現棧的功能
棧有以下功能:
stack(): #創建空棧
push(item) #將元素推入棧頂,不返回信息
pop() #從棧頂移除一個元素並返回此元素
peek() #返回棧頂元素但不移除
is_empty() #測試是否是空棧,返回布爾值
size() #返回棧的大小
我們可以使用list來實現這個功能,可以選擇list的任意一端作爲棧的頂部,我們不妨選擇list的末尾作爲頂部,則
class Stack:
def __init__(self):
self.items = []
def push(self,item):
self.items.append(item)
def pop(self):
self.items.pop()
def peek(self):
return self.items[-1]
def is_empty(self):
return self.items == []
def size(self):
return len(self.items)
s = Stack()
print(s.is_empty())
s.push('Dylan')
s.push(22)
print(s.peek())
print(s.size())
2 隊列queue
2.1 python實現隊列的功能
class Queue:
def __init__(self):
self.items = []
def enqueue(self,item):
return self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def is_empty(self):
return self.items == []
def size(self):
return len(self.items)
3 遞歸
3.1 遞歸給序列求和
def list_sum_rec(numlist):
if len(numlist) == 1:
return numlist[0]
else:
return numlist[0] + list_sum_rec(numlist[1:])
print(list_sum_rec([1,2]))
3.2 遞歸實現進制轉換
def tostr(n,base):
convertString = '0123456789ABCDEF'
if n < base:
return convertString[n]
else:
return tostr(n//base,base) + convertString[n%base]
print(tostr(4,2))
3.3 遞歸調用turtle內置包畫圖
import turtle
t = turtle.Turtle()
def draw_spiral(t,line_len):
if line_len > 0:
t.forward(line_len)
t.right(90)
draw_spiral(t,line_len-5)
draw_spiral(t,100)
turtle.done()
3.4 遞歸實現階乘
def factorial(n):
if n < 2:
return 1
else:
return n*factorial(n-1)
print(factorial(3))
3.5 遞歸常見錯誤RecursionError
遞歸跟棧是緊密相連的,當調用函數時(遞歸實際上就是調用自身函數),系統會把調用時的現場數據(函數名,參數,返回值等)壓入到系統調用棧中。所以如果棧被填滿了,調用棧溢出,就會報錯
def tell_story():
print("從前有座山,山上有座廟,廟裏有個老和尚,他在講故事,他講的故事是:")
tell_story()
print("我給你講個故事吧:")
tell_story()
另一方面,如果棧幀數太少,遞歸層數太少,基本結束條件演進太慢,也會出錯。如上面的求和,如果調用棧幀只有1000個,那麼1002個數的求和就會報錯。
可以通過下面的方法查看和設定遞歸棧的層數:
import sys
sys.getrecursionlimit() 獲取棧層數
>>> 3000 python默認3000
sys.setrecursionlimit(1000) 設置層數爲1000
sys.getrecursionlimit()
>>> 1000
import numpy as np
def list_sum_rec(numlist):
if len(numlist) == 1:
return numlist[0]
else:
return numlist[0] + list_sum_rec(numlist[1:])
print(list_sum_rec(list(np.arange(1002))))
由於我設置層數爲1000,此時已經超過了層數(1002個數相加)
第八部分: 常用模塊
numpy
numpy random生成隨機數
參考筆者另一博文numpy生成隨機數