Python 和C#的交互

https://blog.csdn.net/henu_xk126com

 

IronPython和C#交互

 

IronPython是一個.NET平臺上的Python實現,包括了完整的編譯器、執行引擎與運行時支持,可以與.NET已有的庫無縫整合到一塊兒。html

IronPython已經很好的集成到了.NET framework中,因此Ironpython和C#的交互也就變得很簡單了。下面就經過一些簡單的例子來看看IronPython和C#之間的交互。python

環境設置

工欲善其事,必先利其器,因此在開始IronPython的開發以前,咱們先找到一個方便的開發環境。ide

PTVS(Python tools for Visual Studio)是一個免費開源的VisualStudio的插件,支持 VisualStudio 2010/2012/2013,安裝好這個插件以後,咱們就能夠直接經過VS進行IronPython的開發了。函數

下面一個截圖顯示了咱們能夠新建的項目模板:post

IronPython調用C#

首先咱們看下如何在IronPython中使用C#的簡單例子。性能

使用標準.NET庫

在.NET中,有不少標準庫,在IronPython中,就可使用import來引入這些標準庫來直接使用。看一個簡單的例子,咱們使用.NET中的String和DateTime測試

from System import DateTime, String 
formatStr = String.Format("{0} {1}", "Hello World! The current date and time is ", DateTime.Now) 
print formatStr  
print dir(String)
raw_input("press Enter to exit!")

代碼輸出以下,能夠看到在IronPython代碼中,能夠經過String的Format方法進行字符串格式化的輸出。this

引入.NET庫

在.NET開發中,會常常經過References來引用一些.NET庫,固然在IronPython項目中,也能夠引用並使用.NET庫。lua

例如,如今咱們有一個Calc的計算類型,裏面有一個Add和Sub方法。經過這個類型,生成了一個CalcLib.dll。url

複製代碼

複製代碼

namespace CalcLib
{
    public class Calc
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public int Sub(int a, int b)
        {
            return a - b;
        }
    }
}

複製代碼

複製代碼

下面看看如何在IronPython項目中使用這個dll,在IronPython中,可使用"clr"模塊來添加.NET引用:

複製代碼

複製代碼

import clr
clr.AddReference('CalcLib')
#clr.AddReferenceToFile('CalcLib.dll')
from CalcLib import Calc
print dir(Calc)
calcObj = Calc()
print "result of 3+4 is:", calcObj.Add(3,4)
print "result of 10+2 is:", calcObj.Sub(10,2)

raw_input("press Enter to exit!")

複製代碼

複製代碼

代碼輸出以下,當引用了CalcLib.dll以後,咱們就可使用Calc類型建立實例,而且使用實例的C#方法。

IronPython建立WPF應用

在IronPython項目中,也能夠引入WPF相關的.NET庫,這樣就能夠方便的建立圖形界面應用。

安裝過PTVS以後,裏面有個"IronPython WPF Application"的模板,經過這個模板,能夠直接建立WPF應用。

在新建的項目中,VS會幫咱們自動引用WPF相關的庫,同時會有一個.py和.xaml文件。

下面看一個簡單的例子,經過IrpnPython實現的一個簡單計算器,界面的代碼以下:

複製代碼

複製代碼

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
       
    <TextBlock Name="InputTb" Grid.Row="0" Grid.ColumnSpan="4"/>
    <TextBlock Name="ResultTb" Grid.Row="1" Grid.ColumnSpan="3"/>
       
    <Button Content="1" Grid.Row="2" Grid.Column="0" Click="Input_Button_Click"/>
    <Button Content="2" Grid.Row="2" Grid.Column="1" Click="Input_Button_Click"/>
    <Button Content="3" Grid.Row="2" Grid.Column="2" Click="Input_Button_Click"/>
    <Button Content="4" Grid.Row="3" Grid.Column="0" Click="Input_Button_Click"/>
    <Button Content="5" Grid.Row="3" Grid.Column="1" Click="Input_Button_Click"/>
    <Button Content="6" Grid.Row="3" Grid.Column="2" Click="Input_Button_Click"/>
    <Button Content="7" Grid.Row="4" Grid.Column="0" Click="Input_Button_Click"/>
    <Button Content="8" Grid.Row="4" Grid.Column="1" Click="Input_Button_Click"/>
    <Button Content="9" Grid.Row="4" Grid.Column="2" Click="Input_Button_Click"/>
    <Button Content="0" Grid.Row="5" Grid.Column="0" Click="Input_Button_Click"/>
    <Button Content="+" Grid.Row="2" Grid.Column="3" Click="Input_Button_Click"/>
    <Button Content="-" Grid.Row="3" Grid.Column="3" Click="Input_Button_Click"/>
    <Button Content="*" Grid.Row="4" Grid.Column="3" Click="Input_Button_Click"/>
    <Button Content="/" Grid.Row="5" Grid.Column="3" Click="Input_Button_Click"/>
    <Button Content="." Grid.Row="5" Grid.Column="1" Click="Input_Button_Click"/>
    
    <Button Content="C" Grid.Row="5" Grid.Column="2" Click="Clear_Button_Click"/>

    <Button Content="=" Grid.Row="1" Grid.Column="3" Click="Calc_Button_Click"/>
</Grid>

複製代碼

複製代碼

對應的IronPython代碼以下:

複製代碼

複製代碼

from __future__ import division
import traceback 
import wpf

from System.Windows import Application, Window, MessageBox

class MyWindow(Window):
    def __init__(self):
        wpf.LoadComponent(self, 'IronPythonWPF.xaml')

    def Calc_Button_Click(self, sender, e):
        try:
            result = eval(self.InputTb.Text)
            self.ResultTb.Text = str(result)
        except Exception, e:
            tracelog = traceback.format_exc()
            MessageBox.Show(str(e))
            
        pass
    
    def Clear_Button_Click(self, sender, e):
        self.InputTb.Text = ""
        self.ResultTb.Text = ""
        pass
    
    def Input_Button_Click(self, sender, e):
        self.InputTb.Text += sender.Content
        pass
    
if __name__ == '__main__':
    Application().Run(MyWindow())

複製代碼

複製代碼

代碼運行效果以下:

C#調用IronPython

前面介紹了在IronPython中如何使用.NET庫,下面看看經過C#代碼執行IronPython腳本。在.NET framework中,包含了IronPython的編譯器和執行引擎,因此咱們能夠經過C#代碼建立一個引擎實例,而後執行腳本。

先看看咱們須要使用的類型:

  • ScriptEngine: 動態語言(IronPython)執行類,可於解析和執行動態語言代碼。
  • ScriptScope:構建一個執行上下文,其中保存了環境及全局變量;宿主(Host)能夠經過建立不一樣的 ScriptScope 來提供多個數據隔離的執行上下文。
  • ScriptSource:操控動態語言代碼的類型,能夠編譯(Compile)、運行(Execute)代碼。

如今咱們有一個簡單的打印當前時間的IronPython腳本:

import datetime
print "current datetiem is:", datetime.datetime.now()

而後就可使用下面的方式執行腳本:

複製代碼

複製代碼

static void Main(string[] args)
{
    try
    {
        ScriptEngine engine = Python.CreateEngine();
        ScriptScope scope = engine.CreateScope();

        ScriptSource script = engine.CreateScriptSourceFromFile(@"Script.py");

        var result = script.Execute(scope);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

    Console.Read();
}

複製代碼

複製代碼

給IronPython傳遞參數

在ScriptScope類型中,有一個SetVariable方法,咱們能夠經過這個方法給腳本傳遞參數。

public void SetVariable(string name, object value)

這樣,咱們就能夠把一個C#實例傳遞給IronPython,而後腳本就可使用C#實例的成員。看一個例子:

複製代碼

複製代碼

public class Student
{
    public int Age { get; set; }
    public string Name { get; set; }
    public override string ToString()
    {
        return string.Format("{0} is {1} years old", this.Name, this.Age);
    }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();
            Student stu = new Student { Name = "Wilber", Age = 28 };
            scope.SetVariable("stuObj", stu);
            ScriptSource script = engine.CreateScriptSourceFromFile(@"PrintStuInfo.py");

            var result = script.Execute(scope);

        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }

        Console.Read();
    }
}

複製代碼

複製代碼

在這個例子中,C#代碼中建立了一個Student類型的實例,並把這個實例傳遞給了PrintStuInfo.py腳本。

print "Student name:", stuObj.Name
print "Student age:", stuObj.Age
print stuObj.ToString()

經過輸出能夠看到,IronPython腳本能夠方便的訪問C#實例的成員。

總結

本篇文章經過一些例子演示了IronPython與C#的交互,感受有幾個例子仍是頗有意思的。

有時候使用C#調用IronPython可使程序變得更加靈活,經過一個C#類型提供一組封裝好的操做,每次構建類型實例而後傳遞給腳本;這樣,用戶就能夠編寫IronPython腳本,而後使用C#類型中提供的操做方法,從而實現不一樣的自定義操做。

 

 

 

Python迭代器和生成器

 

在Python中,不少對象都是能夠經過for語句來直接遍歷的,例如list、string、dict等等,這些對象均可以被稱爲可迭代對象。至於說哪些對象是能夠被迭代訪問的,就要了解一下迭代器相關的知識了。

迭代器

迭代器對象要求支持迭代器協議的對象,在Python中,支持迭代器協議就是實現對象的__iter__()和next()方法。其中__iter__()方法返回迭代器對象自己;next()方法返回容器的下一個元素,在結尾時引起StopIteration異常。

__iter__()和next()方法

這兩個方法是迭代器最基本的方法,一個用來得到迭代器對象,一個用來獲取容器中的下一個元素。

對於可迭代對象,可使用內建函數iter()來獲取它的迭代器對象:

例子中,經過iter()方法得到了list的迭代器對象,而後就能夠經過next()方法來訪問list中的元素了。當容器中沒有可訪問的元素後,next()方法將會拋出一個StopIteration異常終止迭代器。

其實,當咱們使用for語句的時候,for語句就會自動的經過__iter__()方法來得到迭代器對象,而且經過next()方法來獲取下一個元素。

自定義迭代器

瞭解了迭代器協議以後,就能夠自定義迭代器了。

下面例子中實現了一個MyRange的類型,這個類型中實現了__iter__()方法,經過這個方法返回對象自己做爲迭代器對象;同時,實現了next()方法用來獲取容器中的下一個元素,當沒有可訪問元素後,就拋出StopIteration異常。

複製代碼

複製代碼

class MyRange(object):
    def __init__(self, n):
        self.idx = 0
        self.n = n
        
    def __iter__(self):
        return self
        
    def next(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return val
        else:
            raise StopIteration()

複製代碼

複製代碼

這個自定義類型跟內建函數xrange很相似,看一下運行結果:

myRange = MyRange(3)
for i in myRange:
    print i   

迭代器和可迭代對象

在上面的例子中,myRange這個對象就是一個可迭代對象,同時它自己也是一個迭代器對象。

看下面的代碼,對於一個可迭代對象,若是它自己又是一個迭代器對象,就會有下面的 問題,就沒有辦法支持屢次迭代。

爲了解決上面的問題,能夠分別定義可迭代類型對象和迭代器類型對象;而後可迭代類型對象的__iter__()方法能夠得到一個迭代器類型的對象。看下面的實現:

複製代碼

複製代碼

class Zrange:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return ZrangeIterator(self.n)

class ZrangeIterator:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()    

            
zrange = Zrange(3)
print zrange is iter(zrange)         

print [i for i in zrange]
print [i for i in zrange]

複製代碼

複製代碼

代碼的運行結果爲:

其實,經過下面代碼能夠看出,list類型也是按照上面的方式,list自己是一個可迭代對象,經過iter()方法能夠得到list的迭代器對象:

生成器

在Python中,使用生成器能夠很方便的支持迭代器協議。生成器經過生成器函數產生,生成器函數能夠經過常規的def語句來定義,可是不用return返回,而是用yield一次返回一個結果,在每一個結果之間掛起和繼續它們的狀態,來自動實現迭代協議。

也就是說,yield是一個語法糖,內部實現支持了迭代器協議,同時yield內部是一個狀態機,維護着掛起和繼續的狀態。

下面看看生成器的使用:

在這個例子中,定義了一個生成器函數,函數返回一個生成器對象,而後就能夠經過for語句進行迭代訪問了。

其實,生成器函數返回生成器的迭代器。 "生成器的迭代器"這個術語一般被稱做"生成器"。要注意的是生成器就是一類特殊的迭代器。做爲一個迭代器,生成器必需要定義一些方法,其中一個就是next()。如同迭代器同樣,咱們可使用next()函數來獲取下一個值。

生成器執行流程

下面就仔細看看生成器是怎麼工做的。

從上面的例子也能夠看到,生成器函數跟普通的函數是有很大差異的。

結合上面的例子咱們加入一些打印信息,進一步看看生成器的執行流程:

經過結果能夠看到:

  • 當調用生成器函數的時候,函數只是返回了一個生成器對象,並無 執行。
  • 當next()方法第一次被調用的時候,生成器函數纔開始執行,執行到yield語句處中止

    • next()方法的返回值就是yield語句處的參數(yielded value)
  • 當繼續調用next()方法的時候,函數將接着上一次中止的yield語句處繼續執行,併到下一個yield處中止;若是後面沒有yield就拋出StopIteration異常

生成器表達式

在開始介紹生成器表達式以前,先看看咱們比較熟悉的列表解析( List comprehensions),列表解析通常都是下面的形式。

[expr for iter_var in iterable if cond_expr]

迭代iterable裏全部內容,每一次迭代後,把iterable裏知足cond_expr條件的內容放到iter_var中,再在表達式expr中應該iter_var的內容,最後用表達式的計算值生成一個列表。

例如,生成一個list來保護50之內的因此奇數:

[i for i in range(50) if i%2]

生成器表達式是在python2.4中引入的,當序列過長, 而每次只須要獲取一個元素時,應當考慮使用生成器表達式而不是列表解析。生成器表達式的語法和列表解析同樣,只不過生成器表達式是被()括起來的,而不是[],以下:

(expr for iter_var in iterable if cond_expr)

看一個例子:

生成器表達式並非建立一個列表, 而是返回一個生成器,這個生成器在每次計算出一個條目後,把這個條目"產生"(yield)出來。 生成器表達式使用了"惰性計算"(lazy evaluation),只有在檢索時才被賦值(evaluated),因此在列表比較長的狀況下使用內存上更有效。

繼續看一個例子:

從這個例子中能夠看到,生成器表達式產生的生成器,它自身是一個可迭代對象,同時也是迭代器自己。

遞歸生成器

生成器能夠向函數同樣進行遞歸使用的,下面看一個簡單的例子,對一個序列進行全排列:

複製代碼

複製代碼

def permutations(li):
    if len(li) == 0:
        yield li
    else:
        for i in range(len(li)):
            li[0], li[i] = li[i], li[0]
            for item in permutations(li[1:]):
                yield [li[0]] + item
    
for item in permutations(range(3)):
    print item

複製代碼

複製代碼

代碼的結果爲:

生成器的send()和close()方法

生成器中還有兩個很重要的方法:send()和close()。

  • send(value):

    從前面瞭解到,next()方法能夠恢復生成器狀態並繼續執行,其實send()是除next()外另外一個恢復生成器的方法。

    Python 2.5中,yield語句變成了yield表達式,也就是說yield能夠有一個值,而這個值就是send()方法的參數,因此send(None)和next()是等效的。一樣,next()和send()的返回值都是yield語句處的參數(yielded value)

    關於send()方法須要注意的是:調用send傳入非None值前,生成器必須處於掛起狀態,不然將拋出異常。也就是說,第一次調用時,要使用next()語句或send(None),由於沒有yield語句來接收這個值。

  • close():

    這個方法用於關閉生成器,對關閉的生成器後再次調用next或send將拋出StopIteration異常。

下面看看這兩個方法的使用:

總結

本文介紹了Python迭代器和生成器的相關內容。

  • 經過實現迭代器協議對應的__iter__()和next()方法,能夠自定義迭代器類型。對於可迭代對象,for語句能夠經過iter()方法獲取迭代器,而且經過next()方法得到容器的下一個元素。
  • 像列表這種序列類型的對象,可迭代對象和迭代器對象是相互獨立存在的,在迭代的過程當中各個迭代器相互獨立;可是,有的可迭代對象自己又是迭代器對象,那麼迭代器就無法獨立使用。
  • itertools模塊提供了一系列迭代器,可以幫助用戶輕鬆地使用排列、組合、笛卡爾積或其餘組合結構。

 

  • 生成器是一種特殊的迭代器,內部支持了生成器協議,不須要明肯定義__iter__()和next()方法。
  • 生成器經過生成器函數產生,生成器函數能夠經過常規的def語句來定義,可是不用return返回,而是用yield一次返回一個結果。

]

Python裝飾器

 

裝飾模式有不少經典的使用場景,例如插入日誌、性能測試、事務處理等等,有了裝飾器,就能夠提取大量函數中與自己功能無關的相似代碼,從而達到代碼重用的目的。下面就一步步看看Python中的裝飾器。

一個簡單的需求

如今有一個簡單的函數"myfunc",想經過代碼獲得這個函數的大概執行時間。

咱們能夠直接把計時邏輯方法"myfunc"內部,可是這樣的話,若是要給另外一個函數計時,就須要重複計時的邏輯。因此比較好的作法是把計時邏輯放到另外一個函數中("deco"),以下:

可是,上面的作法也有一個問題,就是全部的"myfunc"調用處都要改成"deco(myfunc)"。

下面,作一些改動,來避免計時功能對"myfunc"函數調用代碼的影響:

通過了上面的改動後,一個比較完整的裝飾器(deco)就實現了,裝飾器沒有影響原來的函數,以及函數調用的代碼。例子中值得注意的地方是,Python中一切都是對象,函數也是,因此代碼中改變了"myfunc"對應的函數對象。

裝飾器語法糖

在Python中,可使用"@"語法糖來精簡裝飾器的代碼:

使用了"@"語法糖後,咱們就不須要額外代碼來給"myfunc"從新賦值了,其實"@deco"的本質就是"myfunc = deco(myfunc)",當認清了這一點後,後面看帶參數的裝飾器就簡單了。

被裝飾的函數帶參數

前面的例子中,被裝飾函數的自己是沒有參數的,下面看一個被裝飾函數有參數的例子:

從例子中能夠看到,對於被裝飾函數須要支持參數的狀況,咱們只要使裝飾器的內嵌函數支持一樣的簽名便可。

也就是說這時,"addFunc(3, 8) = deco(addFunc(3, 8))"。

這裏還有一個問題,若是多個函數擁有不一樣的參數形式,怎麼共用一樣的裝飾器?在Python中,函數能夠支持(*args, **kwargs)可變參數,因此裝飾器能夠經過可變參數形式來實現內嵌函數的簽名。

帶參數的裝飾器

裝飾器自己也能夠支持參數,例如說能夠經過裝飾器的參數來禁止計時功能:

經過例子能夠看到,若是裝飾器自己須要支持參數,那麼裝飾器就須要多一層的內嵌函數。

這時候,"addFunc(3, 8) = deco(True)( addFunc(3, 8))","myFunc() = deco(False)( myFunc ())"。

裝飾器調用順序

裝飾器是能夠疊加使用的,那麼這是就涉及到裝飾器調用順序了。對於Python中的"@"語法糖,裝飾器的調用順序與使用 @ 語法糖聲明的順序相反。

在這個例子中,"addFunc(3, 8) = deco_1(deco_2(addFunc(3, 8)))"

Python內置裝飾器

在Python中有三個內置的裝飾器,都是跟class相關的:staticmethod、classmethod 和property。

  • staticmethod 是類靜態方法,其跟成員方法的區別是沒有 self 參數,而且能夠在類不進行實例化的狀況下調用
  • classmethod 與成員方法的區別在於所接收的第一個參數不是 self (類實例的指針),而是cls(當前類的具體類型)
  • property 是屬性的意思,表示能夠經過經過類實例直接訪問的信息

對於staticmethod和classmethod這裏就不介紹了,經過一個例子看看property。

注意,對於Python新式類(new-style class),若是將上面的 "@var.setter" 裝飾器所裝飾的成員函數去掉,則Foo.var 屬性爲只讀屬性,使用 "foo.var = 'var 2'" 進行賦值時會拋出異常。可是,對於Python classic class,所聲明的屬性不是 read-only的,因此即便去掉"@var.setter"裝飾器也不會報錯。

總結

本文介紹了Python裝飾器的一些使用,裝飾器的代碼仍是比較容易理解的。只要經過一些例子進行實際操做一下,就很容易理解了。