Python 程式設計入門 - 08

Pyladies Taiwan

Speaker : Mars

2018/01/21

Roadmap

  • 前情提要:檔案操作
  • 模組
  • 好用的內建模組
  • 第三方模組-站在眾人的肩膀上
  • 補充知識
    • 再次載入模組
    • 套件

前情提要:檔案操作

  • file:檔案名稱與路徑,需為字串
    • 基準是程式所在的位置
  • mode:模式,預設為文字讀取(rt)。
    • r:文字讀取模式
    • w:文字寫入模式
    • a:文字附加模式
    • wb:二進位寫入模式
  • encoding:文字編碼系統,預設None,Windows需要寫上cp950或是utf8
    • f = open(file,encoding='cp950')

注意:

  • 使用檔案有開啟就要記得關閉!
  • 檔案也是個「可迭代物件」
  • 建議讀寫分開。
In [1]:
# Windows:open("doc2.txt","w",encoding='utf8')
fo = open("doc2.txt","w")
data = "something"
fo.write(data)
fo.close()
In [2]:
# Windows:open("doc2.txt",encoding='utf8')
fi = open("doc2.txt")
data = fi.read()
print(data)
fi.close()
something

Checkpoint:

  • 請大家開啟jupyter
  • 使用 New > Text File 增加一個文字檔
  • 改名為 article.txt
  • 輸入以下內容
  • 記得儲存
  • 使用 New > Python 撰寫寫程式利用開檔、讀檔(read)把 article.txt 的內容印出來
Back in 1927 it was Charles Lindbergh, flying non-stop from New York to Paris.
Earlier this year, it was Russian adventurer Fedor Konyukhov, making a nonstop solo circumnavigation of the globe in a hot air balloon.
The focus in aviation has always been on flying farther and staying airborne for longer.
And yet, while long-haul flights have the undeniable allure of the exotic, other, much shorter, air routes have the beauty of the minimal.
November sees the launch of the world's shortest international commercial flight -- an eight-minute hop between Switzerland's St. Gallen-Altenrhein and Germany's Friedrichshafen.
To mark the event, we've compiled a list of air routes that stand out for being the shortest commercial flights on the planet.
What's it like to fly when you are within sight of your final destination?

CSV (Comma-Separated Values)

In [3]:
import csv 
fo = open("CSV2.csv","w")
cw = csv.writer(fo,delimiter=' ')
cw.writerow(["data11","data12","data13"])
cw.writerow(["data21","data22","data23"])
cw.writerow(["data31","data32","data33"])
fo.close()
In [4]:
# Yahoo 股市 https://finance.yahoo.com/quote/CSV/history?p=CSV
import csv
fi = open("CSV.csv")
cr = csv.reader(fi)
for row_num,row in enumerate(cr):
    print(row_num,row)
fi.close()
0 ['Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']
1 ['2017-12-06', '26.100000', '26.570000', '25.709999', '26.490000', '26.490000', '100400']
2 ['2017-12-07', '26.469999', '26.670000', '26.219999', '26.520000', '26.520000', '82900']
3 ['2017-12-08', '26.530001', '26.780001', '26.340000', '26.629999', '26.629999', '535800']
4 ['2017-12-11', '26.639999', '26.660000', '26.230000', '26.340000', '26.340000', '80200']
5 ['2017-12-12', '26.360001', '26.500000', '26.200001', '26.330000', '26.330000', '78500']
6 ['2017-12-13', '26.270000', '26.540001', '26.150000', '26.299999', '26.299999', '70700']
7 ['2017-12-14', '26.290001', '26.290001', '25.830000', '25.840000', '25.840000', '58300']
8 ['2017-12-15', '25.860001', '26.320000', '25.860001', '26.209999', '26.209999', '199800']
9 ['2017-12-18', '26.379999', '26.469999', '26.139999', '26.230000', '26.230000', '107200']
10 ['2017-12-19', '26.320000', '26.500000', '26.180000', '26.270000', '26.270000', '125400']
11 ['2017-12-20', '26.350000', '26.350000', '25.900000', '26.010000', '26.010000', '75200']
12 ['2017-12-21', '26.120001', '26.120001', '25.879999', '25.900000', '25.900000', '91300']
13 ['2017-12-22', '26.070000', '26.120001', '25.530001', '25.750000', '25.750000', '81200']
14 ['2017-12-26', '25.770000', '25.840000', '25.600000', '25.650000', '25.650000', '39900']
15 ['2017-12-27', '25.590000', '25.799999', '25.590000', '25.680000', '25.680000', '44900']
16 ['2017-12-28', '25.690001', '26.170000', '25.690001', '26.080000', '26.080000', '61300']
17 ['2017-12-29', '26.129999', '26.129999', '25.690001', '25.709999', '25.709999', '56900']
18 ['2018-01-02', '25.719999', '25.980000', '25.570000', '25.959999', '25.959999', '105300']
19 ['2018-01-03', '25.959999', '26.309999', '25.639999', '26.209999', '26.209999', '119400']
20 ['2018-01-04', '26.400000', '26.709999', '26.270000', '26.459999', '26.459999', '130600']
21 ['2018-01-05', '26.520000', '26.670000', '26.270000', '26.420000', '26.420000', '69900']

JSON (JavaScript Object Notation)

In [5]:
import json
data = {
    "user":"Mars",
    "work":["Tripresso","PyLadies"]
}
json_data = json.dumps(data)
print(json_data,type(json_data))
{"user": "Mars", "work": ["Tripresso", "PyLadies"]} <class 'str'>
In [6]:
import json
json_data = '{"work": ["Tripresso", "PyLadies"], "user": "Mars"}'
data = json.loads(json_data)
print(data)
print(data["work"])
print(data["work"][0])
{'user': 'Mars', 'work': ['Tripresso', 'PyLadies']}
['Tripresso', 'PyLadies']
Tripresso
import csv
import json
import math

這些都是我們利用已經寫好的工具,幫我們輕鬆的處理資料,
Python 比起其他語言,最強大的地方就是有很多好用的模組。

  • [網路爬蟲]:urllib、requests、lxml、beautiful_soup、scrapy、selenium
  • [資料庫串接]:sqlite3(sqlite)、MySQLdb(MySQL)、pymssql(MSSQL)、Psycopg(PostgreSQL)
  • [自然語言]:NLTK、jieba
  • [統計應用]:pandas、numpy、scipy、matplotlib
  • [機器學習]:scikit-learn、TensorFlow
  • [影像處理]:PIL、opencv
  • [網站架設]:Django、Flask
  • [網路分析]:scapy
  • [GUI設計]:tkinter、PyQt
  • [軟硬整合]:raspberry pi 樹莓派、Arduino
  • [遊戲開發]:pygame
  • [App開發]:kivy
  • [各種服務的API串接]:Bot

模組 module

import 模組名

載入的模組會生成一個__pycache__存放Python直譯器編譯過後的模組檔,
之後在載入模組時,會以這個編譯後的模組檔來執行,
如果原檔比編譯檔還新,會重新編譯。

每個模組都有自己的__name__名稱

  • 主程式模組的__name__值為__main__

module1.py (跟主程式在同一資料夾層級)

def main(name):
    print("Hello {}".format(name))

if __name__=='__main__':
    main("Mars")
In [7]:
import module1
def main():
    print("Hello Python")
    
if __name__=="__main__":
    main()
    module1.main("MarsW")
    print("My     __name__ is",__name__)
    print("Module __name__ is",module1.__name__)
Hello Python
Hello MarsW
My     __name__ is __main__
Module __name__ is module1

模組是整個被載入的,無法只選取部分

module2.py (跟主程式在同一資料夾層級)

def main(name):
    print("Hello {}".format(name))

main("Mars")
In [8]:
import module2
def main():
    print("Hello Python")
    
if __name__=="__main__":
    main()
    module2.main("MarsW")
Hello Mars
Hello Python
Hello MarsW

更多匯入模組用法

import / as 幫載入的模組取別名

  • 程式可以更簡短
  • 但模組的原名還是沒有改變
In [9]:
import module1 as m1
def main():
    print("Hello Python")
    
if __name__=="__main__":
    main()
    m1.main("MarsW")
    print("My     __name__ is",__name__)
    print("Module __name__ is",m1.__name__)
Hello Python
Hello MarsW
My     __name__ is __main__
Module __name__ is module1

from / import 直接使用模組內的名稱

  • 程式可以更簡短,不需用模組名.名稱的寫法
  • 模組是整個被載入的,無法只選取部分
  • 小心同名問題
In [10]:
from module1 import main
# def main():
#     print("Hello Python") 
if __name__=="__main__":
#     main()
    main("MarsW")
Hello MarsW
In [11]:
from module1 import main
def main():
    print("Hello Python") 
if __name__=="__main__":
    main()
    main("MarsW")  # <= 跟主程式的main有衝突
Hello Python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-0116f6df3624> in <module>()
      4 if __name__=="__main__":
      5     main()
----> 6     main("MarsW")  # <= 跟主程式的main有衝突

TypeError: main() takes 0 positional arguments but 1 was given

可配合as取別名

In [12]:
from module1 import main as main1
def main():
    print("Hello Python") 
if __name__=="__main__":
    main()
    main1("MarsW")
Hello Python
Hello MarsW

Why

為什麼需要撰寫另外的檔案存放程式碼,不乾脆寫在同一份程式中呢?

  • 在需要的時候才載入,程式不會過於肥大
  • 分類你的程式內容,方便找尋
bot.py      # 主程式
chat.py     # 模組
food.py     # 模組
movie.py    # 模組
weather.py  # 模組

好用的內建模組

除了之前提過的csv, json, math,Python還有很多不需要額外安裝的好用內建模組。

Counter

還記得如果我要計算一個元素出現多少次會怎麼處理嗎?

  • 計算文章單字
  • 計算資料集(list,tuple...)中有幾個元素
In [13]:
data = ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt = {}
for word in data:
    if word not in cnt:
        cnt[word]=0
    cnt[word] += 1
print(cnt)
{'blue': 3, 'red': 2, 'green': 1}
In [14]:
from collections import Counter
data = ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt = Counter(data)
print(cnt)
Counter({'blue': 3, 'red': 2, 'green': 1})
  • Counter也是個可迭代物件
  • counter.most_common(n):選出最多的前n名
  • 不存在的元素數量Counter會設為0,不用擔心出錯
In [15]:
from collections import Counter
data = ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt = Counter(data)
for key in cnt:
    print(key,cnt[key])
print(cnt.most_common(2))
print(cnt["yellow"])
blue 3
red 2
green 1
[('blue', 3), ('red', 2)]
0
In [16]:
from collections import Counter
data = "PyLadies Taiwan"
cnt = Counter(data)
for key in cnt:
    print(key,cnt[key])
  1
y 1
L 1
n 1
w 1
s 1
i 2
e 1
P 1
T 1
a 3
d 1

練習-計算單字

還記得今天剛開始請大家儲存一篇文字到檔案 article.txt中嗎?
請開啟這個檔案,計算出現最多的五個單字

答案應該是 [('the', 10), ('of', 6), ('air', 3), ('it', 3), ('a', 3)]

Hint:

In [17]:
import random
print(random.randrange(0, 101, 2))   # 隨機從0~100取偶數
print(random.uniform(1, 10))         # 隨機從1~10 取浮點數
print(random.choice(['a',123,'c']))  # 隨機取元素
6
4.493935093959479
c
In [18]:
# 洗牌
import random
cards = ["A","J",5,9,10]
random.shuffle(cards)
print(cards)
[9, 'J', 'A', 5, 10]

練習-下一餐要吃什麼

建立一個檔案,每一列都是一個餐廳或是食物,
使用開檔、讀取、random,隨機產生下一餐的選擇吧!
eg.

麥當勞
7-11
牛肉麵

Hint:

  • 讀檔進來是整篇文章,需要把內容斷開可以用字串分割
  • 換行的符號是\n

第三方模組-站在眾人的肩膀上

建議大家安裝Anaconda是因為它內建了很多大家常用的Python第三方模組: https://docs.anaconda.com/anaconda/packages/pkg-docs

模組搜尋路徑:

  • 當前目錄:如果是自己撰寫的模組會在這層
  • 環境變數 PYTHONPATH:也就是Python的內建模組
  • 標準程式庫目錄:安裝Python的時候會幫你設定好
  • .pth檔的內容:裡面會有額外要搜尋的路徑,Python執行時會自行合併所有.pth
  • 第三方程式庫安裝目錄 site-packages、dist-packages:額外安裝的通常會在這
In [19]:
import sys
print(sys.path)
['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/local/lib/python3.4/dist-packages/wordcloud-1.3.1-py3.4-linux-x86_64.egg', '/usr/lib/python3/dist-packages', '/usr/local/lib/python3.4/dist-packages/IPython/extensions', '/home/marsw/.ipython']

可以看到模組搜尋路徑是一個串列,所以可以用append去增加新的路徑,
而如果要永久增加路徑,需視個作業系統設定:

Google

- PYTHONPATH Windows
- PYTHONPATH mac

安裝

pip install 模組名稱
pip3 install 模組名稱:如果電腦上同時有Python 2&3

注意:

  • Windows使用者請用「系統管理員身份」開啟命令提示字元
  • Linux、Mac 使用者請在指令前面加上「sudo」(sudo pip install ...)

Checkpoint:

  • 請大家安裝好此兩項模組
    import jieba
    import wordcloud
    

如果沒有安裝成功,會出現 ImportError: No module named 'xxx'
可能會遇到各種安裝狀況=>錯誤代碼丟給Google

中文斷詞

In [20]:
import jieba
with open("lyric.txt") as fi:
    data = fi.read()
data = data.replace(" ","").replace("\n","")
seg_list = list(jieba.cut(data))
print(seg_list)
with open("seg.txt","w") as fo:
    fo.write("\n".join(seg_list))
Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.681 seconds.
Prefix dict has been built succesfully.
['我身', '在', '當時', '你', '幻想', '的', '未來裡', '這個', '狂熱', '和', '衝動', '早已', '冷', '卻', '的', '如今', '你', '頑固', '的', '神情', '消失', '在', '鏡子裡', '只', '留下', '時光', '消逝', '的', '痕跡', '每顆', '心', '的', '相信', '每個', '人', '的', '際', '遇', '每個', '故事', '的', '自己', '反覆', '地', '問著', '自己', '這些', '年', '讓', '步', '的', '你', '是否', '會嘆息', '有', '什麼', '是', '你', '永遠', '不放棄', '?', '一次', '一次', '你', '吞下', '了', '淚滴', '一次', '一次', '拼回', '破碎', '自己', '一天', '一天', '你', '是否', '還', '相信', '活在', '你', '心', '深處', '那', '頑固', '自己', '你', '追逐', '你', '呼吸', '你', '囂張', '的', '任性', '鼻青', '臉腫', '的', '哭過', '若', '無', '其事', '的', '忘記', '如果', '你', '能', '預知', '這條', '路', '的', '陷阱', '我', '想', '你', '依然', '錯得', '很', '過癮', '走過', '的', '叫', '足跡', '走', '不到', '叫', '憧憬', '學會', '收拾', '起', '叛逆', '學會', '隱藏', '了', '表情', '更', '多', '更', '詳盡', '歌詞', '在', '※', 'Mojim', '.', 'com', '\u3000', '魔鏡', '歌', '詞網', '卸下', '了', '這', '面具', '我', '想', '說', '謝謝', '你', '謝謝', '你', '一路', '陪', '我', '到', '這裡', '一次', '一次', '你', '吞下', '了', '淚滴', '一次', '一次', '拼回', '破碎', '自己', '一天', '一天', '你', '是否', '還', '相信', '活在', '你', '心', '深處', '那', '頑固', '的', '自己', '一次', '一次', '你', '吞下', '了', '淚滴', '一次', '一次', '拼回', '破碎', '自己', '一天', '一天', '你', '是否', '還', '相信', '活在', '你', '心', '深處', '那', '頑固', '的', '你', '當時', '相信', '的', '那些', '事情', '會', '在', '如今', '變成', '美麗風景', '每當', '我', '遲疑', '從', '不曾', '忘', '記活', '在', '我心', '深處', '那', '頑固', '的', '自己']

文字雲

In [21]:
%matplotlib inline
import matplotlib.pyplot as plt
from wordcloud import WordCloud

with open("seg.txt") as fi:
    data = fi.read()
    
wc = WordCloud(font_path="SimHei.ttf")
wc.generate(data)

# 視覺化呈現
plt.imshow(wc)
plt.axis("off") # 不要顯示x,y軸
plt.show()

當然還有很多,
今天其實最主要目的是讓大家在需要工具的時候,自己去查詢
因為之後想要寫更多好玩、能解決問題的程式,
無法再靠一個講者把所有內容都講解透徹,
需要自己去查、去try...

Google

- Python 隨機
- Python 斷詞
- Python 文字雲
- Python 時間

補充知識

再次載入模組 reload

如果我們修改了某一模組,
但主程式太大重啟太麻煩或是主程式必須隨時運行,無法將主程式重啟,
這時就需要reload功能。

In [22]:
from importlib import reload
import time
with open("module3.py","w") as fo:
    fo.write("x = 100\ndata = [1,2,3,4,5]")
time.sleep(5)
import module3 as m
print("第一次載入")
print(m.x,m.data)
with open("module3.py","w") as fo:
    fo.write("x = 200\ndata = [1,2,3,4,5]")
print("修改原模組但未再次載入")
print(m.x,m.data)
time.sleep(5)
reload(m)
print("修改原模組再次載入")
print(m.x,m.data)
第一次載入
100 [1, 2, 3, 4, 5]
修改原模組但未再次載入
100 [1, 2, 3, 4, 5]
修改原模組再次載入
200 [1, 2, 3, 4, 5]

套件 package

雖然模組已經可以把不少程式碼分離,讓我們能對程式功能分門別類,
但有時候程式功能太過龐大,模組還不足夠,就需要「套件」~含有子模組的模組。

  • 套件裡面要放 __init__.py來做初始化,可以為空,
    或是決定在套件載入時要不要就預設載入某些套件/模組
  • 使用importfrom/import配合「.
    import 套件名.模組名
    import 套件名.套件名.模組名
    ...
bot.py           # 主程式
chat.py          # 模組 (有greeting函式)
movie/           # 套件 
    __init__.py  # 初始化
    search.py    # 模組 (有nowon函式)
    booking.py   # 模組
life/            # 套件 
    __init__.py  # 初始化
    bus.py       # 模組
    weather.py   # 模組
In [23]:
import chat
import movie.search as ms
chat.greeting("Mars")
ms.nowon()
Hello Mars
['大娛樂家', '歌喉讚3', '1987:黎明到來的那一天', '移動迷宮']
In [24]:
from chat import greeting
from movie import search as s
from movie.search import nowon
greeting("Mars")
s.nowon()
nowon()
Hello Mars
['大娛樂家', '歌喉讚3', '1987:黎明到來的那一天', '移動迷宮']
['大娛樂家', '歌喉讚3', '1987:黎明到來的那一天', '移動迷宮']

學習資源對應