注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

Cython 第3章 - Cython in Depth  

2016-06-09 06:00:14|  分类: lang_python |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
Cython 第0章 - distutils Quick Guide - kasicass - Code@Pig Home
Python byte-code 是跑在 Python VM 上的,总是慢了点。
Cython 将 Python 代码变成 .c 直接编译成 native code。

为了让 Cython 生成的代码更快。我们要从 dynamically typed 变成 statically typed。

cdef - statically typed
cdef 静态声明的变量,准守 C 的规则。
def integrate(a, b, f):
  cdef int i
  cdef int N=2000
  cdef float dx, s=0.0
  dx = (b-a)/N
  for i in range(N):
    s += f(a+i*dx)
  return s*dx
Cython 第3章 - Cython in Depth - kasicass - Code@Pig Home

指针怎么玩?
def hello_pointer():
    cdef double v;
    cdef double *p = &v;
    p[0] = 1.2   # *p = 1.2
    print p[0]
只要记住 *p 变成了 p[0] 即可。

struct 的使用:
cdef st_t *p_st = make_struct()
cdef int a_doubled = p_st.a + p_st.a

Python 与 C/C++ 中类型的映射
Cython 第3章 - Cython in Depth - kasicass - Code@Pig Home

Statically Python Type
我们还可以静态声明一个 Python Type。oh~ what's the fuck~
def hello_pytype():
    cdef list mylist = [1,2,3]
    print mylist
    del mylist[0]
    print mylist

    cdef dict mydict = {1:"hello", 2:"baby"}
    print mydict.values()

目前 Cython 支持的 静态Python Type:
* type, object
* bool
* complex
* basestring, str, unicode, bytes, bytearray
* list, tuple, dict, set, frozenset
* array
* slice
* date, time, datetime, timedelta, tzinfo

Compiler Directive
C 和 python 的一些行为是不一样的。比如:除法。
-1 % 5 在 Python 中等于 4,在 C 中等于 -1

# cython: cdivision=True

def remainder(int a, int b):
  return a % b

等价于

cimport cpython

@cython.cdivision(True)
def remainder(int a, int b):
  return a % b

等价于

cimport cython

def remainder(int a, int b):
  with cython.cdivision(True):
    return a % b

上面这种改变 compiler 行为的方法,称为 compiler directive。

Object Reference Count
Cython 会搞定大部分情况下的 object refcount。

b1 + b2 会生成要给临时变量。这种情况下,cython 会报编译错误。
b1 = b"hello1"
b2 = b" bytes"
cdef char *p = b1 + b2

但你只要这样写,cython 就能正确处理对象的 refcount。
b1 = b"hello1"
b2 = b" bytes"
cdef bytes cb = b1 + b2
cdef char *p = cb
print p

关于函数
下面的函数,放到 Cython 中 build_ext,要比 python interpreter 快一些。
def py_fact(n):
  if n <= 1:
    return 1
  return n * py_fact(n - 1)

参数改成静态声明,并不会比 py_fact() 快。
因为 return value 是 PyIntObject,递归调用很耗。
def typed_fact(long n):
  if n <= 1:
    return 1
  return n * typed_fact(n - 1)

我们引入 cdef。这下 c_fact() 就是个 pure C 的函数了。
cdef long c_fact(long n):
  if n <= 1:
    return 1
  return n * c_fact(n - 1)

不过 cdef 的函数,不允许在 extension module 之外调用。要这样包装一下:
def wrap_c_fact(n):
  return c_fact(n)

当然,性能提高了,也是有代价的。因为 C 中 long 是有上限的:
>>> function.py_fact(21)
51090942171709440000L
>>> function.wrap_c_fact(21)
-1195114496

每次都写一个 wrap_xxx() 是不是很麻烦。可以用 cpdef,自动构建 C func 和对应的 wrapper。
cpdef long cp_fact(long n):
  if n <= 1:
    return 1
  return n * cp_fact(n - 1)

Functions and Exception Handling
Cython 会默认捕获函数中的异常,不让其被外部看到。
cpdef int divide_ints(int i, int j):
  return i / j

>>> divide_ints(1, 0)
Exception ZeroDivisionError: 'integer division or modulo by zero' in 'exception.divide_ints' ignored

如果加入 except?,则可以将异常抛出。
cpdef int divide_ints_exception(int i, int j) except? -1:
  return i / j

>>> divide_ints_exception(1, 0)
Traceback (most recent call last):
  ...
  File "exception.pyx", line 5, in exception.divide_ints_exception (exception.c:804)
    return i / j
ZeroDivisionError: integer division or modulo by zero

except? -1 中的 -1 是啥意思呢?Cython 用 -1 作为 c function 碰到异常的标记值。这个值可以随意定义的,只要
保证这个值不在正常返回值的范围之内,即可。

类型强制转换
cdef int *ptr_i = <int*>v
等价于C中的:
int *ptr_i = (int*)v;

def print_address(a):
  cdef void *v = <void*>a
  cdef long addr = <long>v
  print "Cython address:", addr
  print "Python id     :", id(a)

将 a 直接作为 list 去操作,如果 a 不是一个 list,append() 的时候可能有 SystemError
def cast_to_list(a):
  cdef list cast_list = <list>a
  print type(a)
  print type(cast_list)
  cast_list.append(1)

在做 cast 的时候,检查 a 是否是一个 list。不是,则抛 TypeError
def safe_cast_to_list(a):
  cdef list cast_list = <list?>a
  print type(a)
  print type(cast_list)
  cast_list.append(1)

Declaring and Using structs, unions, and enums
在 C 中可以定义 struct 和 union。
struct mycpx {
  int a;
  float b;
};

union uu {
  int a;
  short b, c;
}

而 Cython 中可以这样:
cdef struct mycpx:
  float real
  float imag

cdef union uu:
  int a
  short b, c

也可以用 ctypedef (生成的代码对应于 typedef 那种风格的):
ctypedef struct mycpx:
  float real
  float imag

ctypedef union uu:
  int a
  short b, c

如何使用:
cdef mycpx a = cycpx(3.1415, -1.0)
cdef mycpx b = mycpx(real=2.718, imag=1.618034)

cdef mycpx zz
zz.real = 3.1415
zz.imag = -1.0

cdef mycpx zz = {'real': 3.1415, 'imag': -1.0}  # 这种比较耗

还可以 struct 嵌套 struct 哦:
struct nested {
  int outer_a;
  struct _inner {
    int inner_a;
  } inner;
};
在 Cython 中写成:
cdef struct _inner:
  int inner_a

cdef struct nested:
  int outer_a
  _inner inner

cdef nested n = {'outer_a': 1, 'inner': {'inner_a': 2}}

定义枚举类型:
cdef enum PRIMARIES:
  RED = 1
  YELLOW = 3
  BLUE = 5

cdef enum SECONARIES:
  ORANGE, GREEN, PURPLE

匿名枚举:
cdef enum:
  GLOBAL_SEED = 37

ctypedef 的使用
ctypedef double real
ctypedef long integral

def displacement(real d0, real v0, real a, real t):
  cdef real d = d0 + (v0 * t) + (0.5 * a * t**2)
  return d

Cython中的macro
DEF E  = 2.718281828459045
DEF PI = 3.141592653589793

def feynmans_jewel():
  return E ** (1j * PI) + 1.0

UNAME_SYSNAME, Operating system name
UNAME_RELEASE, Operating system release
UNAME_VERSION, Operating system version
UNAME_MACHINE, Machine hardware name
UNAME_NODENAME, Name on network

IF UNAME_SYSNAME == "Windows":
  ...
ELIF UNAME_SYSNAME == "Darwin":
  ...
ELSE:
  ...
(Windows 下测试,没有 UNAME_SYSNAME 这些 built-in macro??)

关于 Python 2 和 Python 3
cython -3 foo.pyx      # 按 Python 3 语法
cython -2 foo.pyx      # 按 Python 2 语法 (默认)
 

 

  评论这张
 
阅读(345)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017