Unix-Like 系統(tǒng)使用自基準點以來消逝的秒數(shù)來表達絕對時間。
用 time() 返回自 epoch 以來的秒數(shù),gmtime()、localtime() 將其轉換為 struct_time 結構體。
>>> from time import *
>>> t = time()
>>> t
1357761634.903692
>>> gmtime(t) # epoch -> UTC
time.struct_time(tm_year=2013, tm_mon=1, tm_mday=9, tm_hour=20, tm_min=0, tm_sec=34,
tm_wday=2, tm_yday=9, tm_isdst=0)
>>> localtime(t) # epoch -> Local (UTC+8)
time.struct_time(tm_year=2013, tm_mon=1, tm_mday=10, tm_hour=4, tm_min=0, tm_sec=34,
tm_wday=3, tm_yday=10, tm_isdst=0)
將 struct_time 轉回 epoch。
>>> from calendar import timegm
>>> t = time()
>>> t
1357762219.162796
>>> utc = gmtime(t) # epoch -> UTC
>>> timegm(utc) # UTC -> epoch
1357762219
>>> local = localtime(t) # epoch -> local
>>> mktime(local) # local -> epoch
1357762219
與 datetime 的轉換,注意返回的是 localtime 時間。
>>> from datetime import datetime
>>> from time import time
>>> t = time()
>>> d = datetime.fromtimestamp(t) # localtime 時間
>>> d
datetime.datetime(2013, 1, 10, 4, 20, 27, 301148)
>>> d.timetuple()
time.struct_time(tm_year=2013, tm_mon=1, tm_mday=10, tm_hour=4, tm_min=20, tm_sec=27,
tm_wday=3, tm_yday=10, tm_isdst=-1)
相關函數(shù):
ctime: 將 epoch 轉換為字符串。 asctime: 將 struct_time 轉換為字符串。
>>> t = time()
>>> ctime(t)
'Thu Jan 10 04:26:01 2013'
>>> asctime(localtime(t))
'Thu Jan 10 04:26:01 2013'
clock: 返回當前進程消耗的CPU時間 (秒)。 sleep: 暫停進程 (秒,可以是小數(shù),以便設置毫秒、微秒級暫停)。
>>> clock()
0.56022400000000006
>>> sleep(0.1)
strftime: 將 struct_time 格式化為字符串。 strptime: 將字符串格式化為 struct_time。
>>> t = time()
>>> s = strftime("%Y-%m-%d %H:%M:%S", localtime(t))
>>> s
'2013-01-10 04:27:39'
>>> strptime(s, "%Y-%m-%d %H:%M:%S")
time.struct_time(tm_year=2013, tm_mon=1, tm_mday=10, tm_hour=4, tm_min=27, tm_sec=39,
tm_wday=3, tm_yday=10, tm_isdst=-1)
timezone: 與 UTC 的時差。 tzname: 當前時區(qū)名稱。
>>> timezone / 3600
-8
>>> tzname # 北京時間,China Standard Time
('CST', 'CST')
盡管因為 GIL 的緣故,Python 多線程一直遭受種種非議。但作為多個并發(fā)執(zhí)行流程,多線程是無法完全用 "手工" 切換的協(xié)程來替代的。
創(chuàng)建 Thread 實例,傳入待執(zhí)行函數(shù)。
>>> from threading import Thread, currentThread, activeCount
>>> def test(s):
... print "ident:", currentThread().ident
... print "count:", activeCount()
... print s
...
>>> Thread(target = test, args = ("Hello",)).start()
ident: 4353970176
count: 3
Hello
除了標識符,還可以線程取個名字,這有助于調試。
還可以繼承 Thread 實現(xiàn)自己的線程類。
>>> class MyThread(Thread):
... def __init__(self, name, *args):
... super(MyThread, self).__init__(name = name)
... self.data = args
...
... def run(self):
... print self.name, self.data
>>> MyThread("abc", range(10)).start()
abc ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],)
將線程 daemon 屬性設為 True,那么表示這是一個背景線程,進程退出時不會等待該線程結束。
調用 join() 等待線程結束,可提供超時參數(shù) (秒,浮點數(shù)設定更小粒度)。isAlive() 檢查線程狀態(tài),join() 可多次調用。
>>> from time import sleep
>>> def test():
... print "__thread_start__"
... sleep(10)
... print "__thread_exit__"
>>> def run():
... t = Thread(target = test)
... t.start()
... t.join(2) // 超時
...
... print t.isAlive() // 檢查狀態(tài)
... t.join() // 再次等待
...
... print "over"
>>> run()
__thread_start__
True
__thread_exit__
over!
Lock 不支持遞歸加鎖,也就是說即便在同一線程中,也必須等待鎖釋放。通常建議改用 RLock,它會處理 "owning thread" 和 "recursion level" 狀態(tài),對于同一線程的多次請求鎖行為,只累加計數(shù)器。每次調用 release() 將遞減該計數(shù)器,直到 0 時釋放鎖,因此 acquire() 和 release() 必須要成對出現(xiàn)。
threading 中的成員大多實現(xiàn)了上下文協(xié)議,盡可能用 with 代替手工調用。
>>> lock = RLock()
>>> def show(i):
... with lock: // 遞歸請求鎖
... print currentThread().name, i
... sleep(0.1)
>>> def test():
... with lock: // 加鎖
... for i in range(5):
... show(i)
>>> for i in range(2):
... Thread(target = test).start()
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 3
Thread-1 4
Thread-2 0
Thread-2 1
Thread-2 2
Thread-2 3
Thread-2 4
Event 通過通過一個內部標記來協(xié)調多線程運行。方法 wait() 阻塞線程執(zhí)行,直到標記為 True。set() 將標記設為 True,clear() 更改標記為 False。isSet() 用于判斷標記狀態(tài)。
>>> def test():
... e = Event()
... def test():
... for i in range(5):
... e.wait()
... e.clear()
... print i
...
... Thread(target = test).start()
... return e
>>> e = test()
>>> e.set()
0
>>> e.set()
1
如果不調用 clear(),那么標記一直為 True,wait() 就不會發(fā)生阻塞行為。
在實際編程中,我們通常為每個線程準備一個獨立的 Event,而不是多個線程共享,以避免未及時調用 clear() 時發(fā)生意外情況。
Condition 像 Lock 和 Event 的綜合體,除基本的鎖操作外,還提供了類似 yield 的功能。在獲取鎖以后,可以調用 wait() 臨時讓出鎖,當前線程被阻塞,直到 notify() 發(fā)送通知后再次請求鎖來恢復執(zhí)行。將 wait 當做 yield,那么 notify 就是 send。
可以將已有的鎖對象傳給 Condition。
>>> def t1():
... with cond:
... for i in range(5):
... print currentThread().name, i
... sleep(0.1)
... if i == 3: cond.wait()
>>> def t2():
... with cond:
... for i in range(5):
... print currentThread().name, i
... sleep(0.1)
... cond.notify()
>>> Thread(target = t1).start(); Thread(target = t2).start()
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 3 // 讓出鎖
Thread-2 0
Thread-2 1
Thread-2 2
Thread-2 3
Thread-2 4
Thread-1 4 // 重新獲取鎖,繼續(xù)執(zhí)行。
只有獲取鎖的線程才能調用 wait() 和 notify(),因此必須在鎖釋放前調用。
當 wait() 釋放鎖后,其他線程也可進入 wait 狀態(tài)。notifyAll() 激活所有等待線程,讓它們去搶鎖然后完成后續(xù)執(zhí)行。
>>> def test():
... with cond:
... for i in range(5):
... print currentThread().name, i
... sleep(0.1)
... if i == 2: cond.wait()
>>> Thread(target = t1).start(); Thread(target = t1).start()
Thread-1 0
Thread-1 1
Thread-1 2 // Thread-1: 等待
Thread-2 0
Thread-2 1
Thread-2 2 // Thread-2: 等待
>>> with cond: cond.notifyAll() // 通知所有 cond.wait 線程。
Thread-2 3 // Thread-1 和 Thread-2 再次搶鎖以完成后續(xù)執(zhí)行,
Thread-2 4 // 至于誰先搶到,就不好說了。
Thread-1 3
Thread-1 4
Semaphore 通過一個計數(shù)器來限制可同時運行的線程數(shù)量。計數(shù)器表示還可以運行的線程數(shù)量,acquire() 遞減計數(shù)器,release() 則是增加計數(shù)器。
>>> sem = Semaphore(2)
>>> def test():
... with sem:
... for i in range(5):
... print currentThread().name, i
... sleep(0.1)
>>> for i in range(3):
... Thread(target = test).start()
Thread-1 0 // 1 和 2 同時執(zhí)行。因為計數(shù)器為 0,所以 3 被阻塞。
Thread-2 0
Thread-1 1
Thread-2 1
Thread-1 2
Thread-2 2
Thread-1 3
Thread-2 3
Thread-1 4
Thread-2 4
Thread-3 0 // 1 和 2 釋放信號量,3 開始執(zhí)行。
Thread-3 1
Thread-3 2
Thread-3 3
Thread-3 4
用一個獨立線程在 n 秒后執(zhí)行某個函數(shù)。如定時器尚未執(zhí)行,可用 cancel() 取消,定時器僅執(zhí)行一次。
>>> def test():
... print datetime.datetime.now()
>>> Timer(2, test).start()
2013-03-26 11:06:19.840455
TLS (thread-local storage) 為線程提供獨立的存儲空間。
>>> data = local()
>>> def test(fn, x):
... data.x = x
... for i in range(5):
... data.x = fn(data.x)
... print currentThread().name, data.x
... sleep(0.1)
>>> t1 = (lambda x: x + 1, 0)
>>> t2 = (lambda x: x + "a", "a")
>>> for d in (t1, t2):
... Thread(target = test, args = d).start()
Thread-1 1
Thread-2 aa
Thread-2 aaa
Thread-1 2
Thread-2 aaaa
Thread-1 3
Thread-2 aaaaa
Thread-1 4
Thread-1 5
Thread-2 aaaaaa
看上去和 threading 類似,區(qū)別在于用進程代替線程。這是規(guī)避 GIL,實現(xiàn)多核并發(fā)的常用方法。
創(chuàng)建子進程執(zhí)行指定函數(shù)。
from multiprocessing import Process, current_process
def test(*args, **kwargs):
p = current_process()
print p.name, p.pid
print args
print kwargs
if __name__ == "__main__":
p = Process(target=test, args=(1, 2), kwargs = {"a": "hello"}, name = "TEST")
p.start()
p.join()
輸出:
TEST, 2570
(1, 2)
{'a': 'hello'}
方法 start() 創(chuàng)建子進程,然后在新進程中通過 run() 執(zhí)行目標函數(shù)。構建參數(shù) args、kwargs 會傳遞給目標函數(shù)。在父進程中用 join() 等待并獲取子進程退出狀態(tài),否則會留下僵尸進程,除非父進程先終止。
從下例輸出結果,可以看到 init() 在父進程執(zhí)行,但 run() 已經是子進程了。
class MyProcess(Process):
def __init__(self):
print "init:", os.getpid()
super(MyProcess, self).__init__()
def run(self):
print "run:", os.getpid()
if __name__ == "__main__":
print "parent:", os.getpid()
p = MyProcess()
p.start()
p.join()
輸出:
parent: 12093
init: 12093
run: 12094
子進程不會調用退出函數(shù),而且只有后臺 (daemon) 進程才可捕獲主進程退出信號,默認處理自然是終止子進程。另外,后臺進程不能創(chuàng)建新的子進程,這將導致僵尸出現(xiàn)。
from os import getpid
from time import sleep
from signal import signal, SIGTERM
from multiprocessing import Process
def test():
def handler(signum, frame):
print "child exit.", getpid()
exit(0)
signal(SIGTERM, handler)
print "child start:", getpid()
while True: sleep(1)
if __name__ == "__main__":
p = Process(target = test)
p.daemon = True # 必須在 start() 前設置。
p.start()
sleep(2) # 給點時間讓子進程進入 "狀態(tài)"。
print "parent exit."
輸出:
child start: 12185
parent exit.
child exit. 12185
調用 terminate() 會立即強制終止子進程 (不會執(zhí)行任何清理操作)。有關狀態(tài)還有: is_alive()、pid、exitcode。
進程池。用多個可重復使用的后臺 (daemon) 進程執(zhí)行函數(shù),默認數(shù)量和 CPU 核相等。
from multiprocessing import Pool
def test(*args, **kwargs):
print args
print kwargs
return 123
if __name__ == "__main__":
pool = Pool()
print pool.apply(test, range(3), dict(a=1, b=2))
pool.close()
pool.join()
輸出:
(0, 1, 2)
{'a': 1, 'b': 2}
123
調用 join() 等待所有工作進程結束前,必須確保用 close() 或 terminate() 關閉進程池。close() 阻止提交新任務,通知工作進程在完成全部任務后結束。該方法立即返回,不會阻塞等待。
使用異步模型時,callback 是可選的。
from multiprocessing import Pool
from time import sleep
def test(*args, **kwargs):
sleep(2)
return 123
def callback(ret):
sleep(2)
print "return:", ret
if __name__ == "__main__":
pool = Pool()
pool.apply_async(test, callback=callback)
ar = pool.apply_async(test)
print ar.get()
pool.close()
pool.join()
apply_async 返回 AsyncResult 實例,其 get([timeout])、wait()、successful() 等方法可獲知任務執(zhí)行狀態(tài)和結果。
map() 和 imap() 用于批量執(zhí)行,分別返回列表和迭代器結果。
from multiprocessing import Pool, current_process
def test(x):
print current_process().pid, x
return x + 100
def test2(s):
print current_process().pid, s
if __name__ == "__main__":
pool = Pool(3)
print pool.map(test, xrange(5))
pool.map(test2, "abc")
輸出:
1566 0
1567 1
1566 3
1568 2
1567 4
[100, 101, 102, 103, 104]
1566 a
1568 b
1567 c
參數(shù) chunksize 指定數(shù)據(jù)分塊大小,如果待處理數(shù)據(jù)量很大,建議調高該參數(shù)。
if __name__ == "__main__":
pool = Pool(3)
print pool.map(test, xrange(10), chunksize=2)
輸出:
1585 0 # 實際輸出順序可能不同。
1585 1
1586 2
1586 3
1587 4
1587 5
1585 6
1585 7
1586 8
1586 9
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
Queue 是最常用的數(shù)據(jù)交換方法。參數(shù) maxsize 限制隊列中的數(shù)據(jù)項數(shù)量,這會影響 get/put 等阻塞操作。默認值無限制。
通常直接使用 JoinableQueue,其內部使用 Semaphore 進行協(xié)調。在執(zhí)行 put()、task_done()時調整信號量計數(shù)器。當 task_done() 發(fā)現(xiàn)計數(shù)值等于 0,立即通知 join() 解除阻塞。
from Queue import Empty
from multiprocessing import Process, current_process, JoinableQueue
def test(q):
pid = current_process().pid
while True:
try:
d = q.get(timeout=2) # 阻塞 + 超時。照顧生產者未及生產情形。
print pid, d
q.task_done()
except Empty:
print pid, "empty"
break
if __name__ == "__main__":
q = JoinableQueue(maxsize=1000)
map(q.put, range(5)) # 未超出隊列容量限制,不會阻塞。
print "put over"
for i in range(3): # 創(chuàng)建多個 consumer。
Process(target=test, args=(q,)).start()
q.join() # 等待任務完成。
print "task done"
輸出:
put over
2127 0
2127 1
2127 2
2127 3
2127 4
task done
2127 empty
2128 empty
2129 empty
或許你會考慮壓入同等數(shù)量的 None 作為結束標志,但無法保證每個 Consumer 都能獲取。
命令行參數(shù)解析模塊。原 optparse 已經停止開發(fā),建議替換為 argparse。
ArgumentParser 默認解析來源 sys.argv,也可提供顯式參數(shù)進行解析。
構造參數(shù)中,通常只需關心 description 和 epilog。前者顯示程序標題,后者在幫助信息的尾部顯示詳細的版權、使用描述等。
>>> from argparse import ArgumentParser
>>> parser = ArgumentParser(description="Test Program", epilog="author:qyuhen")
>>> parser.add_argument("-x", help="xxx...")
>>> parser.print_help()
usage: ipython [-h] [-x X]
Test Program
optional arguments:
-h, --help show this help message and exit
-x X xxx...
author:qyuhen
方法 parse_args 顯式列表參數(shù)可用 string.split 或 shlex.split 分解。
>>> args = parser.parse_args("-x 123".split())
>>> args
Namespace(x='123')
參數(shù)分為可選參數(shù) (optional) 和 位置參數(shù) (positional) 兩種,前者用指定前綴 (默認是 "-") 標識。
>>> parser = ArgumentParser()
>>> parser.add_argument("-name", help="name...")
>>> parser.add_argument("x", help="x...")
>>> parser.print_help()
usage: ipython [-h] [-name NAME] x
positional arguments:
x x...
optional arguments:
-h, --help show this help message and exit
-name NAME name...
>>> parser.parse_args("-name q.yuhen 123".split())
Namespace(name='q.yuhen', x='123')
可選參數(shù)名可以有多個,鍵值間可以有 "=",而且單字符名稱的參數(shù)鍵值可以合并。
>>> parser = ArgumentParser()
>>> parser.add_argument('-x', "-XX")
>>> parser.print_help()
usage: ipython [-h] [-x X]
optional arguments:
-h, --help show this help message and exit
-x X, -XX X
>>> parser.parse_args("-x 100".split()) # 普通方式
Namespace(x='100')
>>> parser.parse_args("-x=100".split()) # 使用等號
Namespace(x='100')
>>> parser.parse_args("-x100".split()) # 合并鍵值
Namespace(x='100')
>>> parser.parse_args("-XX 100".split()) # 其他名稱
Namespace(x='100')
>>> parser.parse_args("-XX100".split()) # 僅單字符名可以合并
error: unrecognized arguments: -XX100
如果參數(shù)值是包含空格的字符串,注意用引號或轉義處理。
>>> parser = ArgumentParser()
>>> parser.add_argument("-s")
>>> parser.parse_args(shlex.split("-s='a b c'")) # 不能用 string.split()
Namespace(s='a b c')
>>> parser.parse_args(shlex.split("-s=a\ b\ c"))
Namespace(s='a b c')
可選參數(shù)默認返回 None,可用 default 參數(shù)或 parser.set_defaults 方法指定默認值。如果參數(shù)是必須的,只需設定 required=True 即可。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", default=123)
>>> parser.add_argument("-y", required=True)
>>> parser.parse_args()
error: argument -y is required
>>> parser.parse_args("-y abc".split())
Namespace(x=123, y='abc')
除非用 dest 指定值存儲名稱,否則和參數(shù)名相同。metavar 用于修改參數(shù)值顯示標記,默認使用 dest 大寫名稱。metavar 不會影響 dest 設置。
>>> parser = ArgumentParser()
>>> parser.add_argument("-n", dest="name")
>>> parser.add_argument("-x", dest="x", metavar="value")
>>> parser.print_help()
usage: ipython [-h] [-n NAME] [-x value]
optional arguments:
-h, --help show this help message and exit
-n NAME
-x value
>>> parser.parse_args("-n q.yuhen -x 123".split())
Namespace(name='q.yuhen', x='123')
type 參數(shù)用于指定值轉換函數(shù),比如內置函數(shù) int、float、file,也可以自定義函數(shù)。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", type=int)
>>> parser.add_argument("-s", type=lambda s: "s:"+s)
>>> parser.parse_args("-x 123 -s abc".split())
Namespace(s='s:abc', x=123)
nargs 指示參數(shù)值數(shù)量,默認為 1。除具體的數(shù)字外,還可以使用通配符。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", nargs="+")
>>> parser.add_argument("-y", nargs="")
>>> parser.add_argument("n", nargs=2)
>>> parser.add_argument("args", nargs=REMAINDER)
>>> parser.print_help()
usage: ipython [-h] [-x X [X ...]] [-y [Y]] n n ...
positional arguments:
n
args
optional arguments:
-h, --help show this help message and exit
-x X [X ...]
-y [Y]
>>> parser.parse_args("-x x1 x2 -y y1 1 2 a b c -xxx".split())
Namespace(args=['a', 'b', 'c', '-xxx'], n=['1', '2'], x=['x1', 'x2'], y='y1')
action 用于指定參數(shù)取值行為。
# 1. store_const: 提供參數(shù)時返回 const 值,否則返回 default。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", action="store_const", const=100)
>>> parser.add_argument("-y", action="store_const", const=100, default=1)
>>> parser.parse_args("-x".split())
Namespace(x=100, y=1)
>>> parser.parse_args("-x -y".split())
Namespace(x=100, y=100)
>>> parser.parse_args()
Namespace(x=None, y=1)
# 2. store_true/store_false: 顯式返回指定布爾值,否則返回相反值。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", action="store_true")
>>> parser.add_argument("-y", action="store_false")
>>> parser.parse_args("-x -y".split())
Namespace(x=True, y=False)
>>> parser.parse_args()
Namespace(x=False, y=True)
# 3. append: 將多個同名參數(shù)值合并成列表。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", action="append")
>>> parser.parse_args("-x 1 -x 2".split())
Namespace(x=['1', '2'])
# 4. append_const: 合并多個不同名參數(shù) const 值,注意所有合并參數(shù)的 dest 相同。
>>> parser = ArgumentParser()
>>> parser.add_argument("-x", dest="numbers", action="append_const", const=1)
>>> parser.add_argument("-y", dest="numbers", action="append_const", const=2)
>>> parser.parse_args("-x -y".split())
Namespace(numbers=[1, 2])
# 5. count: 通常用于統(tǒng)計 -vvv 這類 level 參數(shù),只能是單字符名。
>>> parser = ArgumentParser()
>>> parser.add_argument("--verbose", "-v", action="count")
>>> parser.parse_args("-vv".split())
Namespace(v=2)
# 6. version: 顯示版本信息。
>>> parser = ArgumentParser()
>>> parser.add_argument('--version', "-V", action='version', version='%(prog)s 2.0')
>>> parser.parse_args("--version".split())
ipython 2.0
choices 用于指定參數(shù)取值范圍。
>>> parser = ArgumentParser()
>>> parser.add_argument('-x', choices=range(1, 5), type=int)
>>> parser.add_argument('-s', choices=("a", "b"))
>>> parser.print_help()
usage: ipython [-h] [-x {1,2,3,4}] [-s {a,b}]
optional arguments:
-h, --help show this help message and exit
-x {1,2,3,4}
-s {a,b}
>>> parser.parse_args("-x 2 -s a".split())
Namespace(s='a', x=2)
>>> parser.parse_args("-x 6".split())
error: argument -x: invalid choice: 6 (choose from 1, 2, 3, 4)
>>> parser.parse_args("-s abc".split())
error: argument -s: invalid choice: 'abc' (choose from 'a', 'b')
如果參數(shù)較多,分組顯示更便于查看。
>>> parser = ArgumentParser()
>>> group1 = parser.add_argument_group("group1", "group1 description...")
>>> group1.add_argument("x", help="xxx...")
>>> group2 = parser.add_argument_group("group2", "group2 description...")
>>> group2.add_argument("y", help="yyy...")
>>> group2.add_argument("z", help="zzz...")
>>> parser.print_help()
usage: ipython [-h] x y z
optional arguments:
-h, --help show this help message and exit
group1:
group1 description...
x xxx...
group2:
group2 description...
y yyy...
z zzz...
組的另外一個作用就是互斥,僅允許組中的一個參數(shù)出現(xiàn)??蓪M設置 required=True。
>>> parser = ArgumentParser()
>>> group = parser.add_mutually_exclusive_group(required=True)
>>> group.add_argument("-x")
>>> group.add_argument("-y")
>>> parser.print_help()
usage: ipython [-h] (-x X | -y Y)
optional arguments:
-h, --help show this help message and exit
-x X
-y Y
>>> parser.parse_args("-x 100 -y 200".split())
error: argument -y: not allowed with argument -x
>>> parser.parse_args("-x 100".split())
Namespace(x='100', y=None)
>>> parser.parse_args("-y 200".split())
Namespace(x=None, y='200')
標準庫 ctypes 模塊可以非常方便地調用動態(tài)庫 (.so),這有助于解決安全和性能問題。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int add(int x, int y)
{
return x + y;
}
void inc(int* x)
{
*x += 1;
}
void cprint(char* s)
{
printf("%s: %s\n", __func__, s);
}
編譯:
$ gcc -fPIC -shared -o test.so test.c
測試:
>>> from ctypes import *
>>> so = cdll.LoadLibrary("./test.so")
>>> so.add(10, 20)
30
>>> so.cprint("Hello, World")
cprint: Hello, World
22
>>> x = c_int(123)
>>> so.inc(byref(x)) # 傳入指針
124
>>> x
c_int(124)
當然也可以直接調用系統(tǒng)庫的函數(shù)。
>>> libc = cdll.LoadLibrary("libc.dylib") # Linux: libc.so.6
>>> libc.printf("Hi\n")
Hi
4
>>> import time
>>> time.time(), libc.time()
(1364284691.803043, 1364284691)