顯示具有 python 標籤的文章。 顯示所有文章
顯示具有 python 標籤的文章。 顯示所有文章

2025年1月5日 星期日

Python當中取得UTC時間的方法

在Python當中,有一些取得UTC時間的方法。這裡介紹一個不用安裝package的方法。

from datetime import datetime, timezone

# 設定時間為2025/1/1 00:00:00, 無時區資訊
dtm = datetime(2025, 1, 1)

# 根據系統設定加上時區資訊,時間為2025/1/1 00:00:00 local time
dtm_local = dtm.astimezone()

# 加上UTC時區資訊,時間為 2025/1/1 00:00:00 utc time
utc_20250101 = dtm.astimezone(timezone.utc)

# 將2025/1/1 local time轉換成utc time,按照兩地的時差
utc_from_dtm_local = dtm.astimezone().astimezone(timezone.utc)

如果是取得當下的時間,可以用以下:

utc_now = datetime.now(timezone.utc)
# 或是 datetime.now().astimezone().astimezone(timezone.utc)

2018年1月20日 星期六

Python 3 的 class: instance method、class method、static method的區別

在Python 3當中,一個類別(class)的成員函式(member function/method),可以定義成instance method、class method、或是static method。instance method是物件層級的函式,而class method和static method是類別層級(無法存取物件層式的屬性)的函式(基本大觀念假設不細講)。以下大致比較一下這三者:
  • instance method
    • 物件層級
    • 定義前的修飾字:無
    • 定義語法: def method_name(self, ...): ... 
    • self 是該instance 的 reference
    • 呼叫方法: instance.method_name(...)
    • method當中使用物件/類別屬性的方式
      • self.AAA = 10 # 將物件層級屬性AAA設為 10
      • type(self).BBB = 20 # 將物件類別的類別層級屬性BBB設為 20
      • ClassC.CCC = 30 # 將ClassC的類別層級屬性CCC設為30,ClassC未必是自己的類別
  • class method 
    • 類別層級 (假設類別為 C)
    • 定義前的修飾字:@classmethod
    • 定義語法: def method_name(cls, ...): ...
    • cls 是該 class 的 reference
    • 呼叫方法:C.method_name(...)  或  instance_of_c.method_name(...)
    • method當中使用類別屬性的方式
      • cls.BBB = 20 # 將當下類別層級屬性BBB設為 20
      • C.BBB = 20 # 將C的類別層級屬性BBB設為 20
      • ClassC.CCC = 30 # 將ClassC的類別層級屬性CCC設為30,ClassC未必是自己的類別
  • static method
    • 類別層級 (假設類別為 C)
    • 定義前的修飾字:@staticmethod
    • 定義語法: def method_name(...): ...
    • 呼叫方法:C.method_name(...)  或  instance_of_c.method_name(...)
    • method當中使用類別屬性的方式
      • C.BBB = 20 # 將C的類別層級屬性BBB設為 20
      • ClassC.CCC = 30 # 將ClassC的類別層級屬性CCC設為30,ClassC未必是自己的類別
class method 和 static method 的觀念非常類似,主要差別只在於class method可以使用 cls.BBB的方式來使用自己的類別屬性。上例當中,使用 cls.BBB 和 C.BBB 看似一樣,實際上有一點小差別。當我們用 C1 繼承 C 的時候,cls.BBB 會是 C1 的類別屬性,但是 C.BBB 仍是 C的類別屬性,不會因為繼承而變成 C1 的屬性。最主要的差別就在這裡了。


Python讀寫utf-8的文字檔

Python開啟文字檔的時候,encoding這個參數是大家比較不常用的:
with open(f_name, 'w', encoding='utf-8') as f_out:
    ....
    ....
encoding是指文字檔的編碼。如果要讀寫utf-8的文字檔,編碼會是以下兩者之一:
  • encoding='utf-8':純utf-8文字檔 (常見於linux)
  • encoding='utf-8-sig':檔案開頭有加BOM (常見於Windows上的utf-8檔)
在讀檔時,如果用其中一者開啟失敗,可以換一個試看看。

encoding如果不指定,會是作業系統的預設選項 (使用者應該可以改,但我沒試過)。如果在linux系列,多半會是utf-8 (有不是的嗎?);如果在較舊的Windows,則是 big5/cp950 。當我們的程式碼可能在多個平台執行時,最好把encoding作明確的設定,免得在讀寫共用的文字檔案時,因為encoding而發生執行錯誤。

2017年12月16日 星期六

Python: 在自訂的exception handling中加入traceback來取得錯誤的行號

在我們寫python程式的過程中,難免會在測試時看到程式因Exception/Error而中止。
中止的時候,會看到畫面印出在哪一行出錯。例如程式當中有一行:
 
int('abc') 
我們會看到以下的錯誤訊息。錯誤訊息主要分成三個部份,分別用不同的底色來表示(之後依序簡稱訊息1、訊息2、訊息3):
Traceback (most recent call last):
  File "./traceback_test.py", line 7, in <module>
    int('abc')
ValueError: invalid literal for int() with base 10: 'abc'
 如果我們用了try...except如下:
try:
    int('abc')
except Exception as e:
    print(e)  
則會得到以下的結果,只有訊息3,錯誤的行號不見了:
invalid literal for int() with base 10: 'abc'
要如何取得訊息1(以取得發生錯誤的行號)和訊息2,甚至是完全一樣的資訊呢?我們需要使用traceback這個module。以下是一個簡單的範例供參考,如果想要更深入瞭解,可以去找python文件:
#!/usr/bin/python3

import sys, traceback

try:
    int('abc')
except Exception as e:
    ex_type, ex, tb = sys.exc_info() #此行取得ex_type, ex, tb
    #印出訊息1: File "./traceback_test.py", line 7, in <module>\n   int('abc')
    print('----traceback.print_tb(tb)----')
    traceback.print_tb(tb)
    #印出訊息2:ValueError
    print('----print(ex_type.__name__)----')
    print(ex_type.__name__)
    #印出訊息3:invalid literal for int() with base 10: 'abc'
    print('----print(ex)----')
    print(ex)
    #印出完整訊息,含Traceback (most recent call last):
    print('----format_exception(ex_type, ex, tb)----')
    msg_lines = traceback.format_exception(ex_type, ex, tb)
    for l in msg_lines:
        print(l, end='')
    #印出完整訊息,含Traceback (most recent call last):
    print('----traceback.format_exc()----')
    msg_str = traceback.format_exc()
    print(msg_str)

2017年1月21日 星期六

Tkinter:如何指定視窗的位置

使用Tkinter寫視窗程式,是可以指定視窗出現的位置的。我們可以利用geometry()這個member function:
import tkinter as tk
root = tk.Tk()
root.geometry()   # 會顯示例如   300x200+100+50  的字串,意思是 寬=300、高=200、x=100、y=50
root.geometry('300x200+10+5') # 寬高不變,(x, y) 改成 (10, 5)
也可以寫一個小function來處理:
def setxy(w, x, y):
    w.geometry('{0}+{1}+{2}'.format(w.geometry().split('+')[0], x, y))

2017年1月19日 星期四

如何設定python程式檔的編碼?

在我們的python程式當中,難免會使用到中文字串。我們的程式碼,常見的會是big5(cp950)編碼或是utf-8編碼。我們需要在程式的開始註明檔案的編碼,python才能正確處理中文字串。

註明檔案編碼的方式,是在程式碼「開頭兩行的其中一行」註明,該行格式簡單來說是:
  1. # 或是 [一些空白字元] # 開頭
    • 也就是說,該行只能有註釋,而註釋符號 # 之前可以有一些空白
  2. 包含字串  「coding: utf-8」 或 「coding: cp950」
    • python看到「coding:」之後,會抓取後面的詞當作是編碼
再強調一次,這一行編碼註記必須出現在原始碼的前兩行。

python是用regular expression來處理該行的。所以,如果你要使用 cp950,以下提供一些範例:
  • # -*- coding: cp950 -*-
  • # encoding: cp950
  • # coding: cp950
  • # I love coding: cp950 and you?
  • # ccccccccccccccccccoding: cp950 0000000000000
不過,如果程式要讓人家容易看懂,還是不要太搞怪比較好。


2016年7月28日 星期四

Django筆記

本筆記的目的,在簡述一些重點。比較詳細的資料還是需要上網找,不過看了本筆記之後應該有助於理解網路上的資料(例如 https://docs.djangoproject.com/en/1.9/intro/tutorial01/)。

  • 安裝Django
  • 建立與啟動server
    • 指令:django-admin startproject <server name>
      • 會產生一些和server相關的目錄和檔案
      • 目錄有:
        • server_name/
        • server_name/server_name/
    • 啟動server:python manage.py runserver [[<ip>:]<port number>]
      • 省略ip和port number:預設值127.0.0.1:8000,將造成只有localhost可連線
      • 省略ip:效果同 127.0.0.1:port_number,也會造成只有localhost可連線
      • 當要提供外部服務時,我們要將ip改成0.0.0.0或真實ip,差別會在一台電腦有多個ip的時候(個人沒有實測過):
        • 0.0.0.0:port:佔用該電腦所有ip的port
        • aa.bb.cc.dd:port:只佔用aa.bb.cc.dd的port
    • 網站的功用:根據url選擇application、呼叫application、回傳content。
    • 也就是說,產生content是application的責任。
  • 建立與啟用application
    • 網站會根據url的doc path來選擇將此query送給對應的application去處理
    • 建立app:python manage.py startapp <app name>
      • app目錄為 server_name/app_name
    • url的對應
      • url的對應方法,主要是記錄在 server_name/server_name/urls.py 中
      • 如果有多個applications,把所有的對應放在一起可能有些混亂,所以django網站上的tutorial建議在建立一個 server_name/app_name/urls.py 檔,然後在 server_name/server_name/urls.py 當中來引用。
    • server_name/server_name/settings.py
      • 如果這個application會被其它projects用到,那麼要在settings.py當中設定
      • 如果透過django的database程式架構,那要在settings.py當中設定database的相關資訊
        • 透過django的程式架構,如果後端要更換資料庫系統,會比較方便,可能改設定就好。
        • 執行 python manage.py migrate,會根據application的設定來建立資料庫。

2015年10月30日 星期五

python3中使用http.server撰寫web server的感想

感想很簡單:好像不太穩定,頻繁連線後、開啟超過一天,似乎都容易發生回應速度變超慢的情形。如果要長期開啟,還是使用django吧。

2015年10月16日 星期五

python:正在執行的script如何取得自己的檔案名稱


在python script當中:
  1. 可以用 __file__ 來取得script的檔名
    • 比較新的版本會取得完整路徑,比較舊的版本有可能會取得相對路徑,可以呼叫 os.path.realpath(__file__) 來轉成完整路徑檔名。
  2. 可以用 sys.argv[0] 來取得 script 的檔名
兩者的差別在哪呢? 以下是一例,假設我們寫了一個 main function 如下:
  • A.py
#!/usr/bin/python3
# encoding: utf-8
import sys

def main(argv):
    print(__file__)
    print(argv[0])

if __name__ == '__main__':
    main(sys.argv)
後來,在另一個B.py中,我們發現可以直接使用 A 的 main(本例過於簡化):
  • B.py
#!/usr/bin/python3
# encoding: utf-8
import sys
from A import main


if __name__ == '__main__':
    main(sys.argv)
執行結果,A.py會印出 A.py、A.py,而B.py會印出 A.py、B.py,這就是兩者的差別。在寫重覆使用的模組時,要根據所需要的效果來挑選正確的用法。

2015年10月9日 星期五

Python 的一些 list 操作

  • append, extend, 和 +

>>> a = [1,2,3]            # 將 a 設為 list
>>> a.append(4)         # 將 4 附加到 a,a被修改了,呼叫之後回傳None。
>>> a
[1, 2, 3, 4]
>>> a + [4]                 # 呼叫之後回傳「4 附加到 a」,不會更改a。
[1, 2, 3, 4, 4]
>>> a                         # a沒被修改
[1, 2, 3, 4]
>>> a + [4,5,6]           # 會回傳「4,5,6 附加到 a」,不會更改a。
[1, 2, 3, 4, 4, 5, 6]
>>> a
[1, 2, 3, 4]
>>> a.extend([4,5,6])   # 效果和 extend 類似。不過extend也是直接修改a、呼叫時回傳None。
>>> a
[1, 2, 3, 4, 4, 5, 6]
>>> print(a+[7,8])                   # 直接要將list合併之後的結果當參數時,要用+
[1, 2, 3, 4, 4, 5, 6, 7, 8]
>>> print(a.extend([7,8]))       # 如果誤用extend,會變成傳入None。
None
>>> print(a)         # 事情也不嚴重,先extend再傳入就可以了。關鍵在你想要a之後保留哪個值。
[1, 2, 3, 4, 4, 5, 6, 7, 8]

2014年12月30日 星期二

Python logging (一)

以下是python logging的徹底研究,大家可以一條一條的讀下去,相信會對logging模組的使用有幫助。
  1. 為何要使用logging?
    • 程式執行中,有時需要輸出一些訊息來記錄執行狀態、錯誤、...
    • 訊息可以寫在檔案中,較緊急的訊息可能同時要寫檔案、console顯示、寄email提醒、(或其它管道)
    • 使用logging模組,可以讓我們方便達成單一訊息送至多個管道的功能(個人覺得是最大的好處)
  2. 只在console顯示 (stderr, 預設等級warning以上才顯示,從輕到嚴重分成debug, info, warning, error, fatal/critical 五級)
  3. >>> import logging
    >>> logging.debug('Hi')
    >>> logging.info('Hi')
    >>> logging.warning('Hi')
    WARNING:root:Hi
    >>> logging.error('Hi')
    ERROR:root:Hi
    >>> logging.fatal('Hi')
    CRITICAL:root:Hi
    >>>
  4. 改成從sys.stdout輸出,層級是debug以上就顯示,可使用logging.basicConfig:
  5. >>> import sys
    >>> import logging
    >>> logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
    >>> logging.debug('Debug Msg')
    DEBUG:root:Debug Msg
    >>> logging.info('Info')
    INFO:root:Info
    >>> logging.warning('This is a warning')
    WARNING:root:This is a warning
    >>> logging.error('錯誤發生了')
    ERROR:root:錯誤發生了
    >>> logging.fatal('快來救人阿')
    CRITICAL:root:快來救人阿

  6. 注意:logging.basicConfig 必須在輸出訊息之前呼叫,而且只能呼叫一次。第二次就沒效了。
  7. logging.basicConfig 的詳細介紹,請呼叫 print(logging.basicConfig.__doc__),可看到它還有哪些參數可以使用。
  8. 如何將訊息輸出至檔案?
  9. # 輸出至 log.txt,如果已存在會接著記錄
    logging.basicConfig(filename='log.txt')
    # 輸出至 log.txt,同上
    logging.basicConfig(filename='log.txt', filemode='a')
    # 輸出至 log.txt,會開新檔案
    logging.basicConfig(filename='log.txt', filemode='w')
  10.  如何同時輸出至console和檔案?下例會同時輸出在console和log.txt中。當只有單一輸出時,可以用filename來指定目標檔案,或用stream來指定目標stream。如果要輸出至多個目標,就要將它們以list of handlers當參數傳入。
  11. >>> import sys
    >>> import logging
    >>> logging.basicConfig(handlers=[logging.StreamHandler(sys.stderr), logging.FileHandler('log.txt')], level=logging.CRITICAL)
    >>> logging.debug("Let's debug")    # 無輸出
    >>> logging.critical("Let's go crazy")
    CRITICAL:root:Let's go crazy
  12.  練習一下,如果要同時輸出到stdout和stderr,可以這麼作(當然我們應該不會真的這麼作):
  13. >>> import sys, logging
    >>> logging.basicConfig(handlers=[logging.StreamHandler(sys.stdout), logging.StreamHandler(sys.stderr)])
    >>> logging.warning('你確定要這麼作嗎?')
    WARNING:root:你確定要這麼作嗎?
    WARNING:root:你確定要這麼作嗎?
  14.  輸出的訊息格式是可以改的。例如我們如果要將訊息放在最前面,可以這麼作:
  15. >>> import logging
    >>> logging.basicConfig(format='%(message)s:%(levelname)s:%(name)s')
    >>> logging.warning('倒過來看會有些不習慣')
    倒過來看會有些不習慣:WARNING:root

  16. format當中,可以使用的參數有:
  17. %(name)s            logger名稱(之後介紹)
    %(levelno)s         10, 20, ... 50 =>DEBUG, INFO, WARNING, ERROR, CRITICAL
    %(levelname)s    DEBUG, INFO,  WARNING, ERROR, CRITICAL
    %(pathname)s    呼叫logging的執行原始碼的完整路徑
    %(filename)s       %(pathnames)s 中的檔名部份
    %(module)s          Module
    %(lineno)d           原始碼正執行到的行數
    %(funcName)s     Function name
    %(created)f         一筆log訊息的產生時間(time.time()  return value)
    %(asctime)s        一筆log訊息的產生時間(文字格式)
    %(msecs)d           一筆log訊息的產生時間的毫秒部份
    %(relativeCreated)d  從logging載入算起,至一筆log訊息產生所經毫秒數
    %(thread)d          Thread ID (if available)
    %(threadName)s      Thread name (if available)
    %(process)d         Process ID (if available)
    %(message)s       log訊息
  18. 時間的格式可以更改,方式是使用 datefmt 參數:
  19. >>> import logging
    >>> logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
    >>> logging.warning('Warning msg')
    2014/12/30 21:51:44 Warning msg
  20. 如果要印出milliseconds,不能使用datefmt='%Y/%m/%d %H:%M:%S.%f',而是要這麼作(參考 10 當中的 %(msecs)d ):
  21. >>> import logging
    >>> logging.basicConfig(format='%(asctime)s.%(msecs)d %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
    >>> logging.warning('印出milliseconds的方法如上')
    2014/12/30 21:58:30.869 印出milliseconds的方法如上
待續.....
(目前個人自己並沒有更進階的需求,所以暫時就到此為止)

2014年11月23日 星期日

python 3.4 tkinter widgets (整理中)

以下是我用 dir(module name)撈出來整理的widgets資料,人工過濾了一些底層不大會直接用到的 class,仍可能有一些還沒濾掉。目前至少可以看出有哪些可能可以使用的widgets。

在簡易說明中,只有提示簡單的用法。如果完全沒使用過tkinter,可能要先去找Hello world之類的範例來起步。如果要看widgets有什麼屬性,可以用 widget_obj.config() 來顯示。

使用tix的widgets時,要記得使用tkinter.tix.Tk(),不然會有錯誤訊息。

其它可參考之資訊:
http://effbot.org/tkinterbook/tkinter-index.htm (有點舊,但是有助於觀念)
http://www.tkdocs.com/  (有持續更新,如果能夠看英文,建議看它的Tutorial)

tkinterttktix簡易說明或範例(日後補充)
Balloon 例如滑鼠移至按鍵時,冒出說明文字。
ba = Balloon()
ba.bind_widget(btn, balloonmsg='Hello', statusmsg='Hi')
Button btn = Button(text='Hello')
ButtonBox 方便一次新增多個按鍵
btnbox=ButtonBox()
btnbox.add('ok', text='OK', command=lambda : root.destroy())
btnbox.add('close', text='Cancel', command=lambda : root.destroy())
Canvas 此處有不錯的範例
CheckList chklist=CheckList()
chklist.hlist.add('choice1', text='This is Choice1') chklist.hlist.add('choice2', text='This is Choice2') checklist.setstatus('choice1', 'on') # or 'off' checklist.getstatus('choice1') # 回傳 'on', 'off'
Checkbutton ckbtn_var = IntVar()
ckbtn=Checkbutton(text='btn1', variable=ckbtn_var)
ckbtn_var.get() # 之後取值,值為 0, 1
ComboBox str_var = StringVar()
cb = ComboBox(variable=str_var)
cb.insert(tix.END, '一月')
cb.insert(tix.END, '二月')
cb.insert(tix.END, '三月')
str_var.get() # 讀取值
Combobox str_var = StringVar()
cb = comboBox(textvariable=str_var)
cb['values'] = ('一月', '二月', '三月')
str_var.get() # 讀取值
Control 一個輸入數字的文字框,附加上下鍵按紐。
v1 = IntVar()
ctrl = Control(variable=v1)
v1.get() # 讀取值
DialogShell 會跳出一個視窗。 ds = DialogShell() ds.popup() # 跳出 ds.popdown() # 消失
DirList Segmentation fault....
DirSelectBox Segmentation fault....
DirSelectDialog Segmentation fault....
DirTree Segmentation fault....
Entry 單行的文字輸入框
str_var = StringVar()
entry = Entry(textvariable=str_var)
str_var.get() # 讀取值
entry.get() # 也可以
ExFileSelectBox Segmentation fault....
ExFileSelectDialog Segmentation fault....
FileEntry Segmentation fault....
FileSelectBox Segmentation fault....
FileSelectDialog Segmentation fault....
Frame f1 = Frame() # container功用
Grid Layout manager
gd = Grid()
for x in range(3):
  for y in range(3):
    Label(text='{0}-{1}'.format(x,y), master=gd).grid(row=x, column=y)
gd.pack()
HList hl = HList()
hl.add('Item1', text='Item1 Text') # 最上層項目
hl.add('Item1.SubItem1', text='SubItem1 Text') # 第二層
# CheckList當中也有HList,可作出階層效果
InputOnly Segmentation fault....
Label Label(text='要顯示的文字')
LabelEntry Label + Entry。
le = LabelEntry()
le.label['text'] = '請輸入帳號'
le.entry.get() # 之後用這行來讀取值
LabelFrame Label + Frame
ttklf = LabelFrame()
ttklf['text'] = '標籤'
#### (tix.LabelFrame測試時不大能正常顯示)
tixlf = LabelFrame()
tixlf.label['text'] = '標籤'
LabeledScale 用Scrollbar的方式讓人輸入數字。
l_s = LabeledScale() # 預設是0~10的整數
l_s.scale['from'] = -10 #改成 -10~10
l_s.scale['to'] = 100        # -10~100
l_s.label['text']                # 取得目前的數字
Labelframe 參考LabelFrame
ListNoteBook
Listbox
Menu
Menubutton
Message
Meter
Misc
NoteBook
NoteBookFrame
Notebook nb = ttk.Notebook()
#新增兩個tab
nb.add(ttk.Label(nb, 'Hello'), text='Tab 1')
nb.add(ttk.Label(nb, 'Hi'), text='Tab 2')
#目前所在的tab id
tab_id = nb.index(nb.select())
# 刪除 tab
nb.forget(tab_id)
OptionMenu
PanedWindow
Panedwindow
PopupMenu
Progressbar
Radiobutton
ResizeHandle
Scale
Scrollbar
ScrolledGrid
ScrolledHList
ScrolledListBox
ScrolledTList
ScrolledText
ScrolledWindow
Select
Separator
Shell
Sizegrip
Spinbox
StdButtonBox
Studbutton
TList
Text
Toplevel
Tree
Treeview
Tributton

  • 其它: (root = Tk())
    • 使用 Label 來顯示png檔:
      • 安裝 PIL (for python3)
        • sudo apt-get install python3-pil
        • sudo apt-get install python3-pil.imagetk
      • 程式碼:
        • from PIL import ImageTk, Image
          resized_image = Image.open(檔名) 
          img = ImageTk.PhotoImage(resized_image)
          label = Label(root, image=img
      • 注意事項:
        • 上方程式碼中的img,必須要是global variable或是依附在不會消失的物件上(例如 label.image = img),才不會在garbage collection時被清掉,導致無法顯示。
      • 同時顯示圖和文字:Label(text='....', image=img, compound=TOP/BOTTOM/LEFT/RIGHT/CENTER)
    • 修改 Label 的字型、大小、與加強效果
      • label = tk.Label(root, text='Hello')
      • label['font'] = (字型, 大小, 效果)
        • 字型:字串,例如 Times, arial
        • 大小:數字
        • 效果:normal, bold, italic, underline, overstrike,可一次使用多個
      • Example: label['font'] = ('Times', 20, 'bold italic')
    • 視窗最大化/全螢幕:
      • root.attributes('-zoomed', True)  # 最大化
      • root.attributes('-fullscreen', True) # 全螢幕
    •  

2014年5月18日 星期日

關於 \xbb 字元的處理

在使用python時,曾遇到 print \xbb 出錯的問題:
UnicodeEncodeError: 'cp950' codec can't encode character '\xbb' in position 0: illegal multibyte sequence
從來查了一下,得到以下資訊:
  1. \xbb 字元就是「»」這個字元。
  2. windows環境中的命令提示字元,是使用cp950字元集,會有以上錯誤。
  3. 在ubuntu環境下的terminal,是使用utf-8字元集,可以正確顯示。
所以怎麼辦呢?
  • 方法一:將'\xbb'替換掉 (字串的replace)。
  • 方法二:在命令提示字元輸入 chcp65001,會切換成utf-8字元集。它會有輸出,不會有Error,但是輸出結果看似亂碼而非「»」。

2014年5月9日 星期五

在python中交換兩個變數的值(swap)

在比較古老的程式語言中,
如果我們要交換 a, b 兩個變數的值,
標準的方法是:
tmp = a;
a = b;
b = tmp;
使用python不用這麼麻煩。
python有這樣的語法:
a, b = b, a

2014年4月11日 星期五

如何讓自己的python模組在python2和python3環境下皆可執行

考慮以下的情境:
  • 您有兩個projects,使用python語言,簡稱為PA與PB
  • PA用到別人的舊module(非用不可),用python2.7的語法,所以您使用python2.7 (M1)
  • PB用到別人的新module(非用不可),用python3的語法,所以您使用python3 (M2)
  • 兩個projects有共用的程式碼,您當然將它們抽出來,寫成您自己的module (M3)
因為python3和python2之間,有語法不同的差異,也有module名稱改變的差異,所以您的M3要寫成同時在PA和PB都能使用才行。當然拆成兩份也是可以,不過往後的管理維護上比較麻煩。

  • 化解print function和字串編碼原則的不一致
    • 在python3中,字串統一是使用unicode來儲存;在python2中則是有彈性,但是這個彈性會造成您的module要時時留意編碼問題。在使用python2的大型程式開發,其實就有公司採取「一律先轉成unicode再處理」的原則。
    • 處理方法:在程式碼開頭輸入以下,就可以使用python3的print與字串編碼原則(在使用python3時,該行不會有錯誤)
from __future__ import print_function, unicode_literals 

  • 一般性的處理
    • 在import sys之後,程式碼可以使用sys.version_info取得python的版本。如果只需要分辨python2.x與python3.x,大致上可以這麼作(臨場應變):
if sys.version_info.major == 2:
# codes for python2
elif sys.version_info.major == 3:
# codes for python3

以開utf-8檔為例,只需要 python2 的部份就好了(臨場應變,讓程式碼簡潔):
from __future__ import print_function, unicode_literals
import sys
if sys.version_info.major == 2:
    import codecs
    open = codecs.open

# ....
# 之後可以這麼用

with open(f_name, 'r', encoding='utf-8') as f_in:
    for line in f_in:
        print(line)

2013年10月21日 星期一

在Windows上同時使用Python2和Python3

Python 3.0有較大的改版,使得有些Python 2.x的程式無法執行。
所以我們的電腦需要同時保有Python2.x和Python3.x。

對於例如linux的使用者,問題可能不會很大。
在python程式的第一行,可以指定要用哪個版本的interpreter。
例如 #!/usr/bin/python2.7 來使用 2.7 版,而 #!/usr/bin/python3.3 來使用 3.3 版。
這部份的功能不是python的,而是linux的script language的第一行都會以此語法來指定interpreter。

Windows本身是沒有這個功能的,它是靠副檔名來作判斷。
那麼,我們如果灌了Python 2.x和Python 3.x,
然後直接執行python script,哪一個 interpreter 會被使用呢?

我一直以為,後安裝的會被使用。
不過前幾天在作一個測式時,發現在Windows上,也可以作到和linux一樣的效果了。

方法是:
  1. 先安裝 Python 2.x,再安裝 Python 3.x。順序一定要對。
  2. 如果某支script要用到 Python 2.x,在第一行使用  #!/usr/bin/python2 #!/usr/bin/python #! python (#!開頭,然後後面要有 python 或 python2 即可)
  3.  如果某支script要用到 Python 3.x,在第一行使用  #!/usr/bin/python3 或  #! python3(#!開頭,然後後面要有 python3 即可)
 這樣就可以方便的在 Windows 上,指名要使用Python 2或是Python 3了。如果希望一份程式在linux上也能執行,可以建一個 python3 的link,指到想用的3.x版interpreter。

簡易的測試程式如下:
#!python
#!python3
# choose one of the above


import sys
print(sys.version_info)