A. python如何表示一個無符號的int就像C里的unsigned int a=0
Python不需要事先定義變數類型,例如建立int只需要a=0,而float則表示a=0.0。
B. 如何實現 C/C++ 與 Python 的通信
屬於混合編程的問題。較全面的介紹一下,不僅限於題主提出的問題。
以下討論中,Python指它的標准實現,即CPython(雖然不是很嚴格)
本文分4個部分
C/C++ 調用 Python (基礎篇)— 僅討論Python官方提供的實現方式
Python 調用 C/C++ (基礎篇)— 僅討論Python官方提供的實現方式
C/C++ 調用 Python (高級篇)— 使用 Cython
Python 調用 C/C++ (高級篇)— 使用 SWIG
練習本文中的例子,需要搭建Python擴展開發環境。具體細節見搭建Python擴展開發環境 - 蛇之魅惑 - 知乎專欄
1 C/C++ 調用 Python(基礎篇)
Python 本身就是一個C庫。你所看到的可執行體python只不過是個stub。真正的python實體在動態鏈接庫里實現,在Windows平台上,這個文件位於 %SystemRoot%\System32\python27.dll。
你也可以在自己的程序中調用Python,看起來非常容易:
//my_python.c
#include <Python.h>
int main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]);
Py_Initialize();
PyRun_SimpleString("print 'Hello Python!'\n");
Py_Finalize();
return 0;
}
在Windows平台下,打開Visual Studio命令提示符,編譯命令為
cl my_python.c -IC:\Python27\include C:\Python27\libs\python27.lib
在Linux下編譯命令為
gcc my_python.c -o my_python -I/usr/include/python2.7/ -lpython2.7
在Mac OS X 下的編譯命令同上
產生可執行文件後,直接運行,結果為輸出
Hello Python!
Python庫函數PyRun_SimpleString可以執行字元串形式的Python代碼。
雖然非常簡單,但這段代碼除了能用C語言動態生成一些Python代碼之外,並沒有什麼用處。我們需要的是C語言的數據結構能夠和Python交互。
下面舉個例子,比如說,有一天我們用Python寫了一個功能特別強大的函數:
def great_function(a):
return a + 1
接下來要把它包裝成C語言的函數。我們期待的C語言的對應函數應該是這樣的:
int great_function_from_python(int a) {
int res;
// some magic
return res;
}
首先,復用Python模塊得做『import』,這里也不例外。所以我們把great_function放到一個mole里,比如說,這個mole名字叫 great_mole.py
接下來就要用C來調用Python了,完整的代碼如下:
#include <Python.h>
int great_function_from_python(int a) {
int res;
PyObject *pMole,*pFunc;
PyObject *pArgs, *pValue;
/* import */
pMole = PyImport_Import(PyString_FromString("great_mole"));
/* great_mole.great_function */
pFunc = PyObject_GetAttrString(pMole, "great_function");
/* build args */
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs,0, PyInt_FromLong(a));
/* call */
pValue = PyObject_CallObject(pFunc, pArgs);
res = PyInt_AsLong(pValue);
return res;
}
從上述代碼可以窺見Python內部運行的方式:
所有Python元素,mole、function、tuple、string等等,實際上都是PyObject。C語言里操縱它們,一律使用PyObject *。
Python的類型與C語言類型可以相互轉換。Python類型XXX轉換為C語言類型YYY要使用PyXXX_AsYYY函數;C類型YYY轉換為Python類型XXX要使用PyXXX_FromYYY函數。
也可以創建Python類型的變數,使用PyXXX_New可以創建類型為XXX的變數。
若a是Tuple,則a[i] = b對應於 PyTuple_SetItem(a,i,b),有理由相信還有一個函數PyTuple_GetItem完成取得某一項的值。
不僅Python語言很優雅,Python的庫函數API也非常優雅。
現在我們得到了一個C語言的函數了,可以寫一個main測試它
#include <Python.h>
int great_function_from_python(int a);
int main(int argc, char *argv[]) {
Py_Initialize();
printf("%d",great_function_from_python(2));
Py_Finalize();
}
編譯的方式就用本節開頭使用的方法。
在Linux/Mac OSX運行此示例之前,可能先需要設置環境變數:
bash:
export PYTHONPATH=.:$PYTHONPATH
csh:
setenv PYTHONPATH .:$PYTHONPATH
2 Python 調用 C/C++(基礎篇)
這種做法稱為Python擴展。
比如說,我們有一個功能強大的C函數:
int great_function(int a) {
return a + 1;
}
期望在Python里這樣使用:
>>> from great_mole import great_function
>>> great_function(2)
3
考慮最簡單的情況。我們把功能強大的函數放入C文件 great_mole.c 中。
#include <Python.h>
int great_function(int a) {
return a + 1;
}
static PyObject * _great_function(PyObject *self, PyObject *args)
{
int _a;
int res;
if (!PyArg_ParseTuple(args, "i", &_a))
return NULL;
res = great_function(_a);
return PyLong_FromLong(res);
}
static PyMethodDef GreateMoleMethods[] = {
{
"great_function",
_great_function,
METH_VARARGS,
""
},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initgreat_mole(void) {
(void) Py_InitMole("great_mole", GreateMoleMethods);
}
除了功能強大的函數great_function外,這個文件中還有以下部分:
包裹函數_great_function。它負責將Python的參數轉化為C的參數(PyArg_ParseTuple),調用實際的great_function,並處理great_function的返回值,最終返回給Python環境。
導
出表GreateMoleMethods。它負責告訴Python這個模塊里有哪些函數可以被Python調用。導出表的名字可以隨便起,每一項有4
個參數:第一個參數是提供給Python環境的函數名稱,第二個參數是_great_function,即包裹函數。第三個參數的含義是參數變長,第四個
參數是一個說明性的字元串。導出表總是以{NULL, NULL, 0, NULL}結束。
導出函數initgreat_mole。這個的名字不是任取的,是你的mole名稱添加前綴init。導出函數中將模塊名稱與導出表進行連接。
在Windows下面,在Visual Studio命令提示符下編譯這個文件的命令是
cl /LD great_mole.c /o great_mole.pyd -IC:\Python27\include C:\Python27\libs\python27.lib
/LD 即生成動態鏈接庫。編譯成功後在當前目錄可以得到 great_mole.pyd(實際上是dll)。這個pyd可以在Python環境下直接當作mole使用。
在Linux下面,則用gcc編譯:
gcc -fPIC -shared great_mole.c -o great_mole.so -I/usr/include/python2.7/ -lpython2.7
在當前目錄下得到great_mole.so,同理可以在Python中直接使用。
本部分參考資料
《Python源碼剖析-深度探索動態語言核心技術》是系統介紹CPython實現以及運行原理的優秀教程。
Python 官方文檔的這一章詳細介紹了C/C++與Python的雙向互動Extending and Embedding the Python Interpreter
關於編譯環境,本文所述方法僅為出示原理所用。規范的方式如下:3. Building C and C++ Extensions with distutils
作為字典使用的官方參考文檔 Python/C API Reference Manual
用以上的方法實現C/C++與Python的混合編程,需要對Python的內部實現有相當的了解。接下來介紹當前較為成熟的技術Cython和SWIG。
3 C/C++ 調用 Python(使用Cython)
在
前面的小節中談到,Python的數據類型和C的數據類型貌似是有某種「一一對應」的關系的,此外,由於Python(確切的說是CPython)本身是
由C語言實現的,故Python數據類型之間的函數運算也必然與C語言有對應關系。那麼,有沒有可能「自動」的做替換,把Python代碼直接變成C代碼
呢?答案是肯定的,這就是Cython主要解決的問題。
安裝Cython非常簡單。Python 2.7.9以上的版本已經自帶easy_install:
easy_install -U cython
在Windows環境下依然需要Visual
Studio,由於安裝的過程需要編譯Cython的源代碼,故上述命令需要在Visual
Studio命令提示符下完成。一會兒使用Cython的時候,也需要在Visual
Studio命令提示符下進行操作,這一點和第一部分的要求是一樣的。
繼續以例子說明:
#great_mole.pyx
cdef public great_function(a,index):
return a[index]
這其中有非Python關鍵字cdef和public。這些關鍵字屬於Cython。由於我們需要在C語言中使用
「編譯好的Python代碼」,所以得讓great_function從外面變得可見,方法就是以「public」修飾。而cdef類似於Python的
def,只有使用cdef才可以使用Cython的關鍵字public。
這個函數中其他的部分與正常的Python代碼是一樣的。
接下來編譯 great_mole.pyx
cython great_mole.pyx
得到great_mole.h和great_mole.c。打開great_mole.h可以找到這樣一句聲明:
__PYX_EXTERN_C DL_IMPORT(PyObject) *great_function(PyObject *, PyObject *)
寫一個main使用great_function。注意great_function並不規定a是何種類型,它的
功能只是提取a的第index的成員而已,故使用great_function的時候,a可以傳入Python
String,也可以傳入tuple之類的其他可迭代類型。仍然使用之前提到的類型轉換函數PyXXX_FromYYY和PyXXX_AsYYY。
//main.c
#include <Python.h>
#include "great_mole.h"
int main(int argc, char *argv[]) {
PyObject *tuple;
Py_Initialize();
initgreat_mole();
printf("%s\n",PyString_AsString(
great_function(
PyString_FromString("hello"),
PyInt_FromLong(1)
)
));
tuple = Py_BuildValue("(iis)", 1, 2, "three");
printf("%d\n",PyInt_AsLong(
great_function(
tuple,
PyInt_FromLong(1)
)
));
printf("%s\n",PyString_AsString(
great_function(
tuple,
PyInt_FromLong(2)
)
));
Py_Finalize();
}
編譯命令和第一部分相同:
在Windows下編譯命令為
cl main.c great_mole.c -IC:\Python27\include C:\Python27\libs\python27.lib
在Linux下編譯命令為
gcc main.c great_mole.c -o main -I/usr/include/python2.7/ -lpython2.7
這個例子中我們使用了Python的動態類型特性。如果你想指定類型,可以利用Cython的靜態類型關鍵字。例子如下:
#great_mole.pyx
cdef public char great_function(const char * a,int index):
return a[index]
cython編譯後得到的.h里,great_function的聲明是這樣的:
__PYX_EXTERN_C DL_IMPORT(char) great_function(char const *, int);
很開心對不對!
這樣的話,我們的main函數已經幾乎看不到Python的痕跡了:
//main.c
#include <Python.h>
#include "great_mole.h"
int main(int argc, char *argv[]) {
Py_Initialize();
initgreat_mole();
printf("%c",great_function("Hello",2));
Py_Finalize();
}
在這一部分的最後我們給一個看似實用的應用(僅限於Windows):
還是利用剛才的great_mole.pyx,准備一個dllmain.c:
#include <Python.h>
#include <Windows.h>
#include "great_mole.h"
extern __declspec(dllexport) int __stdcall _great_function(const char * a, int b) {
return great_function(a,b);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved) {
switch( fdwReason ) {
case DLL_PROCESS_ATTACH:
Py_Initialize();
initgreat_mole();
break;
case DLL_PROCESS_DETACH:
Py_Finalize();
break;
}
return TRUE;
}
在Visual Studio命令提示符下編譯:
cl /LD dllmain.c great_mole.c -IC:\Python27\include C:\Python27\libs\python27.lib
會得到一個dllmain.dll。我們在Excel裡面使用它,沒錯,傳說中的Excel與Python混合編程:
參考資料:Cython的官方文檔,質量非常高:
Welcome to Cython』s Documentation
出自:Jerry Jho
C. python裝飾器有什麼用
先來個形象比方
內褲可以用來遮羞,但是到了冬天它沒法為我們防風禦寒,聰明的人們發明了長褲,有了長褲後寶寶再也不冷了,裝飾器就像我們這里說的長褲,在不影響內褲作用的前提下,給我們的身子提供了保暖的功效。
再回到我們的主題
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用於有切面需求的場景,比如:插入日誌、性能測試、事務處理、緩存、許可權校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。
先來看一個簡單例子:
def foo():
print('i am foo')
現在有一個新的需求,希望可以記錄下函數的執行日誌,於是在代碼中添加日誌代碼:
def foo():
print('i am foo')
logging.info("foo is running")
bar()、bar2()也有類似的需求,怎麼做?再寫一個logging在bar函數里?這樣就造成大量雷同的代碼,為了減少重復寫代碼,我們可以這樣做,重新定義一個函數:專門處理日誌 ,日誌處理完之後再執行真正的業務代碼
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func()def bar():
print('i am bar')use_logging(bar)
邏輯上不難理解,
但是這樣的話,我們每次都要將一個函數作為參數傳遞給use_logging函數。而且這種方式已經破壞了原有的代碼邏輯結構,之前執行業務邏輯時,執行運行bar(),但是現在不得不改成use_logging(bar)。那麼有沒有更好的方式的呢?當然有,答案就是裝飾器。
簡單裝飾器
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapperdef bar():
print('i am bar')bar = use_logging(bar)bar()
函數use_logging就是裝飾器,它把執行真正業務方法的func包裹在函數裡面,看起來像bar被use_logging裝飾了。在這個例子中,函數進入和退出時
,被稱為一個橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。
@符號是裝飾器的語法糖,在定義函數的時候使用,避免再一次賦值操作
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper@use_loggingdef foo():
print("i am foo")@use_loggingdef bar():
print("i am bar")bar()
如上所示,這樣我們就可以省去bar =
use_logging(bar)這一句了,直接調用bar()即可得到想要的結果。如果我們有其他的類似函數,我們可以繼續調用裝飾器來修飾函數,而不用重復修改函數或者增加新的封裝。這樣,我們就提高了程序的可重復利用性,並增加了程序的可讀性。
裝飾器在Python使用如此方便都要歸因於Python的函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被賦值給其他變數,可以作為返回值,可以被定義在另外一個函數內。
帶參數的裝飾器
裝飾器還有更大的靈活性,例如帶參數的裝飾器:在上面的裝飾器調用中,比如@use_logging,該裝飾器唯一的參數就是執行業務的函數。裝飾器的語法允許我們在調用時,提供其它參數,比如@decorator(a)。這樣,就為裝飾器的編寫和使用提供了更大的靈活性。
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator@use_logging(level="warn")def foo(name='foo'):
print("i am %s" % name)foo()
上面的use_logging是允許帶參數的裝飾器。它實際上是對原有裝飾器的一個函數封裝,並返回一個裝飾器。我們可以將它理解為一個含有參數的閉包。當我
們使用@use_logging(level="warn")調用的時候,Python能夠發現這一層的封裝,並把參數傳遞到裝飾器的環境中。
類裝飾器
再來看看類裝飾器,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器還可以依靠類內部的\_\_call\_\_方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
functools.wraps
使用裝飾器極大地復用了代碼,但是他有一個缺點就是原函數的元信息不見了,比如函數的docstring、__name__、參數列表,先看例子:
裝飾器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
函數
@loggeddef f(x):
"""does some math"""
return x + x * x
該函數完成等價於:
def f(x):
"""does some math"""
return x + x * xf = logged(f)
不難發現,函數f被with_logging取代了,當然它的docstring,__name__就是變成了with_logging函數的信息了。
print f.__name__ # prints 'with_logging'print f.__doc__ # prints None
這個問題就比較嚴重的,好在我們有functools.wraps,wraps本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器函數中,這使得裝飾器函數也有和原函數一樣的元信息了。
from functools import wrapsdef logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging@loggeddef f(x):
"""does some math"""
return x + x * xprint f.__name__ # prints 'f'print f.__doc__ # prints 'does some math'
內置裝飾器
@staticmathod、@classmethod、@property
裝飾器的順序
@a@b@cdef f ():
等效於
f = a(b(c(f)))
D. python列表中如何做到一個列表分割成多個列表
def groups(L1,len1):
groups=zip(*(iter(L1),)*len1)
L2=[list(i) for i in groups]
n=len(L1) % len1
L2.append(L1[-n:]) if n !=0 else L2
return L2
L1=[23,26,8,9, 'AB', 'CDEF']
print(groups(L1,3)
E. python怎麼使用cython
1. Cython是什麼?
它是一個用來快速生成Python擴展模塊(extention mole)的工具
語法是Python和c的混血
Cython作為一個Python的編譯器,在科學計算方面很流行,用於提高Python的速度,通過OpenMPI庫還可以進行吧並行計算。
2. Cython安裝(Windows)
我的環境是win7 x64, python27, vs2010
安裝的基礎是有一個c編譯器(這里以vs2010為例)
從http://cython.org下載安裝包,解壓到一目錄,進入該目錄,在cmd命令行中執行
python setup.py install
註:執行過程可能遇到問題:Windows下pip安裝包報錯:Microsoft Visual C++ 9.0 is required Unable to find vcvarsall.bat
解決方案:下載Microsoft Visual C++ Compiler for Python 2.7,點擊直接安裝即可。
3. 例子
例3.1:入門
創建hello.pyx,內容如下
def say_hello():
print "Hello World!"
創建setup.py,內容如下
from distutils.core import setup
from Cython.Build import cythonize
setup(name = 'Hello world app',
ext_moles = cythonize("hello.pyx"))
編譯Cython代碼
step1: 把.pyx文件被Cython便以為.c文件
step2: 把.c文件編譯為可導入的使用模塊.so(Windows下為.pyd)文件
1
2
python setup.py build
python setup.py install
註:可能出現問題:Unable to find vcvarsall.bat
原因:Python 2.7 會搜索 Visual Studio 2008.如果你電腦上沒有這個版本的話就會報錯。
如果裝的是vs2010,那麼在cmd命令行中執行
1
SET VS90COMNTOOLS=%VS100COMNTOOLS%
如果裝的是vs2010,那麼在cmd命令行中執行
1
SET VS90COMNTOOLS=%VS110COMNTOOLS%
執行
1
2
3
>>> import hello
>>> hello.say_hello()
Hello World!例3.2 通過靜態類型提高速度
在Cython中可以通過標記靜態類型來提高速度,凡是標記為靜態類型的部分都會將動態語言類型變為簡單的c代碼,從而提速。
但是如果濫用靜態類型,會降低可讀性,甚至因類型設置不當導致錯誤類型檢查造成速度降低。
例3.2.1 靜態類型變數
Python原生態代碼
compute.pyx
def f(x):
return x ** 2 - x
def integrate_f(a, b, N):
s = 0
dx = (b - a) / N
for i in range(N):
x += f(a + i * dx)
return s * dx
setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = 'Hello world app',
ext_moles = cythonize("compute.pyx"),
)
test.py
import compute
import time
starttime = time.clock()
compute.integrate_f(3.2, 6.9, 1000000)
endtime = time.clock()
print "read: %f s" %(endtime - starttime)
執行
1
2
3
python setup.py build
python setup.py install
python test.py
結果
1
read: 0.332332 s
使用靜態變數替換後的代碼
compute2.pyx
def f(double x):
return x ** 2 - x
def integrate_f(double a, double b, int N):
cdef int i
cdef double s, dx
s = 0
dx = (b - a) / N
for i in range(N):
s += f(a + i * dx)
return s * d
setup2.py
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = 'Hello world app',
ext_moles = cythonize("compute2.pyx"),
)
test2.py
import compute2
import time
starttime = time.clock()
compute2.integrate_f(3.2, 6.9, 1000000)
endtime = time.clock()
print "read: %f s" %(endtime - starttime)
執行
1
2
3
python setup.py build
python setup.py install
python test.py
結果
1
read: 0.109200s
結論
該測試用例,使用靜態類型速度是不使用靜態類型的3倍。
例3.2.2 靜態類型函數
把compute2.pyx中的函數變為
cdef double f(double x):
return x ** 2 - x
def integrate_f(double a, double b, int N):
cdef int i
cdef double s, dx
s = 0
dx = (b - a) / N
for i in range(N):
s += f(a + i * dx)
return s * dx
結果
1
read: 0.084859 s
結論:比例子3.2.1速度又快了
例3.3 調用C函數
cdef extern from "math.h":
double sin(double)
double cos(double)
cpdef double Sin(double x):
return sin(x)
cpdef double Cos(double x):
return cos(x)
cpdef: 對於Python可使用的函數使用(為了使得在以後的Python程序中調用Sin,Cos函數,用cpdef,而不用cdef)
cdef: 對於C可使用的函數使用
請注意,上面的代碼聲明了 math.h 里的函數,提供給 Cython 使用。C編譯器在編譯時將會看到 math.h 的聲明,但 Cython 不會去分析 math.h 和單獨的定義。
F. cython與python的不同有哪些
Cython是Python的一個超集,結合了Python的易用性和原生代碼的速度,可以編譯成C語言,產生的性能提升可以從幾個百分點到幾個數量級,具體取決於手頭的任務。
使用Cython,你可以避開Python的許多原生限制,或者完全超越Python,而無需放棄Python的簡便性和便捷性。
Python代碼可以直接調用C模塊。這些C模塊可以是通用的C庫或專門為Python工作的庫。Cython生成第二種類型的模塊:與Python內部對話的C庫,可以與現有的Python代碼綁定在一起。
Cython代碼在設計上看起來很像Python代碼。如果你給Cython編譯器提供了一個Python程序,它將會按原樣接受它,但是Cython的原生加速器都不會起作用。但是如果你用Cython的特殊語法來修飾Python代碼,那麼Cython就可以用快速的C代替慢的Python對象。
請注意,Cython的方法是漸進的。這意味著開發人員可以從現有的Python應用程序開始,通過對代碼立刻進行更改來加快速度,而不是從頭開始重寫整個應用程序。
這種方法通常與軟體性能問題的性質相吻合。在大多數程序中,絕大多數CPU密集型代碼都集中在一些熱點上,也就是帕累托原則的一個版本,也被稱為「80/20」規則。因此,Python應用程序中的大部分代碼不需要進行性能優化,只需要幾個關鍵部分。你可以逐漸將這些熱點轉換為Cython,從而獲得你最需要的性能提升。程序的其餘部分可以保留在Python中,以方便開發人員。
相關推薦:《Python入門教程》
Cython優勢
除了能夠加速已經編寫的代碼之外,Cython還具有其他幾個優點:
使用外部C庫可以更快
像NumPy這樣的Python軟體包可以在Python界面中打包C庫,使它們易於使用。但是,這些包在Python和C之間來回切換會減慢速度。Cython可以讓你直接與底層庫進行通信,而不需要Python(也支持C ++庫)。
可以同時使用C和Python內存管理
如果你使用Python對象,它們就像在普通的Python中一樣被內存管理和垃圾收集。但是如果你想創建和管理自己的C級結構,並使用malloc/free來處理它們,你可以這樣做,只記得自己清理一下。
可以根據需要選擇安全性或速度
Cython通過decorator 和編譯器指令(例如@boundscheck(False))自動執行對C中彈出的常見問題的運行時檢查,例如對數組的超出邊界訪問。因此,由Cython生成的C代碼默認比手動C代碼安全得多。
如果確信在運行時不需要這些檢查,則可以在整個模塊上或僅在選擇功能上禁用它們以獲得額外的編譯速度。
Cython還允許本地訪問使用「緩沖協議」的Python結構,以直接訪問存儲在內存中的數據(無需中間復制)。Cython的「記憶視圖」可以高速地在這些結構上進行工作,並且具有適合任務的安全級別。
Cython C代碼可以從釋放GIL中受益
Python的全局解釋器鎖(Global Interpreter Lock,GIL)同步解釋器中的線程,保護對Python對象的訪問並管理資源的爭用。但GIL被廣泛批評為Python性能的絆腳石,特別是在多核系統上。
如果有一段代碼不會引用Python對象並執行長時間運行,那麼可以使用nogil:指令將其標記為允許它在沒有GIL的情況下運行。這使得Python中間人可以做其他事情,並允許Cython代碼使用多個內核(附加工作)。
Cython可以使用Python類型的提示語法
Python有一個類型提示語法,主要由linters和代碼檢查器使用,而不是CPython解釋器。 Cython有它自己的代碼裝飾的自定義語法,但是最近修改了Cython,你可以使用Python類型提示語法為Cython提供類型提示。
Cython限制
請記住,Cython不是一個魔術棒。它不會自動將每一個poky Python代碼變成極速的C代碼。為了充分利用Cython,你必須明智地使用它,並理解它的局限性:
常規Python代碼的加速很少
當Cython遇到Python代碼時,它不能完全翻譯成C語言,它將這些代碼轉換成一系列對Python內部的C調用。這相當於將Python的解釋器從執行循環中提取出來,這使得代碼默認加速了15%到20%。請注意,這是最好的情況。在某些情況下,可能看不到性能改善,甚至性能下降。
原生Python數據結構有一點加速
Python提供了大量的數據結構 - 字元串,列表,元組,字典等等。它們對於開發者來說非常方便,而且他們自帶了自動內存管理功能,但是他們比純C慢。
Cython讓你繼續使用所有的Python數據結構,盡管沒有太多的加速。這又是因為Cython只是在Python運行時調用創建和操作這些對象的C API。因此,Python數據結構的行為與Cython優化的Python代碼大致相同:有時會得到一個提升,但只有一點。
Cython代碼運行速度最快時,「純C」
如果你在C中有一個標有cdef關鍵字的函數,那麼它的所有變數和內聯函數調用都是純C的,所以它的運行速度可以和C一樣快。 但是,如果該函數引用任何Python原生代碼(如Python數據結構或對內部Python API的調用),則該調用將成為性能瓶頸。
幸運的是,Cython提供了一種方法來發現這些瓶頸:一個源代碼報告,一目瞭然地顯示您的Cython應用程序的哪些部分是純C以及哪些部分與Python交互。 對應用程序進行了更好的優化,就會減少與Python的交互。
為Cython應用程序生成的源代碼報告。 白色區域純C;黃色區域顯示與Python內部的交互。一個精心優化的Cython程序將盡可能的黃色。 展開的最後一行顯示了解釋其相應Cython代碼的C代碼。
Cython NumPy
Cython改進了基於C的第三方數字運算庫(如NumPy)的使用。由於Cython代碼編譯為C,它可以直接與這些庫進行交互,並將Python的瓶頸帶出循環。
但是NumPy特別適用於Cython。 Cython對NumPy中的特定結構具有本地支持,並提供對NumPy數組的快速訪問。在傳統的Python腳本中使用的熟悉的NumPy語法可以在Cython中使用。
但是,如果要創建Cython和NumPy之間最接近的綁定,則需要使用Cython的自定義語法進一步修飾代碼。例如,cimport語句允許Cython代碼在編譯時在庫中查看C級構造,以實現最快的綁定。
由於NumPy被廣泛使用,Cython支持NumPy「開箱即用」。如果你安裝了NumPy,你可以在你的代碼中聲明cimport numpy,然後添加進一步的裝飾來使用暴露的函數。
Cython分析和性能
可以通過分析代碼並親眼目睹瓶頸在哪裡獲得最佳性能。Cython為Python的cProfile模塊提供鉤子,因此可以使用Python自己的分析工具來查看Cython代碼的執行情況。無需在工具組之間切換;可以繼續所熟悉和喜愛的Python世界中工作。
它有助於記住所有情況下,Cython不是魔術,仍然適用明智的現實世界的表現實踐。在Python和Cython之間來回穿梭越少,你的應用運行得越快。
例如,如果你有一個你想要在Cython中處理的對象的集合,那麼不要在Python中迭代它,並且在每一步調用一個Cython函數。將整個集合傳遞給你的Cython模塊並在那裡迭代。這種技術經常在管理數據的庫中使用,因此這是在自己的代碼中模擬的好模型。
我們使用Python是因為它為程序員提供了便利,並且能夠快速開發。有時程序員的工作效率是以犧牲性能為代價的。使用Cython,只需要一點點額外的努力就可以給你兩全其美的好處。
G. 如何系統地自學 Python
是否非常想學好 Python,一方面被瑣事糾纏,一直沒能動手,另一方面,擔心學習成本太高,心裡默默敲著退堂鼓?
幸運的是,Python 是一門初學者友好的編程語言,想要完全掌握它,你不必花上太多的時間和精力。
Python 的設計哲學之一就是簡單易學,體現在兩個方面:
語法簡潔明了:相對 Ruby 和 Perl,它的語法特性不多不少,大多數都很簡單直接,不玩兒玄學。
切入點很多:Python 可以讓你可以做很多事情,科學計算和數據分析、爬蟲、Web 網站、游戲、命令行實用工具等等等等,總有一個是你感興趣並且願意投入時間的。
廢話不多說,學會一門語言的捷徑只有一個: Getting Started
¶ 起步階段
任何一種編程語言都包含兩個部分:硬知識和軟知識,起步階段的主要任務是掌握硬知識。
硬知識
「硬知識」指的是編程語言的語法、演算法和數據結構、編程範式等,例如:變數和類型、循環語句、分支、函數、類。這部分知識也是具有普適性的,看上去是掌握了一種語法,實際是建立了一種思維。例如:讓一個 Java 程序員去學習 Python,他可以很快的將 Java 中的學到的面向對象的知識 map 到 Python 中來,因此能夠快速掌握 Python 中面向對象的特性。
如果你是剛開始學習編程的新手,一本可靠的語法書是非常重要的。它看上去可能非常枯燥乏味,但對於建立穩固的編程思維是必不可少。
下面列出了一些適合初學者入門的教學材料:
廖雪峰的 Python 教程 Python 中文教程的翹楚,專為剛剛步入程序世界的小白打造。
笨方法學 Python 這本書在講解 Python 的語法成分時,還附帶大量可實踐的例子,非常適合快速起步。
The Hitchhiker』s Guide to Python! 這本指南著重於 Python 的最佳實踐,不管你是 Python 專家還是新手,都能獲得極大的幫助。
Python 的哲學:
學習也是一樣,雖然推薦了多種學習資料,但實際學習的時候,最好只選擇其中的一個,堅持看完。
必要的時候,可能需要閱讀講解數據結構和演算法的書,這些知識對於理解和使用 Python 中的對象模型有著很大的幫助。
軟知識
「軟知識」則是特定語言環境下的語法技巧、類庫的使用、IDE的選擇等等。這一部分,即使完全不了解不會使用,也不會妨礙你去編程,只不過寫出的程序,看上去顯得「傻」了些。
對這些知識的學習,取決於你嘗試解決的問題的領域和深度。對初學者而言,起步階段極易走火,或者在選擇 Python 版本時徘徊不決,一會兒看 2.7 一會兒又轉到 3.0,或者徜徉在類庫的大海中無法自拔,Scrapy,Numpy,Django 什麼都要試試,或者參與編輯器聖戰、大括弧縮進探究、操作系統辯論賽等無意義活動,或者整天跪舔語法糖,老想著怎麼一行代碼把所有的事情做完,或者去構想聖潔的性能安全通用性健壯性全部滿分的解決方案。
很多「大牛」都會告誡初學者,用這個用那個,少走彎路,這樣反而把初學者推向了真正的彎路。
還不如告訴初學者,學習本來就是個需要你去走彎路出 Bug,只能腳踏實地,沒有奇跡只有狗屎的過程。
選擇一個方向先走下去,哪怕臟丑差,走不動了再看看有沒有更好的解決途徑。
自己走了彎路,你才知道這么做的好處,才能理解為什麼人們可以手寫狀態機去匹配卻偏要發明正則表達式,為什麼面向過程可以解決卻偏要面向對象,為什麼我可以操縱每一根指針卻偏要自動管理內存,為什麼我可以嵌套回調卻偏要用 Promise...
更重要的是,你會明白,高層次的解決方法都是對低層次的封裝,並不是任何情況下都是最有效最合適的。
技術涌進就像波浪一樣,那些陳舊的封存已久的技術,消退了遲早還會涌回的。就像現在移動端應用、手游和 HTML5 的火熱,某些方面不正在重演過去 PC 的那些歷史么?
因此,不要擔心自己走錯路誤了終身,堅持並保持進步才是正道。
起步階段的核心任務是掌握硬知識,軟知識做適當了解,有了穩固的根,粗壯的枝幹,才能長出濃密的葉子,結出甜美的果實。
¶ 發展階段
完成了基礎知識的學習,必定會感到一陣空虛,懷疑這些語法知識是不是真的有用。
沒錯,你的懷疑是非常正確的。要讓 Python 發揮出它的價值,當然不能停留在語法層面。
發展階段的核心任務,就是「跳出 Python,擁抱世界」。
在你面前會有多個分支:科學計算和數據分析、爬蟲、Web 網站、游戲、命令行實用工具等等等等,這些都不是僅僅知道 Python 語法就能解決的問題。
拿爬蟲舉例,如果你對計算機網路,HTTP 協議,HTML,文本編碼,JSON 一無所知,你能做好這部分的工作么?而你在起步階段的基礎知識也同樣重要,如果你連循環遞歸怎麼寫都還要查文檔,連 BFS 都不知道怎麼實現,這就像工匠做石凳每次起錘都要思考錘子怎麼使用一樣,非常低效。
在這個階段,不可避免要接觸大量類庫,閱讀大量書籍的。
類庫方面
「Awesome Python 項目」:vinta/awesome-python · GitHub
這里列出了你在嘗試解決各種實際問題時,Python 社區已有的工具型類庫,如下圖所示:
vinta/awesome-python
你可以按照實際需求,尋找你需要的類庫。
至於相關類庫如何使用,必須掌握的技能便是閱讀文檔。由於開源社區大多數文檔都是英文寫成的,所以,英語不好的同學,需要惡補下。
書籍方面
這里我只列出一些我覺得比較有一些幫助的書籍,詳細的請看豆瓣的書評:
科學和數據分析:
❖「集體智慧編程」:集體智慧編程 (豆瓣)
❖「數學之美」:數學之美 (豆瓣)
❖「統計學習方法」:統計學習方法 (豆瓣)
❖「Pattern Recognition And Machine Learning」:Pattern Recognition And Machine Learning (豆瓣)
❖「數據科學實戰」:數據科學實戰 (豆瓣)
❖「數據檢索導論」:信息檢索導論 (豆瓣)
爬蟲:
❖「HTTP 權威指南」:HTTP權威指南 (豆瓣)
Web 網站:
❖「HTML & CSS 設計與構建網站」:HTML & CSS設計與構建網站 (豆瓣)
...
列到這里已經不需要繼續了。
聰明的你一定會發現上面的大部分書籍,並不是講 Python 的書,而更多的是專業知識。
事實上,這里所謂「跳出 Python,擁抱世界」,其實是發現 Python 和專業知識相結合,能夠解決很多實際問題。這個階段能走到什麼程度,更多的取決於自己的專業知識。
¶ 深入階段
這個階段的你,對 Python 幾乎了如指掌,那麼你一定知道 Python 是用 C 語言實現的。
可是 Python 對象的「動態特徵」是怎麼用相對底層,連自動內存管理都沒有的C語言實現的呢?這時候就不能停留在表面了,勇敢的拆開 Python 的黑盒子,深入到語言的內部,去看它的歷史,讀它的源碼,才能真正理解它的設計思路。
這里推薦一本書:
「Python 源碼剖析」:Python源碼剖析 (豆瓣)
這本書把 Python 源碼中最核心的部分,給出了詳細的闡釋,不過閱讀此書需要對 C 語言內存模型和指針有著很好的理解。
另外,Python 本身是一門雜糅多種範式的動態語言,也就是說,相對於 C 的過程式、 Haskell 等的函數式、Java 基於類的面向對象而言,它都不夠純粹。換而言之,編程語言的「道學」,在 Python 中只能有限的體悟。學習某種編程範式時,從那些面向這種範式更加純粹的語言出發,才能有更深刻的理解,也能了解到 Python 語言的根源。
這里推薦一門公開課
「編程範式」:斯坦福大學公開課:編程範式
講師高屋建瓴,從各種編程範式的代表語言出發,給出了每種編程範式最核心的思想。
值得一提的是,這門課程對C語言有非常深入的講解,例如C語言的范型和內存管理。這些知識,對閱讀 Python 源碼也有大有幫助。
Python 的許多最佳實踐都隱藏在那些眾所周知的框架和類庫中,例如 Django、Tornado 等等。在它們的源代碼中淘金,也是個不錯的選擇。
¶ 最後的話
每個人學編程的道路都是不一樣的,其實大都殊途同歸,沒有迷路的人只有不能堅持的人!
希望想學 Python 想學編程的同學,不要猶豫了,看完這篇文章,
Just Getting Started !!!
H. python[1:12]是什麼意思
#截取變數(str,list,tuple類型變數)的第2到第11個之間的所有元素。比如;
import string;
test=[str(j) for j in string.ascii_lowercase];
t=tuple(test)[2:12]#type of tuple;
l=list(test)[2:12]#type of list;
s="".join(test)[2:12]#type of str;
print(f"{test=}\n{t=}\n{l=}\n{s=}");
'''
test=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't'爛高, 'u', 'v', 'w', 'x', 'y', 'z']
t=('c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', '纖稿k', 'l')
l=['c', 'd', 'e', 'f', 'g', '飢豎尺h', 'i', 'j', 'k', 'l']
s='cdefghijkl'
'''
I. python中flask如何降低內存
Dict
在小型程序中,特別是在腳本中,使用Python自帶的dict來表示結構信息非常簡單方便:
>>> ob = {'x':1, 'y':2, 'z':3}
>>> x = ob['x']
>>> ob['y'] = y
由於在Python 3.6中dict的實現採用了一組有序鍵,因此其結構更為緊湊,更深得人心。但是,讓我們看看dict在內容中佔用的空間大小:
>>> print(sys.getsizeof(ob))
240
如上所示,dict佔用了大量內存,尤其是如果突然虛需要創建大量實例時:
實例數
對象大小
1 000 000
240 Mb
10 000 000
2.40 Gb
100 000 000
24 Gb
類實例
有些人希望將所有東西都封裝到類中,他們更喜歡將結構定義為可以通過屬性名訪問的類:
class Point:
#
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
>>> ob = Point(1,2,3)
>>> x = ob.x
>>> ob.y = y
類實例的結構很有趣:
欄位
大小(比特)
PyGC_Head
24
PyObject_HEAD
16
__weakref__
8
__dict__
8
合計:
56
在上表中,__weakref__是該列表的引用,稱之為到該對象的弱引用(weak reference);欄位__dict__是該類的實例字典的引用,其中包含實例屬性的值(注意在64-bit引用平台中佔用8位元組)。從Python3.3開始,所有類實例的字典的鍵都存儲在共享空間中。這樣就減少了內存中實例的大小:
>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))
56 112
因此,大量類實例在內存中佔用的空間少於常規字典(dict):
實例數
大小
1 000 000
168 Mb
10 000 000
1.68 Gb
100 000 000
16.8 Gb
不難看出,由於實例的字典很大,所以實例依然佔用了大量內存。
帶有__slots__的類實例
為了大幅降低內存中類實例的大小,我們可以考慮幹掉__dict__和__weakref__。為此,我們可以藉助 __slots__:
class Point:
__slots__ = 'x', 'y', 'z'
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
64
如此一來,內存中的對象就明顯變小了:
欄位
大小(比特)
PyGC_Head
24
PyObject_HEAD
16
x
8
y
8
z
8
總計:
64
在類的定義中使用了__slots__以後,大量實例占據的內存就明顯減少了:
實例數
大小
1 000 000
64 Mb
10 000 000
640 Mb
100 000 000
6.4 Gb
目前,這是降低類實例佔用內存的主要方式。
這種方式減少內存的原理為:在內存中,對象的標題後面存儲的是對象的引用(即屬性值),訪問這些屬性值可以使用類字典中的特殊描述符:
>>> pprint(Point.__dict__)
mappingproxy(
....................................
'x': ,
'y': ,
'z': })
為了自動化使用__slots__創建類的過程,你可以使用庫namedlist(https://pypi.org/project/namedlist)。namedlist.namedlist函數可以創建帶有__slots__的類:
>>> Point = namedlist('Point', ('x', 'y', 'z'))
還有一個包attrs(https://pypi.org/project/attrs),無論使用或不使用__slots__都可以利用這個包自動創建類。
元組
Python還有一個自帶的元組(tuple)類型,代表不可修改的數據結構。元組是固定的結構或記錄,但它不包含欄位名稱。你可以利用欄位索引訪問元組的欄位。在創建元組實例時,元組的欄位會一次性關聯到值對象:
>>> ob = (1,2,3)
>>> x = ob[0]
>>> ob[1] = y # ERROR
元組實例非常緊湊:
>>> print(sys.getsizeof(ob))
72
由於內存中的元組還包含欄位數,因此需要佔據內存的8個位元組,多於帶有__slots__的類:
欄位
大小(位元組)
PyGC_Head
24
PyObject_HEAD
16
ob_size
8
[0]
8
[1]
8
[2]
8
總計:
72
命名元組
由於元組的使用非常廣泛,所以終有一天你需要通過名稱訪問元組。為了滿足這種需求,你可以使用模塊collections.namedtuple。
namedtuple函數可以自動生成這種類:
>>> Point = namedtuple('Point', ('x', 'y', 'z'))
如上代碼創建了元組的子類,其中還定義了通過名稱訪問欄位的描述符。對於上述示例,訪問方式如下:
class Point(tuple):
#
@property
def _get_x(self):
return self[0]
@property
def _get_y(self):
return self[1]
@property
def _get_z(self):
return self[2]
#
def __new__(cls, x, y, z):
return tuple.__new__(cls, (x, y, z))
這種類所有的實例所佔用的內存與元組完全相同。但大量的實例佔用的內存也會稍稍多一些:
實例數
大小
1 000 000
72 Mb
10 000 000
720 Mb
100 000 000
7.2 Gb
記錄類:不帶循環GC的可變更命名元組
由於元組及其相應的命名元組類能夠生成不可修改的對象,因此類似於ob.x的對象值不能再被賦予其他值,所以有時還需要可修改的命名元組。由於Python沒有相當於元組且支持賦值的內置類型,因此人們想了許多辦法。在這里我們討論一下記錄類(recordclass,https://pypi.org/project/recordclass),它在StackoverFlow上廣受好評(https://stackoverflow.com/questions/29290359/existence-of-mutable-named-tuple-in)。
此外,它還可以將對象佔用的內存量減少到與元組對象差不多的水平。
recordclass包引入了類型recordclass.mutabletuple,它幾乎等價於元組,但它支持賦值。它會創建幾乎與namedtuple完全一致的子類,但支持給屬性賦新值(而不需要創建新的實例)。recordclass函數與namedtuple函數類似,可以自動創建這些類:
>>>Point = recordclass('Point', ('x', 'y', 'z'))
>>>ob = Point(1, 2, 3)
類實例的結構也類似於tuple,但沒有PyGC_Head:
欄位
大小(位元組)
PyObject_HEAD
16
ob_size
8
x
8
y
8
z
8
總計:
48
在默認情況下,recordclass函數會創建一個類,該類不參與垃圾回收機制。一般來說,namedtuple和recordclass都可以生成表示記錄或簡單數據結構(即非遞歸結構)的類。在Python中正確使用這二者不會造成循環引用。因此,recordclass生成的類實例默認情況下不包含PyGC_Head片段(這個片段是支持循環垃圾回收機制的必需欄位,或者更准確地說,在創建類的PyTypeObject結構中,flags欄位默認情況下不會設置Py_TPFLAGS_HAVE_GC標志)。
大量實例佔用的內存量要小於帶有__slots__的類實例:
實例數
大小
1 000 000
48 Mb10 000 000
480 Mb
100 000 000
4.8 Gb
dataobject
recordclass庫提出的另一個解決方案的基本想法為:內存結構採用與帶__slots__的類實例同樣的結構,但不參與循環垃圾回收機制。這種類可以通過recordclass.make_dataclass函數生成:
>>> Point = make_dataclass('Point', ('x', 'y', 'z'))
這種方式創建的類默認會生成可修改的實例。
另一種方法是從recordclass.dataobject繼承:
class Point(dataobject):
x:int
y:int
z:int
這種方法創建的類實例不會參與循環垃圾回收機制。內存中實例的結構與帶有__slots__的類相同,但沒有PyGC_Head:
欄位
大小(位元組)
PyObject_HEAD
16
ob_size
8
x
8
y
8
z
8
總計:
48
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
40
如果想訪問欄位,則需要使用特殊的描述符來表示從對象開頭算起的偏移量,其位置位於類字典內:
mappingproxy({'__new__': ,
.......................................
'x': ,
'y': ,
'z': })
大量實例佔用的內存量在CPython實現中是最小的:
實例數
大小
1 000 000
40 Mb
10 000 000
400 Mb
100 000 000
4.0 Gb
Cython
還有一個基於Cython(https://cython.org/)的方案。該方案的優點是欄位可以使用C語言的原子類型。訪問欄位的描述符可以通過純Python創建。例如:
cdef class Python:
cdef public int x, y, z
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
本例中實例佔用的內存更小:
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
32
內存結構如下:
欄位
大小(位元組)