- 量化投資:交易模型開發與數據挖掘
- 韓燾
- 2626字
- 2020-04-03 12:57:00
2.3 量化平臺常用語言—Python
在量化平臺中,既有用戶比較熟悉的Python語言,也有現在用途不太廣泛的Matlab語言,當然,無論哪種語言,只有用戶能很好地應用才行。出于對系統維護升級的考慮,建議選擇Python語言進行開發。
2.3.1 Python簡介
Python語言的創始人吉多·范羅蘇姆(Guido van Rossum)是一名荷蘭計算機程序員。1989年范羅蘇姆在阿姆斯特丹,為了打發圣誕節的無趣,決心開發一個新的腳本解釋程序,來作為ABC語言的繼承。Python語言的名字被譯為大蟒蛇是因為范羅蘇姆很喜歡看一個英國電視節目“Monty Python”(飛行馬戲團),從20世紀90年代初Python語言誕生至今,它已被逐漸廣泛應用于系統管理任務的處理和Web編程中。
Python是由ABC、Modula-3、C、C++、Algol-68、SmallTalk、UNIX Shell及其他的腳本語言發展而來的。
Python是一個高層次的結合了解釋性、編譯性、互動性的面向對象的腳本語言,可以應用于Web開發、Internet開發、科學計算和統計、教育、桌面界面開發、軟件開發、后端開發等領域。而且在開發過程中沒有編譯環節。類似于PHP和Perl語言。可以在一個Python提示符(>>>)后直接執行代碼。相比其他編程語言經常使用英文關鍵字和一些特殊的標點符號而言,它更具有特色語法結構,是一種交互式語言。它支持面向對象的風格或將代碼封裝在對象的編程技術。
Python已經成為非常受歡迎的程序設計語言。從2004年以后,Python的使用率呈線性增長。在2019年3月TIOBE的編程語言排行榜中,Java繼續保持第一名,而Python超越C++排在第三名,如圖2-5所示。

圖2-5
2.3.2 量化基礎語法及數據結構
Python的基礎語法包括如下幾種。
1.Python標識符
在Python里,標識符由字母、數字、下畫線組成,所有標識符可以包括字母、數字及下畫線(_),但不能以數字開頭。Python中的標識符區分大小寫。
以下畫線開頭的標識符是有特殊意義的。
· 以單下畫線開頭(_foo),代表不能直接訪問的類屬性,需通過類提供的接口進行訪問,不能用from xxx import * 來導入。
· 以雙下畫線開頭(__foo),代表類的私有成員。
·以雙下畫線開頭和結尾(__foo__),代表Python里特殊方法專用的標識,如__init__()代表類的構造函數。
Python有5種標準的數據類型:Numbers(數字)、String(字符串)、List(列表)、Tuple(元組)和Dictionary(字典)。
Python支持4種不同的數字類型:int(符號整型)、long(長整型,也可以代表八進制數和十六進制數)、float(浮點型)和complex(復數)。
Python的字符串列表有兩種取值順序:從左到右索引默認是從0開始的,最大范圍是字符串長度減1、從右到左索引默認是從-1開始的,最大范圍是字符串開頭。
List(列表)是Python中使用最頻繁的數據類型。
列表可以完成大多數集合類的數據結構實現。它支持字符、數字、字符串,甚至可以包含列表(即嵌套)。列表用“[]”標識,是Python最通用的復合數據類型。列表中值的切割也可以用到變量 [頭下標:尾下標],從而可以截取相應的列表,從左到右索引默認從0開始,從右到左索引默認從-1開始,下標可以為空,表示取到頭或尾。
加號(+)是列表連接運算符,星號(*)表示重復操作,如表2-1所示。
表2-1

Python包含的方法及其描述如表2-2所示。
表2-2

元組是另一個數據類型,類似于列表。元組用“()”標識。內部元素用逗號隔開。但是元組不能進行二次賦值,相當于只讀列表。
Python的元組與列表類似,不同之處在于:元組的元素不能修改,元組使用小括號,列表使用方括號。元組內置函數及其描述如表2-3所示。
表2-3

字典是另一種可變容器模型,且可存儲任意類型的對象。字典用“{}”標識。字典是除列表外,Python中最靈活的內置數據結構類型。
列表是有序的對象集合,字典是無序的對象集合。兩者之間的區別在于:字典當中的元素是通過鍵存取的,而不是通過偏移存取的。
字典由索引(key)和它對應的值value組成。字典的每個鍵值(key/value)對用冒號(:)分隔,整個字典包括在大括號({})中。
Python字典包含的內置函數及其描述如表2-4所示。
表2-4

Python字典包含的內置方法及其描述如表2-5所示。
表2-5

2.Python數據類型轉換
有時候,我們需要對數據內置的類型進行轉換,進行數據類型的轉換,只需要將數據類型作為函數名即可。
表2-6中列舉的內置函數可以執行數據類型之間的轉換。這些函數將返回一個新的對象,表示轉換的值。
表2-6

3.Python運算符
Python中的運算符包括算術運算符、比較(關系)運算符、賦值運算符、邏輯運算符、位運算符、成員運算符、身份運算符、運算符優先級。
· Python算術運算符如表2-7所示(假設變量a為10,變量b為20)。
表2-7

· Python比較運算符如表2-8所示(假設變量a為10,變量b為20)。
表2-8

· Python賦值運算符如表2-9所示。
表2-9

· Python位運算符如表2-10所示。
表2-10

表2-10中變量a為60, b為13,二進制格式如下:
a = 00111100 b = 00001101 —————— a&b = 00001100 a|b = 00111101 a^b = 00110001 ~a = 11000011
· Python成員運算符如表2-11所示。
除了以上的一些運算符,Python還支持成員運算符,測試實例中包括了一系列的成員,包括字符串、列表或元組。
表2-11

4.Python循環語句
· Python提供了for循環和while循環(在Python中沒有do…while循環),如表2-12所示。
表2-12

· 循環控制語句可以更改語句執行的順序。Python支持的循環控制語句如表2-13所示。
表2-13

5.Python函數
· Python數學函數及其描述如表2-14所示。
表2-14

· 隨機數可以用于數學、游戲、安全等領域中,還經常被嵌入到算法中,用以提高算法效率,并提高程序的安全性。Python隨機函數及其描述如表2-15所示。隨機函數用于產生隨機數。
表2-15

· Python三角函數及其描述如表2-16所示。
表2-16

· 匿名函數lambda。
Python使用lambda來創建匿名函數。lambda只是一個表達式,函數體比def簡單很多。lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
lambda函數擁有自己的命名空間,并且不能訪問自有參數列表之外或全局命名空間里的參數。雖然lambda函數看起來只能寫一行,卻不等同于C語言或C++語言的內聯函數,內聯函數的目的是調用小函數時不占用棧內存從而增加運行效率。
示例代碼如下:
sum = lambda arg1, arg2: arg1 + arg2; print "相加后的值為 : ", sum( 10, 20 ) //輸出30
6.Python數學常量
· Python數學常量如表2-17所示。
表2-17

7.Python字符串
· 當需要在字符中使用特殊字符時,Python使用反斜杠(\)來表示,稱為轉義字符,如表2-18所示。
表2-18

· Python字符串運算符如表2-19所示。
其中,實例變量a值為字符串 "Hello",變量b值為"Python"。
表2-19

· Python字符串格式化。
Python支持格式化字符串的輸出。盡管這樣可能會用到非常復雜的表達式,但最基本的用法是將一個值插入一個有字符串格式符 %s的字符串中。
在Python中,字符串格式化使用的語法與C語言中sprintf()函數一樣。
示例代碼如下:
#! /usr/bin/Python print "My name is %s and weight is %d kg! " % ('BROWN',33)
以上示例輸出結果如下:
My name is BROWN and weight is 33 kg!
Python字符串格式化符號及其描述如表2-20所示。
表2-20

8.Python import語句
· from...import語句。
Python的from語句可以從模塊中導入一個指定的部分到當前命名空間中。語句如下:
from modname import name1[, name2[, ... nameN]]
例如,要導入模塊fib的fibonacci函數,可以使用如下語句:
from fib import fibonacci
這個聲明不會把整個fib模塊導入當前的命名空間中,它只會將fib里的fibonacci單個引入到執行這個聲明的模塊的全局符號表中。
· from...import*語句。
把一個模塊的所有內容全都導入當前的命名空間也是可行的,只需使用如下語句:
from modname import*
這提供了一個簡單的方法來導入一個模塊中的所有項目。然而這種聲明不可以被過多地使用。
例如,我們想一次性引入math模塊中所有的東西,語句如下:
from math import*
9.Python文件操作
1)打開和關閉文件
Python提供了必要的函數和方法對文件進行基本的操作。可以使用file對象對大部分的文件進行操作。
· open()函數。
用戶必須先用Python內置的open()函數打開一個文件,創建一個與file對象相關的方法才可以調用該函數進行讀/寫操作。
語句如下:
file object = open(file_name [, access_mode][, buffering])
各個參數的說明如下。
? file_name:包含了一個要訪問的文件名稱的字符串值。
? access_mode:決定了打開文件的模式,包括只讀、寫入、追加等。所有可取值如表2-21所示。這個參數是非強制的,默認文件訪問模式為只讀(r)。
? buffering:如果buffering的值被設為0,就不會有寄存。如果buffering的值被設為1,訪問文件時會寄存行。如果buffering的值被設為大于1的整數,表明這就是寄存區的緩沖大小。如果buffering的值被設為負值,寄存區的緩沖大小則為系統默認。
表2-21

file對象的屬性:當一個文件被打開后,將會有一個file對象,并且可以得到有關該文件的各種信息。與file對象相關的所有屬性列表如表2-22所示。
表2-22

· close()方法:file對象的close()方法可以刷新緩沖區里任何還沒有寫入的信息,并關閉該文件,這之后便不能再進行寫入。
當一個文件對象的引用被重新指定給另一個文件時,Python會關閉之前的文件。用close()方法關閉文件是一個很好的習慣。
語句如下:
fileObject.close();
· write()方法:將任何字符串寫入一個打開的文件中。需要重點注意的是,Python字符串可以是二進制數據,而不僅僅是文字。
write()方法不會在字符串的結尾添加換行符('\n')。
語句如下:
fileObject.write(string);
· read()方法:從一個打開的文件中讀取一個字符串。需要重點注意的是,Python字符串可以是二進制數據,而不僅僅是文字。
語句如下:
fileObject.read([count]);
· tell()方法:tell()方法指定文件內的當前位置;換句話說,下一次的讀/寫會發生在文件開頭的字節之后。
· seek(offset[, from])方法:改變當前文件的位置。offset變量表示要移動的字節數。from變量指定開始移動字節的參考位置。
如果from被設為0,這意味著將文件的開頭作為移動字節的參考位置;如果from被設為1,則使用當前的位置作為參考位置;如果from被設為2,那么將該文件的末尾作為參考位置。
2)重命名和刪除文件
Python的OS模塊提供了執行文件處理操作的方法,比如重命名和刪除文件。要使用這個模塊,必須先導入它,然后才可以調用相關的功能。
· remove()方法:可以使用remove()方法刪除文件,需要將要刪除的文件名作為參數。
Python里的所有文件都包含在各個不同的目錄下,即使這樣Python也能輕松處理。OS模塊提供了許多創建、刪除和更改目錄的方法。
· mkdir()方法:可以使用OS模塊的mkdir()方法在當前目錄下創建新的目錄。用戶需要提供一個包含了要創建的目錄名稱的參數。
語句如下:
os.mkdir("newdir")
· chdir()方法:可以使用chdir()方法來更改當前的目錄。chdir()方法需要的一個參數是:想設成當前目錄的目錄名稱。
語句如下:
os.chdir("newdir")
· rmdir()方法:可以使用rmdir()方法刪除目錄,目錄名稱以參數傳遞。
在刪除某個目錄之前,這個目錄中的所有內容應該先被清除。
語句如下:
os.rmdir('dirname')
file對象方法和OS對象方法可以對Windows和UNIX操作系統上的文件及目錄進行廣泛且實用的處理及操控。
· file對象方法:file對象提供了操作文件的一系列方法。
· OS對象方法:OS對象提供了處理文件及目錄的一系列方法。
10.Python file方法
file對象使用open()函數來創建,file對象常用的函數及其描述如表2-23所示。
表2-23

使用Python語言需要注意以下問題。
· 大小寫敏感:即字母是區分大小寫的。所以如果把前面例子代碼中的若干個字母從小寫變成大寫,系統將會報錯。
· 要用英文字符:冒號、逗號、分號、括號、引號等各種符號必須用英文,使用中文字符將會報錯。
· 注釋:為了讓人們更好地理解代碼的含義,通常都會在代碼中寫入注釋。注釋是給人看的,計算機會忽略(需要注意的是,空行也會被忽略),所以用中文記錄思路也是可以的。筆者強烈建議養成寫注釋的好習慣。注釋的寫法為“#”,表示會把所在行的其后所有內容設定為注釋。
舉例如下。
解決Python中不能輸入漢字的問題,我們在Python的IDE中有時候會輸入中文,Python對中文不太友好。在一般情況下,在代碼前加入“# coding: utf-8”就可以了。示例代碼如下:
# coding: utf-8 reload(sys) sys.setdefaultencoding("utf-8")
11.Python數據類型
1)列表(List)
在Python中沒有數組的概念,與數組最接近的概念就是列表和元組。列表是用來存儲一連串元素的容器,用“[]”來表示。例如,可以用序列表示數據庫中一個人的信息,第一個元素是姓名,第二個元素是年齡,根據上述內容定義一個列表(列表元素通過逗號分隔,寫在方括號中),示例代碼如下:
# 定義一個列表,列表是一個可變序列 edward = ['Edward Gumby',42] # 打印列表 edward
以上示例輸出結果如下:
['Edward Gumby',42]
2)元組(Tuple)
在Python中與數組類似的還有元組,元組中的元素可以進行索引計算。列表和元組的區別在于:列表中元素的值可以修改,而元組中元素的值不可以修改,只可以讀取;另外,列表的符號是“[]”,而元組的符號是“()”。示例代碼如下:
# 定義一個元組,元組是一個不可變序列 tom = ('Tom Teddy',37) # 打印元組 tom
以上示例輸出結果如下:
('Tom Teddy',37)
3)字典(Dictionary)
在Python中,字典也叫作關聯數組,可以理解為列表的升級版,用大括號括起來,格式為{key1: value1, key2: value2, …, keyn: valuen},即字典的每個鍵值(key/value)對用冒號分隔,每個對之間用逗號分隔,整個字典包括在大括號中。示例代碼如下:
# 定義一個字典,字典是一個由鍵值對構成的序列 age = {’張三’:27, ’李四’:29} # 打印字典 print(age)
以上示例輸出結果如下:
{’張三’:27, ’李四’:29}
4)字符串或串(String)
字符串或串是由數字、字母、下畫線組成的一串字符。一般記為s=“a1a2…an”(n≥0)。它在編程語言中表示文本的數據類型。在程序設計中,字符串為符號或數值的一個連續序列,如符號串(一串字符)或二進制數字串(一串二進制數字)。示例代碼如下:
# Python數據類型:字符串、整數、浮點數 a = ’中國’ b = 25 c = 3.14
5)軟件包(numPy)
軟件包是Python的一個擴展程序庫,支持大量的維度數組與矩陣運算,此外它也針對數組運算提供了大量的數學函數庫。示例代碼如下:
# 軟件包numpy例子 # 導入庫 import numpy as np # 創建一個3*5的多維數組(數據類型) a = np.arange(15).reshape(3, 5) a
輸出結果如下:
array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]])
再舉一個例子,代碼如下:
# 軟件包pandas例子 # 導入庫 import numpy as np import pandas as pd df = pd.DataFrame([’張三’, ’李四’, ’王五’], columns = {’姓名’}, index = {1,2,3}) df
輸出結果如圖2-6所示。

圖2-6
2.3.3 量化中函數的定義及使用方法
Python中的函數是組織好的,可重復使用的,用來實現單一或相關聯功能的代碼段。它能提高應用的模塊性能和代碼的重復利用率。支持遞歸、默認參數值、可變參數,但不支持函數重載。Python提供了許多內建函數,如print()。但也可以自己創建函數,稱為用戶自定義函數。
定義一個想要實現某種功能的函數,要遵循以下規則。
函數代碼塊以def關鍵詞開頭,后接函數標識符名稱和小括號。任何傳入參數和自變量必須放在小括號中間。小括號之間可以定義參數。函數的第一行語句可以選擇性地使用文檔字符串—用于存放函數說明。函數內容以冒號起始,并且縮進。例如,return [表達式]結束函數,選擇性地返回一個值給調用方。不帶表達式的return返回None。
1.函數語法
Python定義函數使用def關鍵字,一般格式如下:
def函數名 (參數列表): 函數體
在默認情況下,參數值和參數名稱是按函數聲明中定義的順序進行匹配的。
例如,使用函數來輸出"Hello BROWN! ",示例代碼如下:
def hell(): print("Hello BROWN! ") hell() x = itertools.compress(range(5), (True, False, True, True, False)) print(list(x)) Hello BROWN!
舉一個更復雜的例子,在函數中帶上參數變量,示例代碼如下:
# 計算面積函數 def area(width, height): return width * height def print_welcome(name): print("Welcome", name) print_welcome("Runoob") w = 7 h = 8 print("width = ", "height ", h, "area ", area(w, h))
以上示例輸出結果如下:
('Welcome', 'Runoob') ('width = ', 'height ', 8, 'area ', 56)
2.函數調用
定義一個函數:賦予函數一個名稱,指定函數里包含的參數和代碼塊結構。
這個函數的基本結構完成以后,可以通過另一個函數調用執行,也可以直接使用Python命令提示符執行。
調用printme() 函數的示例代碼如下:
# 定義函數 def printme (str): # 打印任何傳入的字符串 print (str) return # 調用函數 printme("調用王東澤函數!") printme("再次調用王東澤函數")
以上示例輸出結果如下:
調用王東澤函數! 再次調用王東澤函數
sort()函數用于對原列表進行排序,如果指定參數,則使用比較函數指定的函數。
sort()語句如下:
list.sort(key = None, reverse = False)
參數說明如下。
· key:主要是用來進行比較的元素,只有一個參數,具體函數的參數取自可迭代對象,指定可迭代對象中的一個元素來進行排序。
· reverse:排序規則,reverse = True表示降序,reverse = False表示升序(默認)。sort()函數沒有返回值,但是會對列表的對象進行排序。
以下示例展示了sort() 函數的使用方法:
aList = [’量化網’, ’優礦’, ’谷歌’, ’百度’] aList.sort() print ( "List : ", aList)
以上示例輸出結果如下:
aList = [’量化網’, ’優礦’, ’谷歌’, ’百度’]
以下示例按降序輸出列表:
# 列表 vowels = ['d', 'r', 'w', 'c', 'q'] # 降序 vowels.sort(reverse=True) # 輸出結果 print(’降序輸出:', vowels)
以上示例輸出結果如下:
降序輸出: ['w', 'r', 'q', 'd', 'c']
以下示例演示了通過指定列表中的元素排序來輸出列表:
# 獲取列表第二個元素 def takeSecond(elem): return elem[1] # 列表 random = [(7,6), (3,5), (4,7), (2,1)] # 指定第二個元素排序 random.sort(key = takeSecond) # 輸出類別 print (’排序列表: ', random)
以上示例輸出結果如下:
排序列表: [(2, 1), (3, 5), (7, 6), (4, 7)]
2.3.4 面向對象編程OOP的定義及使用方法
Python從設計之初就是一門面向對象的語言,所以很容易在Python中創建一個類和對象。
對象:以類為單位來管理所有代碼。對象包括兩個數據成員(類變量和實例變量)和方法。并且加入了類機制,可以包含任意數量和類型的數據。
Python中的類是對象的“抽象部分”,提供了面向對象編程的所有基本功能:類的繼承機制允許有多個基類,派生類可覆蓋基類方法,方法中可調用基類的同名方法。
語法格式如下:
class ClassName: <statement-1> . . . <statement-N>
將類實例化可以使用其屬性,即創建一個類之后,可以通過類名訪問其屬性。
類對象支持屬性引用和實例化兩種操作方法。其屬性引用使用的語法為:obj.name。類對象創建后,所有的命名都是有效屬性名。類定義代碼如下:
class MyClass: """一個簡單的類實例""" i = 3.14159265 def f(self): return 'hello BROWN' # 實例化類 x = MyClass() # 訪問類的屬性和方法 print("MyClass類的屬性i為: ", x.i) print("MyClass類的方法f輸出為: ", x.f())
以上代碼創建了一個新的類實例并將該對象賦給局部變量x, x是一個空的對象。
執行以上代碼后,輸出的結果如下:
MyClass類的屬性i為: 3.14159265 MyClass類的方法f輸出為: hello BROWN
類有一個名為 __init__()的特殊構造方法,該方法在類實例化時可自動調用,例如:
def __init__(self): self.data = []
類定義了__init__()方法,類的實例化操作會自動調用__init__()方法。例如,實例化類MyClass,對應的__init__()方法就會被調用,例如:
x = MyClass()
__init__() 方法也可以設置參數,參數可以通過 __init__()傳遞到類。例如:
class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart x = Complex(7.8, -2.6) print(x.r, x.i)
執行以上代碼后,輸出的結果如下:
(7.8, -2.6)
self代表類的實例而不是類。類的方法必須有一個額外的第一個參數名稱,按照慣例它的名稱是self。這是與普通函數的唯一區別。例如:
class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt()
執行以上代碼后,輸出的結果如下:
<__main__.Test object at 0x000001C9FBB43F60> <class'__main__.Test'>
在類的內部,如果使用def關鍵字來定義一個方法就必須包含參數self,且為第一個參數,self代表的是類的實例。這一點與一般函數的定義不同。
# 類定義 class people: # 定義基本屬性 name = '' age = 0 # 定義私有屬性,私有屬性在類外部無法直接進行訪問 __weight = 0 # 定義構造方法 def __init__(self, n, a, w): self.name = n self.age = a self.__weight = w def speak(self): print("%s說:王東澤 %d歲。" %(self.name, self.age)) # 實例化類 p = people('runoob',31,30) p.speak()
執行以上代碼后,輸出的結果如下:
runoob說:王東澤 31歲。
· 單繼承:Python支持類的繼承,否則類就失去了意義。單繼承的類定義如下:
class DerivedClassName(BaseClassName1): <statement-1> . . . <statement-N>
注意小括號中基類的順序,如果基類中有相同的方法名,而子類使用時未指定,Python將從左到右搜索,即方法在子類中未找到時,可以從左到右查找基類中是否包含方法。
BaseClassName(示例中的基類名)必須與派生類定義在一個作用域內。基類定義在另一個模塊中時表達式非常有用,代碼如下:
class DerivedClassName(modname.BaseClassName):
示例代碼如下:
# 類定義 class people: # 定義基本屬性 name = '' age = 0 # 定義私有屬性,私有屬性在類外部無法直接進行訪問 __weight = 0 # 定義構造方法 def __init__(self, n, a, w): self.name = n self.age = a self.__weight = w def speak(self): print("%s說:我 %d歲。" %(self.name, self.age)) # 單繼承 class student(people): grade = '' def __init__(self, n, a, w, g): # 調用父類的構造方法 people.__init__(self, n, a, w) self.grade = g # 覆寫父類的方法 def speak(self): print("%s說:王東澤 %d歲了,王東澤在讀 %d年級博士后"%(self.name, self. age, self.grade)) s = student('ken',31,60,2) s.speak()
執行以上代碼后,輸出的結果如下:
ken說:王東澤 31歲了,王東澤在讀 2年級博士后
· 多繼承:Python同樣支持多繼承形式。多繼承的類定義如下:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
需要注意小括號中父類的順序,若父類中有相同的方法名,而在子類使用時未指定,Python將從左到右搜索,即方法在子類中未找到時,從左到右查找父類中是否包含方法。從單繼承到多繼承的示例代碼如下:
# 類定義 class people: # 定義基本屬性 name = '' age = 0 # 定義私有屬性,私有屬性在類外部無法直接進行訪問 __weight = 0 # 定義構造方法 def __init__(self, n, a, w): self.name = n self.age = a self.__weight = w def speak(self): print("%s說:我 %d歲。" %(self.name, self.age)) # 單繼承 class student(people): grade = '' def __init__(self, n, a, w, g): # 調用父類的構造方法 people.__init__(self, n, a, w) self.grade = g # 覆寫父類的方法 def speak(self): print("%s說:我 %d歲了,我在讀 %d年級"%(self.name, self.age, self.grade)) # 定義另一個類 class speaker(): topic = '' name = '' def __init__(self, n, t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一名作家,我的寫作主題是 %s"%(self.name, self.topic)) # 多繼承 class sample(speaker, student): a ='' def __init__(self, n, a, w, g, t): student.__init__(self, n, a, w, g) speaker.__init__(self, n, t) test = sample("王東澤",25,80,4, "Python") test.speak() # 與方法名相同,默認調用的是在括號中排名靠前的父類的方法
執行以上程序后,輸出的結果如下:
我叫 王東澤,我是一名作家,我的寫作主題是Python
2.3.5 itertools的使用方法
itertools迭代器(生成器)是Python中一種很常用也很好用的數據結構,與列表相比,迭代器最大的優勢就是延遲計算、按需使用,從而提高開發者的體驗度和運行效率。所以在Python3中map、filter等操作返回的不再是列表而是迭代器。經常用到的迭代器是range,但是通過iter()函數把列表對象轉化為迭代器對象會多此一舉,所以使用itertools更合適一些。
itertools中的函數大多會返回各種迭代器對象,其中很多函數的作用需要我們寫很多代碼才能發揮,在運行效率上很低,因為其是系統庫。下面列舉itertools的使用方法。
itertools.accumulate表示累加。例如:
import itertools x = itertools.accumulate(range(10)) print(list(x))
執行以上程序后,輸出的結果如下:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
itertools.chain表示連接多個列表或迭代器。例如:
x = itertools.chain(range(3), range(4), [3,2,1]) print(list(x))
執行以上程序后,輸出的結果如下:
[0, 1, 2, 0, 1, 2, 3, 3, 2, 1]
itertools.combinations_with_replacement表示允許重復元素的組合。例如:
x = itertools.combinations_with_replacement('ABC', 2) print(list(x))
執行以上程序后,輸出的結果如下:
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
itertools.compress表示按照真值表篩選元素。例如:
x = itertools.compress(range(5), (True, False, True, True, False)) print(list(x))
執行以上程序后,輸出的結果如下:
[0, 2, 3]
itertools.count是一個計數器,可以指定起始位置和步長。例如:
x = itertools.count(start=20, step=-1) print(list(itertools.islice(x, 0, 10, 1)))
執行以上程序后,輸出的結果如下:
[20, 19, 18, 17, 16, 15, 14, 13, 12, 11]
itertools.dropwhile表示按照真值函數,丟棄列表和迭代器前面的元素。例如:
x = itertools.dropwhile(lambda e: e < 5, range(10)) print(list(x))
執行以上程序后,輸出的結果如下:
[5, 6, 7, 8, 9]
itertools.filterfalse表示保留對應真值為False的元素。例如:
x = itertools.filterfalse(lambda e: e < 5, (1, 5, 3, 6, 9, 4)) print(list(x))
執行以上程序后,輸出的結果如下:
[5, 6, 9]
itertools.groupby表示按照分組函數的值對元素進行分組。例如:
x = itertools.groupby(range(10), lambda x: x < 5 or x > 8) for condition, numbers in x: print(condition, list(numbers))
執行以上程序后,輸出的結果如下:
(True, [0, 1, 2, 3, 4]) (False, [5, 6, 7, 8]) (True, [9])
itertools.islice表示對迭代器進行切片操作。例如:
x = itertools.islice(range(10), 0, 9, 2) print(list(x))
執行以上程序后,輸出的結果如下:
[0, 2, 4, 6, 8]
itertools.permutations返回可迭代對象的所有數學全排列方式。例如:
x = itertools.permutations(range(4), 3) print(list(x))
執行以上程序后,輸出的結果如下:
[(0, 1, 2), (0, 1, 3), (0, 2, 1), (0, 2, 3), (0, 3, 1), (0, 3, 2), (1, 0, 2), (1, 0, 3), (1, 2, 0), (1, 2, 3), (1, 3, 0), (1, 3, 2), (2, 0, 1), (2, 0, 3), (2, 1, 0), (2, 1, 3), (2, 3, 0), (2, 3, 1), (3, 0, 1), (3, 0, 2), (3, 1, 0), (3, 1, 2), (3, 2, 0), (3, 2, 1)]
itertools.repeat表示簡單地生成一個擁有指定數目元素的迭代器。例如:
x = itertools.repeat(0, 5) print(list(x))
執行以上程序后,輸出的結果如下:
[0, 0, 0, 0, 0]
itertools.starmap與map類似。例如:
x = itertools.starmap(str.islower, 'aBCDefGhI') print(list(x))
執行以上程序后,輸出的結果如下:
[True, False, False, False, True, True, False, True, False]
itertools.takewhile與dropwhile相反,保留元素直到真值函數值為假。例如:
x = itertools.takewhile(lambda e: e < 5, range(10)) print(list(x))
執行以上程序后,輸出的結果如下:
[0, 1, 2, 3, 4]
itertools.zip_longest與zip類似,但是需要以較長的列表和迭代器的長度為準。例如:
x = itertools.zip_longest(range(3), range(5)) y = zip(range(3), range(5)) print(list(x))
執行以上程序后,輸出的結果如下:
[(0, 0), (1, 1), (2, 2), (None, 3), (None, 4)] [(0, 0), (1, 1), (2, 2)]
2.4 量化投資工具—Matplotlib
Matplotlib是Python 2D-繪圖領域使用非常廣泛的套件。它能讓使用者輕松地將數據圖形化,并且提供多樣化的輸出格式。下面來介紹Matplotlib的常見用法。
讀者要想使用Python繪制K線圖最好在Anaconda網站下載安裝包。安裝步驟如下。
(1)單擊“Windows”然后單擊“64-Bit Graphical Installer(614.3MB)”,如圖2-7所示。

圖2-7
(2)點擊相應鏈接進行下載,并選擇保存的文件夾,下載完成后,即可在文件夾中找到下載的安裝包,如圖2-8所示。

圖2-8
(3)雙擊安裝包文件,在彈出的安裝對話框中單擊“Next”按鈕,然后單擊“I Agree”按鈕,如圖2-9所示。

圖2-9
(4)根據提示繼續單擊相應按鈕進行安裝。直到出現如圖2-10右圖所示的對話框,然后依次單擊“Install Microsoft VSCode”→“Cancel”按鈕,完成安裝。

圖2-10
Matplotlib僅需要幾行代碼,便可以生成繪圖、直方圖、功率譜、條形圖、錯誤圖、散點圖等。
2.4.1 Matplotlib基礎知識
Matplotlib的基礎知識如下。
(1)Matplotlib中基本圖表的元素包括x軸和y軸、水平和垂直的軸線。
x軸和y軸使用刻度對坐標軸進行分隔,包括最小刻度和最大刻度;x軸和y軸刻度標簽表示特定坐標軸的值、繪圖區域等。
(2)hold屬性默認為True,允許在一幅圖中繪制多條曲線。
(3)使用grid方法為圖添加網格線,方法為設置grid參數(參數與plot()函數相同), lw代表linewidth(線的粗細), Alpha表示線的明暗程度。
(4)axis方法如果沒有任何參數,則返回當前坐標軸的上下限。
(5)除了plt.axis方法,還可以通過xlim、ylim方法設置坐標軸范圍。
(6)legend方法如下。
· 初級繪制。
這一節中,我們將從簡到繁:先嘗試用默認配置在同一張圖上繪制正弦函數和余弦函數圖像,然后逐步美化它。
首先取得正弦函數和余弦函數的值,示例代碼如下:
from pylab import * X = np.linspace(-np.pi, np.pi, 256, endpoint=True) C, S = np.cos(X), np.sin(X)
X是一個numpy數組,包含了-π到+π之間的256個值。C和S分別是這256個值對應的余弦函數和正弦函數值組成的numpy數組。
· 使用默認配置。
Matplotlib的默認配置允許用戶進行自定義。可以調整大多數的默認配置:圖片大小和分辨率(dpi)、線條寬度、顏色、風格、坐標軸、網格的屬性、文字和字體屬性等。不過,Matplotlib的默認配置在大多數情況下已經做得足夠好了,可能只有在特殊的情況下才會想要更改這些默認配置。示例代碼如下:
import numpy as np import matplotlib.pyplot as plt X = np.linspace(-np.pi, np.pi, 256, endpoint=True) C, S = np.cos(X), np.sin(X) plot(X, C) plot(X, S) show()
執行以上程序后,輸出的結果如圖2-11所示(由于本書是黑白印刷,涉及的顏色無法在書中呈現,請讀者結合軟件界面進行辨識)。

圖2-11
在下面的代碼中,我們展現了Matplotlib的默認配置并輔以注釋說明,這部分配置包含了有關繪圖樣式的所有配置。代碼中的配置與默認配置完全相同,可以在交互模式中修改其中的值來觀察效果:
# 導入Matplotlib的所有內容 from pylab import * # 創建一個 9*7 點陣圖,并設置分辨率為80像素 figure(figsize=(9,7), dpi=80) # 創建一個新的 1*1 的子圖,接下來的圖樣繪制在其中的第1 塊 subplot(1,1,1) X = np.linspace(-np.pi, np.pi, 256, endpoint=True) C, S = np.cos(X), np.sin(X) # 繪制余弦曲線,使用藍色的、連續的、寬度為1 像素的線條 plot(X, C, color="blue", linewidth=1.0, linestyle="-") # 繪制正弦曲線,使用綠色的、連續的、寬度為1 像素的線條 plot(X, S, color="green", linewidth=1.0, linestyle="-") # 設置橫軸的上下限 xlim(-3.0,3.0) # 設置橫軸記號 xticks(np.linspace(-3,3,9, endpoint=True)) # 設置縱軸的上下限 ylim(-1.0,1.0) # 設置縱軸記號 yticks(np.linspace(-2,2,5, endpoint=True)) # 以分辨率 72像素來保存圖片 # savefig("exercice_2.png", dpi=72) # 在屏幕上顯示 show()
執行以上程序后,輸出的結果如圖2-12所示(由于本書是黑白印刷,涉及的顏色無法在書中呈現,請讀者結合軟件界面進行辨識)。

圖2-12
現在來改變線條的顏色和粗細。我們首先以藍色和紅色分別表示余弦函數和正弦函數,然后將線條變粗一點,接下來,我們以水平方向拉伸整個圖。示例代碼如下:
figure(figsize=(10,6), dpi=70) plot(X, C, color="blue", linewidth=2.5, linestyle="-") plot(X, S, color="red", linewidth=2.5, linestyle="-")
執行以上程序后,輸出的結果如圖2-13所示(由于本書是黑白印刷,涉及的顏色無法在書中呈現,請讀者結合軟件界面進行辨識)。

圖2-13
2.4.2 Matplotlib可視化工具基礎
可視化工具可以高效獲取信息。人眼是一個高帶寬的巨量信號輸入并行處理器,具有超強的模式識別能力,對可視符號的感知速度比對數字或文本快多個數量級,而可視化就是迎合了人眼的這種特點,才使得獲取信息的難度大大降低。這才會有“一圖勝千言”的說法,一堆數據很難快速看明白,但生成圖形就會一目了然。比如用圖來表達國際象棋與圍棋的復雜度,如圖2-14所示。

圖2-14
可視化工具可以輔助人們在人腦之外保存待處理信息,從而補充人腦有限的記憶內存,提高信息認知的效率。雖然人們能夠有意識地集中注意力,但不能長時間保持視覺搜索的高效狀態。而圖形化符號可以高效地傳遞信息,將用戶的注意力引導到重要的目標上。
可視化的作用體現在多個方面,如揭示想法和關系、形成論點或意見、觀察事物演化的趨勢、總結或積聚數據、存檔和整理、尋求真相和真理、傳播知識和探索性數據分析等。
在計算機學科的分類中,利用人眼的感知能力對數據進行交互的可視表達,以增強認知的技術,稱為可視化。它將不可見或難以直接顯示的數據轉化為可感知的圖形、符號、顏色、紋理等,來增強數據識別效率,傳遞有效信息。
如果要同時繪制多個圖表,可以給figure() 傳遞一個整數參數來指定Figure對象的序號,如果序號所指定的Figure對象已經存在,只需要讓它成為當前的Figure對象即可。示例代碼如下:
import numpy as np # 創建圖表1和圖表2 plt.figure(1) plt.figure(2) # 在圖表2中創建子圖1和子圖2 ax1 = plt.subplot(211) ax2 = plt.subplot(212) x = np.linspace(0, 3, 100) for i in xrange(5): # 選擇圖表1 plt.figure(1) plt.plot(x, np.exp(i*x/3)) plt.sca(ax1) # 選擇圖表2的子圖1 plt.plot(x, np.sin(i*x)) plt.sca(ax2) # 選擇圖表2的子圖2 plt.plot(x, np.cos(i*x))
執行以上程序后,輸出的結果如圖2-15所示。

圖2-15
2.4.3 Matplotlib子畫布及loc的使用方法
定義畫布的方法如下:
import matplotlib.pyplot as plt Populating the interactive namespace from numpy and matplotlib
Matplotlib的圖像都位于Figure畫布中,可以使用plt.figure創建一個新畫布。
如果要在一個圖表中繪制多個子圖,可使用subplot,示例代碼如下:
# 創建一個新的Figure fig = plt.figure() #不能通過空Figure繪圖,必須用add_subplot創建一個或多個subplot ax1 = fig.add_subplot(2, 2, 1) ax2 = fig.add_subplot(2, 2, 2) ax3 = fig.add_subplot(2, 2, 3) from numpy.random import randn # 沒有指定具體subplot的繪圖命令時,會在最后一個用過的subplot上進行繪制 plt.plot(randn(50).cumsum(), 'k--') _ = ax1.hist(randn(100), bins=25, color='k', alpha=0.4) # 這里加分號可以屏蔽不必要的輸出 ax2.scatter(np.arange(50), np.arange(50) + 3*randn(50)) ;
執行以上代碼后,輸出的結果如圖2-16所示。

圖2-16
使用上述命令可以繪制一些圖例,并可用豐富的數學符號進行標注,還可在一個畫布里容納多個子圖形,這些都是很常用的功能。
在一張圖中顯示多個子圖,示例代碼如下:
# sub = 子 x = np.arange(-10, 10, 0.1) plt.figure(figsize=(12, 9)) # 1行3列的第1個子圖 axes = plt.subplot(1, 3, 1) axes.plot(x, np.sin(x)) # 設置網格顏色、樣式、寬度 axes.grid(color='r', linestyle='--', linewidth=2) # 1行3列的第2個子圖 axes2 = plt.subplot(1, 3, 2) axes2.plot(x, np.cos(x)) # 設置網格顏色、樣式、寬度 axes2.grid(color='g', linestyle='-.', linewidth=2) # 1行3列的第3個子圖 axes3 = plt.subplot(1, 3, 3) axes3.plot(x, np.sin(x)) # 設置網格顏色、樣式、寬度 axes3.grid(color='b', linestyle=':', linewidth=2)
執行以上代碼后,輸出的結果如圖2-17所示。

圖2-17
下面來繪制一個子畫布,示例代碼如下:
import matplotlib.pyplot as plt import numpy as np def f(t): return np.exp(-t) * np.cos(2 * np.pi * t) # t = np.arange(0, 5, 0.2) t1 = np.arange(0, 5, 0.1) t2 = np.arange(0, 5, 0.02) # plt.plot(t, t, 'r--', t, t ** 2, 'bs', t, t ** 3, 'g^') plt.figure(12) plt.subplot(221) plt.plot(t1, f(t1), 'bo', t2, f(t2), 'r--') plt.subplot(222) plt.plot(t2, np.cos(2 * np.pi * t2), 'r--') plt.subplot(212) plt.plot([1, 2, 3, 4, 5], [1, 5, 8, 13, 18]) plt.show()
執行以上代碼后,輸出的結果如圖2-18所示。

圖2-18
2.5 Matplotlib繪制K線圖的方法
2.5.1 安裝財經數據接口包(TuShare)和繪圖包(mpl_finance)
TuShare是一個開源的Python財經數據接口包,主要實現對股票等金融數據從數據采集、清洗加工到數據存儲的過程,能夠為金融分析人員提供快速、整潔和多樣的便于分析的數據,極大地減輕他們獲取數據來源方面的工作量,使他們更加專注于策略和模型的研究與實現上。由于Python pandas包在金融量化分析中有突出的優勢,而且TuShare返回的絕大部分數據格式都是pandas DataFrame類型,所以非常便于用pandas、numPy、Matplotlib進行數據分析和可視化。安裝方法如下:
pip install tushare
mpl_finance是Python中用來畫蠟燭圖、線圖的分析工具,目前已經從Matplotlib中獨立出來。安裝方法如下:
pip install mpl_finance
2.5.2 繪制K線圖示例
我們來繪制一張000001平安銀行的股價走勢圖。該示例分為3個部分,步驟如下。
(1)粘貼或輸入如下代碼:
# 2D繪圖包Matplotlib import tushare as ts # 需要先安裝 import matplotlib.pyplot as plt from matplotlib.gridspec import GridSpec from matplotlib.pylab import date2num import mpl_finance as mpf # 需要先安裝 import datetime # 下面輸入想要的股票代碼并下載數據 wdyx = ts.get_k_data('000001', '2017-01-01') # wdyx.info() # print(wdyx.head())
(2)下載完成平安銀行數據后,就可以利用這個數據繪制圖形了,示例代碼如下:
def date_to_num(dates): num_time = []# 2D繪圖包Matplotlib import tushare as ts # 需要先安裝 import matplotlib.pyplot as plt from matplotlib.gridspec import GridSpec from matplotlib.pylab import date2num import mpl_finance as mpf # 需要先安裝 import datetime # 下面輸入想要的股票代碼并下載數據 wdyx = ts.get_k_data('000001', '2017-01-01') # wdyx.info() # print(wdyx.head()) def date_to_num(dates): num_time = [] for date in dates: date_time = datetime.datetime.strptime(date, '%Y-%m-%d') num_date = date2num(date_time) num_time.append(num_date) return num_time
(3)輸入股票代碼后,可以將其轉換為二維數組,示例代碼如下:
# 將Dataframe轉換為二維數組 mat_wdyx = wdyx.values num_time = date_to_num(mat_wdyx[:, 0]) mat_wdyx[:, 0] = num_time # 接下來就可以繪制K線圖了 # 畫布大小 Fig, (ax0, ax1) = plt.subplots(2, sharex=True, figsize=(15, 8)) # 調整兩個子畫布大小(兩種方法) # ax0 = plt.subplot2grid((3, 1), (0, 0), rowspan=2) # ax1 = plt.subplot2grid((3, 1), (2, 0)) gs = GridSpec(3, 1) # 調整上下間隔(兩種方法) # gs.update(hspace=0.05) plt.subplots_adjust(hspace=0.05) ax0 = plt.subplot(gs[0:2]) ax1 = plt.subplot(gs[2]) # 在第一個子畫布上畫K線,在第二個子畫布上畫量的柱線 mpf.candlestick_ochl(ax0, mat_wdyx, width=1, colorup='r', colordown='g', alpha=1.0) ax0.set_title('000001') ax0.set_ylabel('Price') ax0.grid(True) plt.bar(mat_wdyx[:, 0]-0.4, mat_wdyx[:, 5], width=0.8) ax1.xaxis_date() ax1.set_ylabel('Volume') plt.show() for date in dates: date_time = datetime.datetime.strptime(date, '%Y-%m-%d') num_date = date2num(date_time) num_time.append(num_date) return num_time # 將Dataframe轉換為二維數組 mat_wdyx = wdyx.values num_time = date_to_num(mat_wdyx[:, 0]) mat_wdyx[:, 0] = num_time # 接下來就可以繪制K線圖了 # 畫布大小 fig, (ax0, ax1) = plt.subplots(2, sharex=True, figsize=(15, 8)) # 調整兩個子畫布大小(兩種方法) # ax0 = plt.subplot2grid((3, 1), (0, 0), rowspan=2) # ax1 = plt.subplot2grid((3, 1), (2, 0)) gs = GridSpec(3, 1) # 調整上下間隔(兩種方法) # gs.update(hspace=0.05) plt.subplots_adjust(hspace=0.05) ax0 = plt.subplot(gs[0:2]) ax1 = plt.subplot(gs[2]) # 在第一個子畫布上畫K線,在第二個子畫布上畫量的柱線 mpf.candlestick_ochl(ax0, mat_wdyx, width=1, colorup='r', colordown='g', alpha=1.0) ax0.set_title('000001') ax0.set_ylabel('Price') ax0.grid(True) plt.bar(mat_wdyx[:, 0]-0.4, mat_wdyx[:, 5], width=0.8) ax1.xaxis_date() ax1.set_ylabel('Volume') plt.show()
按Ctrl+Enter快捷鍵運行以上程序,輸出的結果如圖2-19所示。

圖2-19