跳至主要内容

4 篇文章 含有標籤「程式教學」

檢視所有標籤

電腦科學概論:演算法概要

· 閱讀時間約 6 分鐘

電腦科學概論:演算法入門概要

隨著資訊科技發展,演算法已經無所不在存在我們的生活當中。舉凡上網 google 搜尋資料、下載檔案的壓縮方法、檔案的加密傳輸等,都可以看到演算法運作的蹤跡。一般來說資料結構和演算法是程式設計最基本的內涵,所以有人說:程式設計 = 資料結構 + 演算法。那究竟什麼是演算法/算法呢?

咱們維基百科給了個非常需要慧根才能理解的解釋:

演算法(algorithm),在數學(算學)和電腦科學之中,為任何良定義的具體計算步驟的一個序列,常用於計算、資料處理和自動推理。精確而言,演算法是一個表示爲有限長,列表的有效方法。演算法應包含清晰定義的指令,用於計算函式。

我們把它翻譯成人話吧:

演算法是一個有輸入且有輸出的解決問題的步驟,它具有明確和有限步驟且有效的特性

舉例來說,我們今天要創作一道蔥花蛋或菜脯蛋,我的步驟會是把材料當做輸入:

  1. 放點油
  2. 打蛋
  3. 如果喜歡蔥花可以加入蔥花,如果喜歡菜脯可以加入菜脯(程式術語:if...else 條件判斷)
  4. 放入少許鹽巴
  5. 中火快炒,翻五次面(程式術語:for 迴圈)
  6. 當看到蛋面呈現金黃色時可以起鍋,結束料理(程式術語:while 迴圈)
  7. 好吃的蔥花蛋或菜脯蛋上桌

透過清楚明確的有限步驟,我們可以解決我們想解決的問題並產出我們要的輸出結果

演算法的定義

一般演算法嚴謹的條件必須符合:

  1. 輸入(Input):0 或多個輸入
  2. 輸出(Output):至少有一個回傳結果(有可能回傳 0 或是 null)
  3. 明確性(Definiteness):每一個指令步驟必須明確
  4. 有限性(Finiteness):在有限步驟後一定會結束不會進入無窮迴圈
  5. 有效性(Effectiveness):步驟清楚可行,能使用紙筆計算求解

舉個例子:

下面是一個 Python 判斷是否可以投票的演算法(假設可以投票為 18 歲),仔細看下面的算法雖然簡單但有輸入也有輸出,且有明確有限步驟,步驟可行

def check_can_vote(age):
if age >= 18:
return True
else:
return False

check_can_vote(20)

評估複雜度

事實上,解決一個問題不一定只有一種演算法。那我們怎麼評估演算法的好壞呢?一般來說會有兩種方式:時間複雜度和空間複雜度,比較常見的是使用時間複雜度

時間複雜度(Time Complexity)

想要評估一個演算法執行速度快慢,最直覺的方式是測量演算法計算的時間。但由於執行時間會受不同電腦/計算機機器硬體規格與實作方式影響,很難放諸四海皆準,因此學術上傾向於統計演算法步驟數目,當做時間複雜度可考量。

最常見的評估演算法好壞就是時間複雜度,時間複雜度是指運用概量(漸近分析 asymptotic analysis,例如:當 f(n) = n^2 + 3n 這個函數 n 很大時,3n 會比 n^2 小很多,可以忽略不計。當 n 趨近無限大時,f(n) 等價於 n^2)而非絕對時間(因為會牽涉到電腦/計算機環境變因,所以絕對時間不容易準確),通常我們使用 Big O notation 大 O 符號來表示時間複雜度。假設算法函式所需執行時間為 T(n) ,則我們將其時間複雜度表示為 O(f(n))f(n) 又稱為執行時間的成長率,是影響速度最大的變數。

首先我們先來看 O(1) 的例子,這個演算法執行的步驟是固定的,跟輸入的值無關:

# 不管 n 輸入為多少,這個程式永遠只會執行一次
def print_num(num):
print(num)

print_num(10)

下面這個例子就是 O(n) 的例子,時間複雜度跟輸入的次數有關,隨著 num 變大所需要跑 num 次,是線性時間的成長。這邊 f(n) 等於 n,所以 O(f(n)) 就是 O(n)

def sum_number(num):
total = 0
for n in num:
total += num
return total

sum_number(10)

O(nlog(n))

一般常見的時間複雜度如下圖表是: 電腦科學概論:演算法入門概要

空間複雜度

演算法的空間複雜度是指演算法所需要消耗的儲存記憶體資源。其計算和表示方法與時間複雜度類似,一般都用複雜度的漸近性來表示(asymptotic analysis)。

例如下面這個函式,不管程式跑了多少遍,都不會影響使用的變數數量,故該函式的空間複雜度為 O(1):

def sum_number(num):
total = 0
for n in num:
total += num
return total

sum_number(10)

但下面這個函式,會隨著丟進去的數字而影響變數的量,例如:

輸入為 n,就換產生 n 個變數空間需要儲存,故該函式空間複雜度為 O(n)

def sum_number(num):
total = []
for n in num:
total.append(num)

sum_number(10)

總結

以上簡單介紹了演算法入門教學。隨著資訊科技發展,演算法已經無所不在存在我們的生活當中。舉凡上網 google 搜尋資料、下載檔案的壓縮方法、檔案的加密傳輸等,都可以看到演算法運作的蹤跡,所以值得我們細細品味,接下來我們將介紹常見的經典演算法範例。

參考文件

  1. 台師大演算法筆記
  2. 演算法與資料結構
  3. 大O符號
  4. Algorithm Analysis
  5. All you need to know about “Big O Notation” to crack your next coding interview

image via pandorafmsfreecodecamp

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

· 閱讀時間約 5 分鐘

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

Excel 幾乎是所有職場工作者最常使用的 Office 軟體工具,小至同事間訂便當、飲料,大到進出貨訂單管理,應收應付賬款的財務報表等都有它的身影。在一般工作上,你可能常常需要在不同表單中複製貼上許多的欄位,或是從幾百個列表中挑選幾列依照某些條件來更新試算表內容等。事實上,這些工作很花時間,但實際上卻沒什麼技術含量。你是否曾想過但使用程式語言來加快你的工作效率,減輕瑣碎的重複性無聊工作但又不知道如何開始?

別擔心,這邊我們就要使用 Python 和 Openyxl 這個模組,讓讀者可以輕鬆使用 Python 來處理 Excel 試算表,解決工作上的繁瑣單調工作!

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

Excel 試算表名詞介紹

在正式開始使用 Python 程式來操作 Excel 試算表前我們先來了解 Excel 常見名詞。首先來談一下基本定義,一般而言 Excel 試算表文件稱作活頁簿(workbook),而活頁簿我們會存在 .xlsx 的副檔名檔案中(若是比較舊版的 Excel 有可能會有其他 .xls 等檔名)。在每個活頁簿可以有多個工作表(worksheet),一般就是我們工作填寫資料的區域,多個資料表使用 tab 來進行區隔,正在使用的資料表(active worksheet)稱為使用中工作表。每個工作表中直的是欄(column)從和橫的是列(row)。在指定的欄和列的區域是儲存格(cell),也就是我們輸入資料的地方。一格格儲存格的網格和內含的資料就組成一份工作表。

環境設定

在開始撰寫程式之前,我們先準備好開發環境(根據你的作業系統安裝 Anaconda Python3virtualenv 模組openyxl 模組),關於開發環境設定可以參考:Python Web Flask 實戰開發教學 - 簡介與環境建置,Windows 讀者開發環境可以參考 如何在 Windows 打造 Python 開發環境設定基礎入門教學

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

這邊我們使用 MacOS 環境搭配 jupyter notebook 做範例教學:

# 創建並移動到資料夾
$ mkdir pyexcel-example
$ cd pyexcel-example
$ jupyter notebook

開啟 jupyter notebook 後新增一個 Python3 Notebook

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

首先先安裝 openyxl 套件(在 jupyter 使用 $ !pip install <your-package> 安裝套件):

使用 shift + enter 可以執行指令

!pip install openpyxl

記得要先安裝 openpyxl 模組,若是沒安裝模組則會出現 ModuleNotFoundError: No module named 'openpyxl' 錯誤訊息。

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

讀取 Excel 檔案

  1. 使用 Openpyxl 開啟 Excel 檔案(可以從這邊下載範例 Excel 資料檔案),下載後檔名改為 sample.xlsx,並放到和 jupyter Notebook 同樣位置的資料夾下:

    from openpyxl import load_workbook

    wb = load_workbook('sample.xlsx')
    print(wb.sheetnames)

    執行後可以讀取活頁簿物件(類似讀取檔案)並印出這個範例檔案的工作表名稱:

    ['Sheet1']
  2. 從工作表中取得儲存格(取得 A1 儲存格資料)

    ws['A1'].value
  3. 從工作表中取得欄和列

    列出每一欄的值

    for row in ws.rows:
    for cell in row:
    print(cell.value)

    列出每一列的值

    for column in ws.columns:
    for cell in column:
    print(cell.value)

寫入 Excel 檔案

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

  1. 創建並儲存 Excel 檔案

    from openpyxl import Workbook

    # 創建一個空白活頁簿物件
    wb = Workbook()
  2. 建立工作表

    # 選取正在工作中的表單
    ws = wb.active
  3. 將值寫入儲存格內

    # 指定值給 A1 儲存格
    ws['A1'] = '我是儲存格'

    # 向下新增一列並連續插入值
    ws.append([1, 2, 3])
    ws.append([3, 2, 1])
  4. 儲存檔案

    # 儲存成 create_sample.xlsx 檔案
    wb.save('create_sample.xlsx')

Python 自動化程式設計:如何使用 Python 程式操作 Excel 試算表

總結

以上簡單介紹如何使用 Python 程式操作 Excel 試算表,透過 Python 可以讀取和寫入 Excel 檔案,相信只要能活用就能夠減少一般例行性的繁瑣工作。若需要更多 openpyxl 操作方式可以參考官方文件教學,我們下回見囉!

參考文件

  1. openpyxl - A Python library to read/write Excel 2010 xlsx/xlsm files

(image via matplotlib

Python Web Flask 實戰開發教學 - SQLAlchemy 與 ORM

· 閱讀時間約 8 分鐘

Python Web Flask 實戰開發教學

Web 開發涉及層面很廣,包含了前後端開發、資料庫優化、平行處理、負載平衡、高可用性、資訊安全、雲端伺服器部屬等議題,本系列文章將透過介紹 Python Web Flask 實戰開發來學習現代化網站開發的方方面面。上一篇我們完成了環境建置,這一單元我們將進入 MVC 架構的 Models 部分並學習如何使用 SQLAlchemy 建立和資料庫互動的 ORM 機制。

Python Web Flask 實戰開發教學

什麼是 MVC?

MVC 模式(Model–view–controller)是軟體工程中的一種軟體架構模式,把軟體系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。

  • 控制器(Controller)- 對 Request/Response 進行處理並透過 Controller 把 Model 的資料串接到 View(Templates)。
  • 視圖(View) - 直接面對使用者的使用者介面設計。
  • 模型(Model) - 負責和資料庫互動,儲存資料。

使用 MVC 的好處在於可以用更高階的角度去思考整個程式架構提高程式可重用性和降低程式耦合性。 事實上 Django、Rails 和 ASP.NET MVC 等較成熟的 Web 框架也是參考 MVC 的架構去設計。

什麼是關聯式資料庫(RDB)?

Database 資料庫一個資料儲存的集合,方便我們去讀取新增刪除修改,而 Relational Database(關聯式資料庫)廣泛應用資料庫應用程式中,它把資料儲存在行列表格中,有可能不同資料表的內容會彼此依賴關聯。常見的關聯式資料庫,例如:MySQL、Postgres、Oracle、MSSSQL、SQLite,本文我們將使用 SQLite 這個輕量級的關聯式資料庫來當做範例教學。

什麼是 ORM?

ORM 指的是 Object Relational Mapping(物件關聯對應),是一種程式設計技術,用於實現物件導向程式語言裡不同類型系統的資料之間的轉換。一般而言物件導向是從軟體工程基本原則(例如:耦合、聚合、封裝)的基礎上發展起來的,然而關聯式資料庫則是從數學理論發展而來的,兩套理論存在顯著的區別。為了解決這個不符合的現象,物件關聯對映技術搬演著中介層的角色,讓開發可以使用物件方式來操作資料庫,而不用使用 SQL 語法,當然若是要使用複雜的操作,仍需要使用到 SQL 語法。

更多 SQL 語法學習:SQL語法教學- 1Keydata

Python Web Flask 實戰開發教學

Flask SQLAlchemy 使用設定

SQLAlchemy 是 Python 社群最廣泛使用的 ORM 套件。為了方便使用 ORM 來操作資料庫,我們使用 SQLAlchemy 的封裝 Flask SQLAlchemy 來進行 Models 的建立(當然你也可以單獨使用 SQLAlchemy)。以下是 SQLAlchemy 使用的簡單範例:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 設定資料庫位置,並建立 app
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
# 建立資料表欄位
from main import db

class Todo(db.Model):
# __table__name = 'user_table',若不寫則看 class name
# 設定 primary_key
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(80))

def __init__(self, content):
self.content = content

def __repr__(self):
return '<Todo %r>' % self.content
from flask_script import Manager, Server
from main import app
from models import Todo

# 設定你的 app
manager = Manager(app)
# 設定 python manage.py runserver 為啟動 server 指令
manager.add_command('runserver', Server())

# 設定 python manage.py shell 為啟動互動式指令 shell 的指令
@manager.shell
def make_shell_context():
return dict(app=app, Todo=Todo)

if __name__ == '__main__':
manager.run()

操作指令:

$ python manage.py shell
>>> db.create_all()

CRUD 操作設計

CRUD 是一般網路應用程式最常見的資料庫操作(create, read, update, delete),接著我們要使用 session 來操作我們的 CRUD 功能,首先先在終端機中輸入 $ python manage.py shell 後進行資料庫指令模擬操作(要注意的是 Flask SQLAlchemy 針對每個 request 會創建一個新的 session,若沒有 commit 的操作即被丟棄):

  1. 新增(Create) 新增一筆資料後將它加到 db.session 中,完成 commit:

    >>> todo = Todo(content='hacking')
    >>> db.session.add(todo)
    >>> db.session.commit()

2. 讀取(Read)
Model.query 是 db.session.query(Model) 的簡寫,所以我們可以使用以下方式讀取資料庫資料:

取得所有 todo 資料

todos = Todo.query.all() todos

限制 1 筆資料

todos = Todo.query.limit(1).all()

正向/逆向排序

todos = Todo.query.order_by(Todo.content).all() todos = Todo.query.order_by(Todo.content.desc()).all()

取得第一筆資料

todo = Todo.query.first()

取得 primary key=1 一筆資料

todo = Todo.query.get(1)


- 分頁(Pagination)

todos = Todo.query.paginate(1, 10)

總頁數

todos.pages

上/下一頁

todos.prev() todos.next()


- 條件查詢(Filter)

todo = Todo.query.filter_by(content='hacking').first()

todos = Todo.query.filter(Todo.id > 1).all()


3. 修改(Update)

```python
todo = Todo.query.filter_by(contant='hacking').update({
'content': 'reading'
})
db.session.commit()
  1. 刪除(Delete)

    >>> todo = Todo.query.filter_by(content='reading').first()
    >>> db.session.delete(todo)
    >>> db.session.commit()

資料庫關聯用法

在關聯式資料庫中,最重要的就是資料表之間的關聯,透過關聯的使用,可以讓我們取得我們想要的資料。舉例而言,一篇部落格文章通常會對應多則評論,所以若是建立好關係則可以透過文章去取得所有和這篇文章有關的評論。同理,一篇文章通常會有多個 tag,而一個 tag 通常對應多篇文章,所以是個多對多關係。

  • 一對多
# 建立資料表欄位
from main import db

class Todo(db.Model):
# __table__name = 'user_table',若不寫則看 class name
# 設定 primary_key
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(80))
user_id = db.Column(db.String(80), db.ForeignKey('user.id))

def __init__(self, content):
self.content = content

def __repr__(self):
return '<Todo %r>' % self.content

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
todos = db.relationship(
'Todo',
backref='user', # ref 可以讓我們使用 Todo.user 進行對 User 操作
lazy='dynamic' # 有使用才載入,提昇效能
)

def __init__(self, name):
self.name = name

def __repr__(self):
return '<User %r>' % self.name

操作方式:

>>> user = User.query.get(1)
>>> new_todo = Todo('Booking')
>>> new_todo.user_id = user.id
>>> db.session.add(new_todo)
>>> db.session.commit()
>>> user.todos
  • 多對多
tags = db.Table('todo_tags',
db.Column('todo_id', db.Integer, db.ForeignKey('todo.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)

# 建立資料表欄位
from main import db

class Post(db.Model):
# __table__name = 'user_table',若不寫則看小寫 class name
# 設定 primary_key
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
content = db.Column(db.String(80))

def __init__(self, title, content):
self.title = title
self.content = content

def __repr__(self):
return '<Todo %r>' % self.content

class Tag(db.Model):
# __table__name = 'user_table',若不寫則看 class name
# 設定 primary_key
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))

def __init__(self, title):
self.title = title

def __repr__(self):
return '<Tag %r>' % self.title

操作方式:

# 創建 3 個 todo
>>> todo_1 = Todo('Python')
>>> todo_2 = Todo('JS')
>>> todo_3 = Todo('R')

>>> tag_1 = Tag('coding')
>>> tag_1.tags = [todo_1, todo_2]
>>> db.session.add()
>>> db.session.commit()

Python Web Flask 實戰開發教學

Flask Migration(Alembic)使用

隨著網路應用程式的發展,我們的 models 會不斷變更,為了記錄所有有關 models 的改動,我們使用 Flask-Migrate 這個 extensions。所有 models 改動都會記錄在 migration 檔案中,就像是資料庫的 git 一樣方便版本控制。

安裝 Flask-Migrate

$ pip install Flask-Migrate

將 db 加入 Flask-Migrate 控制:

from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager, Server
from main import app
from models import Todo

# 設定你的 app
manager = Manager(app)
# 設定 python manage.py db 來管理 models
manager.add_command('db', MigrateCommand)
# 設定 python manage.py runserver 為啟動 server 指令
manager.add_command('runserver', Server())

# 設定 python manage.py shell 為啟動互動式指令 shell 的指令
@manager.shell
def make_shell_context():
return dict(app=app, Todo=Todo)

if __name__ == '__main__':
manager.run()

使用 Flask-Migrate 操作 DB:

# 初始化
$ python manage.py db init
# 記錄 model 變化
$ python manage.py db migrate
# 更新同步到 db
$ python manage.py db upgrade
# 查詢指令
$ python manage.py db --help

總結

本文介紹了資料庫、關聯式資料庫、ORM 的概念,我們也實際使用了 Flask SQLAlchemy 和 Flask-Migrate 來操作我們的資料庫。在接下來章節中我們將持續介紹 Python Web Flask 實戰開發,並學習現代化網站開發的方方面面。

延伸閱讀

  1. Wiki MVC
  2. Python Web 程式設計入門實戰線上課程

(image via basicsofwebdevelopment

Python 101 快速入門教學

· 閱讀時間約 14 分鐘

Python 101 快速入門教學

Python Web 程式設計入門實戰課程準備上課囉!

Python 是一種物件導向、直譯式的跨平台電腦程式語言,它包含了一組功能完備的標準庫和豐富套件生態系,可以輕鬆完成很多常見的任務(例如:讀寫檔案、自然語言處理、網路爬蟲、網站開發、機器學習等),因為它可以很輕易整合其他底層語言,所以又稱為膠水語言。它的語法簡單,與其它大多數程式設計語言使用大括弧不一樣,它使用縮進來定義語句塊。由於具備簡潔易學等特性,許多開發者推薦 Python 為初學者第一個學習的程式語言。由於版本更迭,我們接下來討論的主要是以 Python3 為主,若電腦沒有安裝的話,你可以在官方網站下載,若你不是安裝 Anaconda 這個 all-in-one 版本的話(自帶許多套件和科學運算工具),記得要安裝 pipIPython

Python 設計風格

Python 主要設計的原則和特色就在於簡潔:應該會有一種明顯的作法(最好也只有一種),可以完成工作。

更多有關 Python 設計風格可以在終端機進入 python3 互動模式後輸入 import this

$ python3
Python 3.5.2 (default, Oct 11 2016, 05:00:16)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

空白格式

首先,我們要了解 Python 和其他語言最大的不同就是使用縮排來切分程式碼,這和其他語言使用 不同。

for i in [1, 2, 3]:
print(i)
for j in [1, 2, 3, 4]:
print(j + i)

print('finish')

不過初學者很容易在縮排遇到問題,若是出現以下訊息就可以檢視是否哪裡縮排有問題:

IndentationError: expected an indented block

模組

在 Python 生態系中有豐富的模組和工具。一般情況預設不會載入任何模組,但當你有特定開發需求可以使用第三方工具將模組匯入(import)。若是當模組名稱很長時通常我們會使用別名。

import re as regex

my_regex = regex.compile('[0-9]+', regex.I)

若是只是需要模組中的特定功能,也可以使用比較精準的引入方式 from import,引入到整個命名空間中,使用時前面就不用寫模組名(但要注意有可能覆寫):

from collections import defaultdict, Counter
lookup = defaultdict(int)
my_counter = Counter()

資料型別

在 Python 有以下幾種內建的資料型別,基本資料型別有 Number、String、Boolean

  1. 數字(Number)

    num1 = 3
    num2 = 2
    num3 = num1 / num2
  2. 字串(String) 字串使用上會使用單引號或雙引號成對包起(', ")

    str = 'data engineer'
    # 字串長度
    len(str)
    # 原始字元
    es_str = r'\t'
    # 2
    len(es_str)

    若是多行的情形:

    multi_line_str = """
    多行
    多行
    """
  3. 布林值(Boolean) 決定邏輯判斷,TrueFalse。注意在 Python 中布林值首字是大寫

    is_num_bigger_than_one = 1 < 2

    在 Python 中 None 地位類似於 null

    x = None
    print(x == None)

    以下為 Python 的 Falsy 值:

  • False

  • None

  • []

  • ""

  • set()

  • 0

  • 0.0

    可以搭配 and, or, not 使用

  1. 列表(List) 列表可以說是 Python 中最基礎的一種資料結構。所謂列表指的就是一群按照順序排序的元素(類似於其他程式語言的 array,但多一些額外功能)。

    list_num = [1, 2, 3]
    list = ['string', 1, [], list_num]
    list_length = len(list_num)
    num_sum = sum(list_num)

    print(list_length)
    print(num_sum)

    運用 [] 取值(index 從 0 開始):

    x = range(10) # [0, 1, 2, ..., 9]
    zero = x[0] # 0
    nine = x[-1] # 9
    x[0] = -1

    切割([起始 index, 結束 index 但不包含]):

    print(x[:3]) # [-1, 1, 2]
    print(x[3:]) # [3, 4, 5,..., 9]
    print(x[1:5]) # [1, 2, 3, 4]
    print(x[0:-1]) # [1, 2, ..., 8]
    print(x[-1:-1]) # [-1, 1, ..., 9]

    檢查元素是否在列表中(逐一檢查,效率較差):

    1 in [1, 2, 3] # True

    串接列表:

    x = [1, 2, 3]
    x.extend([4, 5, 6])
    x = [1, 2, 3]
    y = x + [4, 5, 6]
    x = [1, 2, 3]
    x.append(0) # [1, 2, 3, 0]

    賦值方式:

    x, y = [1, 2]
    _, y = [1, 2]
  2. 元組(Tuple) Tuple 類似於 List 的兄弟,比較大差別在於 Tuple 是 immutable,也就是說宣告後不能修改。列表使用 [],而元組使用 ()

    my_list = [1, 2]
    my_tuple = (1, 2)
    my_list[1] = 3

    try:
    my_tuple[1] = 4
    except TypeError:
    print('cannot modify a tuple')

    多重賦值

    x, y = 1, 2
    x, y = y, x # x == 2, y == 1
  3. 字典(Dictionary)

    字典類似 map,包含鍵值與對應的值,可以快速取出對應值:

    dict1 = {} # 建議寫法
    dirct2 = dict()
    grades = { 'Mark': 70, 'Jack': 40 }

    grades['Mark']

    給定值:

    grades['happycoderorg'] = 100
    len(grades) # 3

    使用事先檢驗鍵或是使用 get 方法:

    try:
    grade = grades['XD']
    except KeyError:
    print('no grade for XD')

    grades.get('XD', 40) # 若無則使用 default 值

    取出所有值:

    grades = { 'Mark': 70, 'Jack': 40 }
    grades.keys() # 所有鍵值組成的 list
    grades.values() # 所有值組成的 list
    grades.items() # 所有鍵值組成的 tuple of list [('Mark', 70)]

    defaultdict

    當你檢查一個不存在的鍵值時,會用零參數函式添加一個事先設定的新值,這是一種比較優雅的作法

    在介紹 defaultdict 之前先介紹一般作法

    # 例外處理
    word_counts = {}
    for word in document:
    try:
    word_counts[word] += 1
    except KeyError:
    word_counts[word] = 1
    # 使用 get
    word_counts = {}
    for word in document:
    previous = word_counts.get(word, 0)
    word_counts[word] = previous_count + 1

    defaultdict 作法(不用每次檢查鍵直視否存在)

    # 匯入 defaultdict
    from collections import defaultdict

    word_counts = defaultdict(int) # 會生成 0
    for word in document:
    word_counts[word] += 1

    也可以使用 list 或 dict 甚至是自己定義的函式來做為 defaultdict 的零參數函式:

    dd_list = defaultdict(list)
    dd_list[1].append(2) # { 1: [2] }

    dd_list = defaultdict(list)
    dd_list['Mark']['City'] = 'Bay Area' # { 'Mark': { 'City': 'Bay Area'} }

    dd_list = defaultdict(lambda: [0, 0])
    dd_pair[2][1] = 1

    Counter:可以把一系列值轉成類似 defaultdict(int) 的東西,裡面每個值都對應到相應的數量,主要會使用來建立直方圖

    from collections import Counter

    c = Counter([0, 1, 2, 0]) # { 0: 2, 1: 1, 2: 1 }
    word_counts = Counter(document)

    每個 Counter 實例都有個 most_common 的方法

    for word, count in word_counts.most_common(10):
    print(word, count)
  4. 集合(Set) 集合類似數學中的集合,裡面包含不重複的元素值

    s = set()
    s.add(1) # { 1 }
    s.add(2) # { 1, 2 }
    s.add(2) # { 1, 2 }
    len(s) # 2
    1 in s # True

    集合在判斷元素是否存在的效率相對較好,此外,對於判斷不重複值也很方便

    list_item = [1, 2, 3, 1, 2, 3]
    set_item = set(list_item) # {1, 2, 3}
    lsit(set_item) # [1, 2, 3]

解析式列表(comprehensive list)

在 Python 我們通常會需要把某個 list 轉換成另外一個 list,比如只挑選其中幾個元素,或是對期中某些元素進行轉換。

even_numbers = [x for x in range(5) if x % 2 == 0]
squares = [x for x in range(5) if x % 2 == 0]
even_squares = [x * x for x even_numbers]

# 不操作值的話
zeros = [0 for _ in even_numbers] # 和 even_numbers 長度一樣值都為 0 的串列

建立 set 和 dict

square_dict = { x : x * x for x in range(5) }
square_set = { x * x for x in [1, -1] } # { 1 }

pairs = [(x, y) for x in range(10) for y in range(10)] # (0, 0), (0, 1)
p = [(x, y) for x in range(3) for y in range( x + 1, 10)]

函式

函式(function)是重複使用的程式區塊,有輸入輸出。在 Python 中我們會使用 def 來定義函式:

def sum(x, y):
return x + y

Python 的函數和 JavaScript 一樣,屬於一級函式(first-class)。我們可以將函數指定給某個變數,然後把它傳給某個函數中,就像是平常把參數傳入函式一樣。

def apply_f(fun):
return fun(3, 2)

def sum(x, y):
return x + y

sum_fun = sum
num = apply_f(sum)
print(num) // 5

匿名函數使用方式(類似 ES6 arrow function 簡化寫法,前面是參數後面是操作):

y = apply_f(lambda x: x + 4)

函式參數預設值:

def my_print(message="default"):
print(message)

my_print("hello python")
my_print()

args 與 kwargs

若我們希望函式可以接受任意參數,我們可以使用 args(由無名稱參數組成的元組) 和 kwargs(由無名稱參數組成的字典):

def magic(*args, **kwargs):
print('unnamed args:', args)
print('keywords args:', kwargs)
magic(1, 2, key='word', key2='word2')

# unnamed args: (1, 2)
# keywords args: {'key': 'word', 'key2': 'word2'}

可以用來協助建立以函式當做參數的高階函式:

def double(f):
def g(*args, **kwargs):
return 2 * f(*args, **kwargs)
return g

def f2(x, y):
return x + y

g = doubler_correct(f2)
print(g(1, 2))

常見內建函式

除了自己可以建立函式外,Python 本身也提供許多好用的內建函式:

  • all:列表中所有元素為真

    all([True, 1, { 3 }]) # True
    all([True, 1, { }]) # False
    all([]) # True,沒有元素為假
  • any:列表中只要有任何元素為真

    any([True, 1, {}]) # True
    any([]) # False,沒有元素為真
  • enumerate:列舉

    我們常需要反覆取得列表中每個元素及其索引值

    # choice 1
    for i in range(len(documents)):
    document = documents[i]
    do_something(i, document)

    # choice 2
    i = 0
    for document in documents:
    do_something(i, document)
    i += 1

    使用 enumerate:

    for i, document in enumerate(documents):
    do_something(i, document)

    # 僅需要 index
    for i, _ in enumerate(documents): do_something(i)
  • zip:合併將多個 list 合併成 tuple of list

    list1 = ['a', 'b', 'c']
    list2 = [1, 2, 3]
    zip(list1, list2) # [('a', 1), ('b', 2)]

    zip(*[('a', 1), ('b', 2), ('c', 3)]) == zip(('a', 1), ('b', 2), ('c', 3)) # [('a', 'b', 'c'), ('1', '2', '3')]
    # * 可以參數拆分
    def add(a, b): return a + b
    add(1, 3) # 4
    add([1, 3]) # TypeError
    add(*[1, 3]) # 4
  • range:取得一序列

    range(0, 10) # 0, 1, 2, ... 9
  • random:生成隨機數字

    import random

    randoms = [random.random() for _ in range(4)] # 生成長度為 4 內涵值為 0 - 1 不含 0 的 list

    事實上,random 產生的是偽隨機數字,透過 seed 設定可以取得同樣值

    import random
    random.seed(10) # 把 seed 設為 10
    print(random.random()) # 0.5714025946899135
    random.seed(10) # 把 seed 設為 10
    print(random.random()) # 0.5714025946899135

    隨機產生範圍內數字:

    random.randrange(10)
    random.randrange(3, 6)

    針對列表隨機排列:

    num = range(10)
    random.shuffle(num)
    random.choice(['Mark', 'Bob', 'Jack'])

    隨機取樣不放回:

    nums = range(10)
    random.sample(nums, 3)

    隨機取樣放回:

    nums = range(10)
    random.choice(nums, 3)
  • sort:針對 list 進行排序(由小到大),會改變原來的 list。sorted 不會改變原來 list

    x = [4, 1, 2, 3]
    y = sorted(x) # [1, 2, 3, 4] 不會改變到 x
    x.sort() # x 變成 [1, 2, 3, 4]

    若想改成由大到小排序

    x = sorted([-4, 1, -2, 3, key=abs, reverse=True]) # [-4, 3, -3, 2] 絕對值由大到小

    若是在 key 指定一個函數,就會用這個函數結果去做排序

    wc = sorted(word_counts.items(), key=lambda (word, count): count, reverse=True) # 針對單字數量多到小排序
  • partial:使用函式工具創建另一個函式

    from functools import partial
    def exp(base, power):
    return base ** power
    two_to_the = partial(exp, 2)
    print_two_the(3)
  • map:

    def multiply(x, y):
    return x * y
    map(multiply, [1, 2], [1, 2]) # [1, 4]
  • filter:

    def is_even(x):
    return x % 2 == 0
    filter(is_even, [2, 5, 6]) # [2, 6]
  • reduce:

    def multiply(x, y):
    return x * y
    reduce(multiply, [1, 2, 3]) # 1 * 2 * 3

控制流程

  1. if...elif...else

    if 1 > 2:
    message = 'if onlt 1 were greater than two'
    elif 1 > 3:
    message = 'elif == else if'
    else:
    message = 'else'

    三元運算子:

    parity = 'even' if x % 2 == 0 else 'odd'
  2. for...in

    較複雜情況我們會搭配 continue 和 break 使用:

    for x in range(10): # 0...9 不含 10
    if x == 3:
    continue
    if x == 5:
    break
    print(x)
  3. while

    x = 0
    while x < 10:
    print('x is less than 10')
    x += 1

生成器(generator)與迭代操作

事實上,list 有個問題就是很容易變得很大。例如:range(1000000) 就會製造出一個包含一百萬的元素的 list。若是想要使用其中幾個元素,效能就會變得很差。此時使用生成器(generator)就是一個每次只生成所需數值的一種 lazy 作法。

使用函式和 yield

def lazy_range(n):
i = 0
while i < n:
yield i
i += i
for i in lazy_range(10):
do_something(i)

或是在小括號使用運算解析式

lazy_evens_below_20 = (i for i in lazy_range(20) if i % 2 == 0)

另外,每個 dict 都有一個叫做 items() 的方法

iteritems() # 可以一次生成一個鍵值對

裝飾器(decorator)

裝飾器本身是一個函式,主要是借助閉包力量產生一個可以修飾函式的函式:

@print_fun(title='title:')
def add(*tup):
return sum(tup)

# 以下兩者相同
add(1, 2, 3, 4)
add = print_fun(title='title:')(add)

# 裝飾器撰寫
def print_fun(title):
def decorator(func):
def modified_func(*args, **kwargs):
result = func(*args, ** kwargs)
print(title, result)
return modified_func
return decorator

正規表達式

與許多程式語言一樣,Python 同樣提供正規表達式的用法,可以方便擷取文字。

import re

print.all([
re.match('a', 'cat'),
re.search('a', 'cat')])

物件導向程式設計(OOP)

Python 也是物件導向程式語言:

class Set:
def __init__(self, values=None):
# 建構函數
s1 = Set()
s2 = Set([1, 2, 3])
self.dict = {}
if values is not None:
for value in values:
self.add(value)
def __repr__(self):
return "Set" + str(self.dict.keys())
def add(self, value):
self.dict[value] = True
def contains(self, value):
return value in self.dict
def remove(self, value):
del self.dict[value]
# 使用物件
s = Set([1, 2, 3])

s.add(4)

print(s.contains(4))

例外狀況

若是程式出現錯誤的話會送出 exception,若不妥善處理的話程式很有可能會掛掉,在 Python 中例外處理可以使用 try...except:

try:
print(1/0)
except ZeroDivisionError:
print('cannot divide by zero')

總結

本文快速介紹了 Python 的基礎概念,當讀者學會了 Python 後,事實上可以嘗試使用 Python 開發不同的網路應用程式或是資料科學,甚至是自然語言處理的應用。更多 Python 教學內容都在 Python Web 程式設計!

延伸閱讀

  1. 15 Essential Python Interview Questions
  2. 8 Essential Python Interview Questions*
  3. Python Interview Questions
  4. What are good Python interview questions?
  5. Top 40 Python Interview Questions & Answers
  6. Top Python Interview Questions And Answers
  7. Python Interview Questions
  8. 12步教你理解Python装饰器

(image via fedoramagazine