IronPython .NET Integration官方文档翻译笔记(待整理,持续更新中...)

---恢复内容开始---以下笔记仅记录阅读过程中我认为有必要记录的内容,大多数都是依赖翻译软件的机翻,配合个人对代码的理解写出的笔记,个别不是很确定的,会在句首标注   猜测:在ironPython 中想使用.Net的API必须先导入 CLR,借用CLR导入.Net的类,最常用的是下面这种导法>>> import clr >>> clr.AddReference("System.Xml")

All .NET assemblies have a unique version number which allows using a specific version of a given assembly. The following code will load the version of System.Xml.dll that ships with .NET 2.0 and .NET 3.5:同样支持输入更详细的程序集信息来导入指定版本的.Net类>>> import clr >>> clr.AddReference("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")

在CLR引入.Net大类之后,就可以使用Python代码正式导入这些类,在IronPython中,会将这些.Net 转化为Python类来对待,但如果使用类示例对比,会发现无论你对比的是.Net类还是Python类,都成立。
可以使用import 直接导入命名空间下所有东西,也可以用from x import x【,x...】的语法选择性的导入个别需要被用到的类>>> import System >>> System #doctest: +ELLIPSIS >>> System.Collections #doctest: +ELLIPSIS The types in the namespaces are exposed as Python types, and are accessed as attributes of the namespace. The following code accesses the System.Environment class from mscorlib.dll:>>> import System >>> System.Environment Just like with normal Python modules, you can also use all the other forms of import as well:>>> from System import Environment >>> Environment >>> from System import * >>> Environment
在ironPython里你可以用这样的语法来表示.net中的泛型>>> from System.Collections.Generic import List, Dictionary >>> int_list = List[int]() >>> str_float_dict = Dictionary[str, float]()

Note that there might exist a non-generic type as well as one or more generic types with the same name [1]. In this case, the name can be used without any indexing to access the non-generic type, and it can be indexed with different number of types to access the generic type with the corresponding number of type parameters. The code below accesses System.EventHandler and also System.EventHandler<TEventArgs>在.Net中很多类型都有泛型和非泛型版本,比如下面这个例子,用泛型和非泛型版本你将访问不同类型的对象,使用python的dir方法可以明显看出检索出来的方法属性列表是不同的>>> from System import EventHandler, EventArgs >>> EventHandler # this is the combo type object >>> # Access the non-generic type >>> dir(EventHandler) #doctest: +ELLIPSIS ['BeginInvoke', 'Clone', 'DynamicInvoke', 'EndInvoke', ... >>> # Access the generic type with 1 type paramter >>> dir(EventHandler[EventArgs]) #doctest: +ELLIPSIS ['BeginInvoke', 'Call', 'Clone', 'Combine', ...


很多时候在IronPython 中并不支持一口气导入整个命名空间里所有东西.NET types are exposed as Python classes. Like Python classes, you usually cannot import all the attributes of .NET types using from <name> import *:>>> from System.Guid import * Traceback (most recent call last): File "", line 1, in ImportError: no module named Guid You can import specific members, both static and instance:你通常只能这样玩,需要什么,导入什么>>> from System.Guid import NewGuid, ToByteArray >>> g = NewGuid() >>> ToByteArray(g) #doctest: +ELLIPSIS Array[Byte](... Note that if you import a static property, you will import the value when the import executes, not a named object to be evaluated on every use as you might mistakenly expect:当你导入个别静态属性时,其实你只是导入了一个值类型,如果你用全名称来对比,你会发现无法对等(这句话意思其实有点难理解,反正看代码就知道大概意思)>>> from System.DateTime import Now >>> Now #doctest: +ELLIPSIS >>> # Let's make it even more obvious that "Now" is evaluated only once >>> a_second_ago = Now >>> import time >>> time.sleep(1) >>> a_second_ago is Now True >>> a_second_ago is System.DateTime.Now False


Some .NET types only have static methods, and are comparable to namespaces. C# refers to them as static classes , and requires such classes to have only static methods. IronPython allows you to import all the static methods of such static classes. System.Environment is an example of a static class:有些.net类只包含静态方法,你可以像这样导入>>> from System.Environment import * >>> Exit is System.Environment.Exit True Nested types are also imported:>>> SpecialFolder is System.Environment.SpecialFolder True However, properties are not imported:然而你会发现只有静态方法被导进来了,静态属性并没有被导进来(这和上一段看起来有点矛盾,难理解。不过写代码的时候自己试一试就知道能不能行得通了)>>> OSVersion Traceback (most recent call last): File "", line 1, in NameError: name 'OSVersion' is not defined >>> System.Environment.OSVersion #doctest: +ELLIPSIS


下面这段大概是说在IronPython里导入.Net类型,如果用类型对比你会发现,他即可当做.Net类也可以当做Python类.NET represents types using System.Type. However, when you access a .NET type in Python code, you get a Python type object [2]:>>> from System.Collections import BitArray >>> ba = BitArray(5) >>> isinstance(type(ba), type) True This allows a unified (Pythonic) view of both Python and .NET types. For example, isinstance works with .NET types as well:>>> from System.Collections import BitArray >>> isinstance(ba, BitArray) True If need to get the System.Type instance for the .NET type, you need to use clr.GetClrType. Conversely, you can use clr.GetPythonType to get a type object corresponding to a System.Type object.The unification also extends to other type system entities like methods. .NET methods are exposed as instances of method:这段很难理解,看不懂>>> type(BitArray.Xor) >>> type(ba.Xor)
下面这条我猜他是想说如果按照实例来对比.Net类可以等于Python类,但按照类型来对比,两者并不对等Note that the Python type corresponding to a .NET type is a sub-type of type:>>> isinstance(type(ba), type) True >>> type(ba) is type False This is an implementation detail.这段说的是从.Net那边导进来的类,你不能随意删除里面的方法和属性.NET types behave like builtin types (like list), and are immutable. i.e. you cannot add or delete descriptors from .NET types:>>> del list.append Traceback (most recent call last): File "", line 1, in AttributeError: cannot delete attribute 'append' of builtin type 'list' >>> >>> import System >>> del System.DateTime.ToByteArray Traceback (most recent call last): File "", line 1, in TypeError: can't set attributes of built-in/extension type 'DateTime'


下面这段是说你可以把.Net类用Python的语法来实例化,也可以调用new方法来实例化,我觉得在python里new看着特别扭,还是入乡随俗吧。.NET types are exposed as Python classes, and you can do many of the same operations on .NET types as with Python classes. In either cases, you create an instance by calling the type:>>> from System.Collections import BitArray >>> ba = BitArray(5) # Creates a bit array of size 5 IronPython also supports inline initializing of the attributes of the instance. Consider the following two lines:>>> ba = BitArray(5) >>> ba.Length = 10 The above two lines are equivalent to this single line:>>> ba = BitArray(5, Length = 10) You can also call the __new__ method to create an instance:>> ba = BitArray.__new__(BitArray, 5)


在python里使用.Net类型其实和在.Net中使用方式相似(可以说一模一样)Invoking .NET instance methods works just like invoking methods on a Python object using the attribute notation:>>> from System.Collections import BitArray >>> ba = BitArray(5) >>> ba.Set(0, True) # call the Set method >>> ba[0] True IronPython also supports named arguments:也支持命名参数>>> ba.Set(index = 1, value = True) >>> ba[1] True IronPython also supports dict arguments:他还支持字典式传参,这招在.Net里是没有的,通过下面这个例子你会发现,你可以在使用过程中逐步收集参数,按照顺序放入集合里,最后再丢进一个方法。>>> args = [2, True] # list of arguments >>> ba.Set(*args) >>> ba[2] True IronPython also supports keyword arguments:也支持使用键值对字典给方法传参(爽)>>> args = { "index" : 3, "value" : True } >>> ba.Set(**args) >>> ba[3] True


在IronPython里如果传入的参数不符合方法需要的参数类型,那么它会结合.Net和Python的综合规则来进行强制转化,比如下面这个例子,他会把string转成数字,数字大于1就等于bool的true,然后None是Python里的null等同于falseWhen the argument type does not exactly match the parameter type expected by the .NET method, IronPython tries to convert the argument. IronPython uses conventional .NET conversion rules like conversion operators , as well as IronPython-specific rules. This snippet shows how arguments are converted when calling theSet(System.Int32, System.Boolean) method:>>> from System.Collections import BitArray >>> ba = BitArray(5) >>> ba.Set(0, "hello") # converts the second argument to True. >>> ba[0] True >>> ba.Set(1, None) # converts the second argument to False. >>> ba[1] False

See appendix-type-conversion-rules for the detailed conversion rules. Note that some Python types are implemented as .NET types and no conversion is required in such cases. See builtin-type-mapping for the mapping.Some of the conversions supported are: Python argument type.NET method parameter type int System.Int8, System.Int16 float System.Float tuple with only elements of type T System.Collections.Generic.IEnumerable<T> function, method System.Delegate and any of its sub-classes
在IronPython中同样支持.net中的函数重载,解释器会自动匹配最适合的那个重载.NET supports overloading methods by both number of arguments and type of arguments. When IronPython code calls an overloaded method, IronPython tries to select one of the overloads at runtime based on the number and type of arguments passed to the method, and also names of any keyword arguments. In most cases, the expected overload gets selected. Selecting an overload is easy when the argument types are an exact match with one of the overload signatures:>>> from System.Collections import BitArray >>> ba = BitArray(5) # calls __new__(System.Int32) >>> ba = BitArray(5, True) # calls __new__(System.Int32, System.Boolean) >>> ba = BitArray(ba) # calls __new__(System.Collections.BitArray) The argument types do not have be an exact match with the method signature. IronPython will try to convert the arguments if an unamibguous conversion exists to one of the overload signatures. The following code calls __new__(System.Int32) even though there are two constructors which take one argument, and neither of them accept a float as an argument:猜测:即使调用一个不存在的重载,IronPython也允许将参数强转后找到最适合匹配的那个重载>>> ba = BitArray(5.0) However, note that IronPython will raise a TypeError if there are conversions to more than one of the overloads:猜测:注意,如果传入的参数需要被进行1次以上的强转才能找到匹配的话,那么将报错>>> BitArray((1, 2, 3)) Traceback (most recent call last): File "", line 1, in TypeError: Multiple targets could match: BitArray(Array[Byte]), BitArray(Array[bool]), BitArray(Array[int]) If you want to control the exact overload that gets called, you can use the Overloads method on method objects:你也可以强制指定接下来的代码必须匹配函数的哪种重载,例如下面代码,最后一次试图只传入1个参数,那么将会报错>>> int_bool_new = BitArray.__new__.Overloads[int, type(True)] >>> ba = int_bool_new(BitArray, 5, True) # calls __new__(System.Int32, System.Boolean) >>> ba = int_bool_new(BitArray, 5, "hello") # converts "hello" to a System.Boolan >>> ba = int_bool_new(BitArray, 5) Traceback (most recent call last): File "", line 1, in TypeError: __new__() takes exactly 2 arguments (1 given) TODO - Example of indexing Overloads with an Array, byref, etc using Type.MakeByrefType 猜测:在IronPython中允许一种特殊的调用方式,不同.Net,在.net中我们只能调用子类中实现了接口或者虚方法的方法,但在IronPython中允许直接调用接口中的方法或者虚类中的虚方法,解释器能自动找到匹配的那个实现,例如下面的代码,因为string继承object所以也有Clone方法,调用ICloneable的虚方法Clone,传入参数string,那么解释器会去string类型里寻找Clone方法It is sometimes desirable to invoke an instance method using the unbound class instance method and passing an explicit self object as the first argument. For example, .NET allows a class to declare an instance method with the same name as a method in a base type, but without overriding the base method. SeeSystem.Reflection.MethodAttributes.NewSlot for more information. In such cases, using the unbound class instance method syntax allows you chose precisely which slot you wish to call:>>> import System >>> System.ICloneable.Clone("hello") # same as : "hello".Clone() 'hello' The unbound class instance method syntax results in a virtual call, and calls the most derived implementation of the virtual method slot:猜测:所以object的GetHashCode和String的GetHashCode是一样的,因为string 继承于object,但是string的GetHashCode方法和RunTimeHelpers类中的GetHashCode不同,猜测,可能是因为具体实现不同,解释器能够通过IL代码分辨出实现是否一致?>>> s = "hello" >>> System.Object.GetHashCode(s) == System.String.GetHashCode(s) True >>> from System.Runtime.CompilerServices import RuntimeHelpers >>> RuntimeHelpers.GetHashCode(s) == System.String.GetHashCode(s) False猜测:下面这段大概是讲.Net里方法的实现分为显式和隐式两种,比如说在.Net里如果显式实现方法,那么我们只能用接口才能调用,如果隐式实现,那么直接在实现类或者接口中都可以调用。但是IronPython中似乎没有这种限制,只要实现了,就都可以调用。最后一节推测大概是说,如果实现类中存在同名的方法(显式实现),那么最好还是用接口来调用,才准确无误。.NET allows a method with a different name to override a base method implementation or interface method slot. This is useful if a type implements two interfaces with methods with the same name. This is known as explicity implemented interface methods. For example, Microsoft.Win32.RegistryKey implementsSystem.IDisposable.Dispose explicitly:>>> from Microsoft.Win32 import RegistryKey >>> clr.GetClrType(RegistryKey).GetMethod("Flush") #doctest: +ELLIPSIS >>> clr.GetClrType(RegistryKey).GetMethod("Dispose") >>> In such cases, IronPython tries to expose the method using its simple name - if there is no ambiguity:>>> from Microsoft.Win32 import Registry >>> rkey = Registry.CurrentUser.OpenSubKey("Software") >>> rkey.Dispose() However, it is possible that the type has another method with the same name. In that case, the explicitly implemented method is not accessible as an attribute. However, it can still be called by using the unbound class instance method syntax:>>> rkey = Registry.CurrentUser.OpenSubKey("Software") >>> System.IDisposable.Dispose(rkey)


在这里Python和.Net调用静态方法的方式是一样的Invoking static .NET methods is similar to invoking Python static methods:>>> System.GC.Collect() Like Python static methods, the .NET static method can be accessed as an attribute of sub-types as well:.Net的静态方法会以Python的规则,方法会被认为是一个类的一个属性,推测:下面这2个类的同名方法如果实现是一样的,那么对比起来结果就是一样的>>> System.Object.ReferenceEquals is System.GC.ReferenceEquals True TODO What happens if the sub-type has a static method with the same name but a different signature? Are both overloads available or not? 展示如何调用泛型方法Generic methods are exposed as attributes which can be indexed with type objects. The following code calls System.Activator.CreateInstance<T>>>> from System import Activator, Guid >>> guid = Activator.CreateInstance[Guid]()


下面这段展示了在IronPython中也支持泛型的自动推断功能,比如最后一段Enumerable.Any[int](list, lambda x : x < 2)
和Enumerable.Any(list, lambda x : x < 2)
解释器会自动匹配适合的泛型,写的时候就可以省略泛型,另外让我惊奇的是这里头居然也支持lambda,真是屌爆了In many cases, the type parameter can be inferred based on the arguments passed to the method call. Consider the following use of a generic method [3]:>>> from System.Collections.Generic import IEnumerable, List >>> list = List[int]([1, 2, 3]) >>> import clr >>> clr.AddReference("System.Core") >>> from System.Linq import Enumerable >>> Enumerable.Any[int](list, lambda x : x < 2) True With generic type parameter inference, the last statement can also be written as:>>> Enumerable.Any(list, lambda x : x < 2) True See appendix for the detailed rules. 下面这段大概是说在IronPython里没有像很多高级语言中有 ref和out的概念,在IronPython中对于这种输出引用有两种玩法,一种隐式的一种显式的The Python language passes all arguments by-value. There is no syntax to indicate that an argument should be passed by-reference like there is in .NET languages like C# and VB.NET via the ref and out keywords. IronPython supports two ways of passing ref or out arguments to a method, an implicit way and an explicit way.下面展示的第一种是隐式的,直接调用方法,比如说原来.Net字典里的TryGetValue方法,原本要传入一个key和一个out引用参数,返回bool结果表示有没找到,但是在IronPython中隐式实现只要直接调用方法,传入key就行了,返回一个元组类型的返回值,里面包含了方法返回值和引用输出返回值。In the implicit way, an argument is passed normally to the method call, and its (potentially) updated value is returned from the method call along with the normal return value (if any). This composes well with the Python feature of multiple return values. System.Collections.Generic.Dictionary has a method bool TryGetValue(K key, out value). It can be called from IronPython with just one argument, and the call returns a tuple where the first element is a boolean and the second element is the value (or the default value of 0.0 if the first element is False):>>> d = { "a":100.1, "b":200.2, "c":300.3 } >>> from System.Collections.Generic import Dictionary >>> d = Dictionary[str, float](d) >>> d.TryGetValue("b") (True, 200.2) >>> d.TryGetValue("z") (False, 0.0) 下面展示的是显式实现,说明里说假如方法存在多种重载的话,这种方式很好用,例如下面的例子,使用CLR的方法生成一个float类型的引用对象,传给方法,然后就和.Net玩法一模一样了,方法还是只返回bool,输出的值将通过引用对象的Value属性来读取,有点像.Net里的可空类型In the explicit way, you can pass an instance of clr.Reference[T] for the ref or out argument, and its Value field will get set by the call. The explicit way is useful if there are multiple overloads with ref parameters:>>> import clr >>> r = clr.Reference[float]() >>> d.TryGetValue("b", r) True >>> r.Value 200.2

Extension methods目前IronPython对于扩展方法的支持还不好,不能像C#那样直接调用,只能通过静态方法来调用,例如,假设string有一个substring方法是后来扩展的,在c#你可以写成"sadsad".substring(...),但是在IronPython你只能写成System.String.SubString("sadsad")Extension methods are currently not natively supported by IronPython. Hence, they cannot be invoked like instance methods. Instead, they have to be invoked like static methods. 下面这段大概是说,在.Net里有索引器的概念,我们可以像下面这样去方便的访问集合中的某个对象,.NET indexers are exposed as __getitem__ and __setitem__. Thus, the Python indexing syntax can be used to index .NET collections (and any type with an indexer):>>> from System.Collections import BitArray >>> ba = BitArray(5) >>> ba[0] False >>> ba[0] = True >>> ba[0] True 但是,也支持使用比较麻烦老土的get和set来访问对象,就像下面这样The indexer can be called using the unbound class instance method syntax using __getitem__ and __setitem__. This is useful if the indexer is virtual and is implemented as an explicitly-implemented interface method:>>> BitArray.__getitem__(ba, 0) True

Non-default .NET indexers这段看不懂Note that a default indexer is just a property (typically called Item) with one argument. It is considered as an indexer if the declaraing type uses DefaultMemberAttribute to declare the property as the default member.See property-with-parameters for information on non-default indexers. 在.Net中一个属性都有一对Get和Set方法,所以在Python中调用.Net属性进行读写,实际上后台操作也是调用属性的get和set方法来进行.NET properties are exposed similar to Python attributes. Under the hood, .NET properties are implemented as a pair of methods to get and set the property, and IronPython calls the appropriate method depending on whether you are reading or writing to the properity:>>> from System.Collections import BitArray >>> ba = BitArray(5) >>> ba.Length # calls "BitArray.get_Length()" 5 >>> ba.Length = 10 # calls "BitArray.set_Length()" 上面的代码相当于下面的代码,效果一样To call the get or set method using the unbound class instance method syntax, IronPython exposes methods called GetValue and SetValue on the property descriptor. The code above is equivalent to the following:>>> ba = BitArray(5) >>> BitArray.Length.GetValue(ba) 5 >>> BitArray.Length.SetValue(ba, 10)


下面这段大概是说在IronPython里索引.Net类型中的集合数组的方式以及访问属性的方式,直接访问属性,调用get/set, 调用 对象.Item[?]的方式访问对象.NET properties are exposed similar to Python attributes. Under the hood, .NET properties are implemented as a pair of methods to get and set the property, and IronPython calls the appropriate method depending on whether you are reading or writing to the properity:>>> from System.Collections import BitArray >>> ba = BitArray(5) >>> ba.Length # calls "BitArray.get_Length()" 5 >>> ba.Length = 10 # calls "BitArray.set_Length()" To call the get or set method using the unbound class instance method syntax, IronPython exposes methods called GetValue and SetValue on the property descriptor. The code above is equivalent to the following:>>> ba = BitArray(5) >>> BitArray.Length.GetValue(ba) 5 >>> BitArray.Length.SetValue(ba, 10)

Properties with parameters

COM and VB.NET support properties with paramters. They are also known as non-default indexers. C# does not support declaring or using properties with parameters.

IronPython does support properties with parameters. For example, the default indexer above can also be accessed using the non-default format as such:

>>> ba.Item[0]
False

.net的事件可以使用+= 和-=的形式进行注册和卸载,在IronPython中同样支持这种玩法,下面的代码里用python 代码按照.Net的格式定义了一个回调函数,居然也能注册到.Net事件里去,真爽

.NET events are exposed as objects with __iadd__ and __isub__ methods which allows using += and -= to subscribe and unsubscribe from the event. The following code shows how to subscribe a Python function to an event using +=, and unsubscribe using -=

>>> from System.IO import FileSystemWatcher
>>> watcher = FileSystemWatcher(".")
>>> def callback(sender, event_args):
...     print event_args.ChangeType, event_args.Name
>>> watcher.Created += callback
>>> watcher.EnableRaisingEvents = True
>>> import time
>>> f = open("test.txt", "w+"); time.sleep(1)
Created test.txt
>>> watcher.Created -= callback
>>>
>>> # cleanup
>>> import os
>>> f.close(); os.remove("test.txt")

You can also subscribe using a bound method:

你也可以这样写,下面代码把回调写在一个类里头而已,感觉并没有什么卵用,看起来更麻烦了

>>> watcher = FileSystemWatcher(".")
>>> class MyClass(object):
...     def callback(self, sender, event_args):
...         print event_args.ChangeType, event_args.Name
>>> o = MyClass()
>>> watcher.Created += o.callback
>>> watcher.EnableRaisingEvents = True
>>> f = open("test.txt", "w+"); time.sleep(1)
Created test.txt
>>> watcher.Created -= o.callback
>>>
>>> # cleanup
>>> f.close(); os.remove("test.txt")

You can also explicitly create a delegate instance to subscribe to the event. Otherwise, IronPython automatically does it for you. [4]:

也可以显示定义一个委托

>>> watcher = FileSystemWatcher(".")
>>> def callback(sender, event_args):
...     print event_args.ChangeType, event_args.Name
>>> from System.IO import FileSystemEventHandler
>>> delegate = FileSystemEventHandler(callback)
>>> watcher.Created += delegate
>>> watcher.EnableRaisingEvents = True
>>> import time
>>> f = open("test.txt", "w+"); time.sleep(1)
Created test.txt
>>> watcher.Created -= delegate
>>>
>>> # cleanup
>>> f.close(); os.remove("test.txt")
他说显示定义委托可以使程序内存占用更少,不知为何。。。难道他指的是装箱和拆箱过程的性能损耗?The only advantage to creating an explicit delegate is that it is uses less memory. You should consider it if you subscribe to lots of events, and notice excessive System.WeakReference objects. 

下面这段主要展示了怎么使用python语法实例化.net数组和索引数组中的值

IronPython supports indexing of System.Array with a type object to access one-dimensional strongly-typed arrays:

>>> System.Array[int]
<type 'Array[int]'>

IronPython also adds a __new__ method that accepts a IList<T> to initialize the array. This allows using a Python list literal to initialize a .NET array:

>>> a = System.Array[int]([1, 2, 3])

Further, IronPython exposes __getitem__ and __setitem__ allowing the array objects to be indexed using the Python indexing syntax:

>>> a[2]
3

假如用GetValue索引一个负数,会报错,你只能像a[-1]这种索引方式才不会报错

Note that the indexing syntax yields Python semantics. If you index with a negative value, it results in indexing from the end of the array, whereas .NET indexing (demonstrated by calling GetValue below) raises a System.IndexOutOfRangeException exception:

>>> a.GetValue(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: Index was outside the bounds of the array.
>>> a[-1]
3

同样也支持python 中的数组分割语法

Similarly, slicing is also supported:

>>> a[1:3]
Array[int]((2, 3))

.NET Exceptions

raise语句支持抛出.net和python中的异常

raise can raise both Python exceptions as well as .NET exceptions:

>>> raise ZeroDivisionError()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError
>>> import System
>>> raise System.DivideByZeroException()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: Attempted to divide by zero.

except关键字也可以catch两种语言中的异常

The except keyword can catch both Python exceptions as well as .NET exceptions:

>>> try:
...    import System
...    raise System.DivideByZeroException()
... except System.DivideByZeroException:
...    print "This line will get printed..."
...
This line will get printed...
>>>


前面有一个例子讲过,如果引用.Net对象,是不允许删除和修改对象中的成员,在下面例子中,也一样,如果是python的异常ZeroDivisionError,那么这是一个python类,允许随意修改,比如在里面加一个foo属性,但是换成System.DivideByZeroException就

不行了,因为这是一个.Net对象,如果试图修改,就会报错

IronPython implements the Python exception mechanism on top of the .NET exception mechanism. This allows Python exception thrown from Python code to be caught by non-Python code, and vice versa. However, Python exception objects need to behave like Python user objects, not builtin types. For example, Python code can set arbitrary attributes on Python exception objects, but not on .NET exception objects:

>>> e = ZeroDivisionError()
>>> e.foo = 1 # this works
>>> e = System.DivideByZeroException()
>>> e.foo = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'DivideByZeroException' object has no attribute 'foo'

下面这段写了一大堆,大概意思就是说如果在IrinPython中,你写的是python代码,那只会catch到python类型的异常,.Net则是catch到.Net类型的异常,但是,通过捕获到的异常对象的clsException对象可以得到两种语言公共的异常类,这是靠CLS(公共语言系统)来维护的。

To support these two different views, IronPython creates a pair of objects, a Python exception object and a .NET exception object, where the Python type and the .NET exception type have a unique one-to-one mapping as defined in the table below. Both objects know about each other. The .NET exception object is the one that actually gets thrown by the IronPython runtime when Python code executes a raise statement. When Python code uses the except keyword to catch the Python exception, the Python exception object is used. However, if the exception is caught by C# (for example) code that called the Python code, then the C# code naturally catches the .NET exception object.

The .NET exception object corresponding to a Python exception object can be accessed by using the clsException attribute (if the module has excecuted import clr):

>>> import clr
>>> try:
...     1/0
... except ZeroDivisionError as e:
...     pass
>>> type(e)
<type 'exceptions.ZeroDivisionError'>
>>> type(e.clsException)
<type 'DivideByZeroException'>

推测:下面这段大概是说通过clsException对象中的Data可以索引到异常对应的Python版的异常

IronPython is also able to access the Python exception object corresponding to a .NET exception object [5], thought this is not exposed to the user [6].

[5]

The Python exception object corresponding to a .NET exception object is accessible (to the IronPython runtime) via the System.Exception.Data property. Note that this is an implementation detail and subject to change:

>>> e.clsException.Data["PythonExceptionInfo"] #doctest: +ELLIPSIS <IronPython.Runtime.Exceptions.PythonExceptions+ExceptionDataWrapper object at ...> 

下面是2种语言的常见异常类型对照表

[6] ... except via the DLR Hosting API ScriptEngine.GetService<ExceptionOperations>().GetExceptionMessage
Python exception.NET exception
   
Exception System.Exception  
SystemExit   IP.O.SystemExit
StopIteration System.InvalidOperationException subtype  
StandardError System.SystemException  
KeyboardInterrupt   IP.O.KeyboardInterruptException
ImportError   IP.O.PythonImportError
EnvironmentError   IP.O.PythonEnvironmentError
IOError System.IO.IOException  
OSError S.R.InteropServices.ExternalException  
WindowsError System.ComponentModel.Win32Exception  
EOFError System.IO.EndOfStreamException  
RuntimeError IP.O.RuntimeException  
NotImplementedError System.NotImplementedException  
NameError   IP.O.NameException
UnboundLocalError   IP.O.UnboundLocalException
AttributeError System.MissingMemberException  
SyntaxError   IP.O.SyntaxErrorException (System.Data has something close)
IndentationError   IP.O.IndentationErrorException
TabError   IP.O.TabErrorException
TypeError   Microsoft.Scripting.ArgumentTypeException
AssertionError   IP.O.AssertionException
LookupError   IP.O.LookupException
IndexError System.IndexOutOfRangeException  
KeyError S.C.G.KeyNotFoundException  
ArithmeticError System.ArithmeticException  
OverflowError System.OverflowException  
ZeroDivisionError System.DivideByZeroException  
FloatingPointError   IP.O.PythonFloatingPointError
ValueError ArgumentException  
UnicodeError   IP.O.UnicodeException
UnicodeEncodeError System.Text.EncoderFallbackException  
UnicodeDecodeError System.Text.DecoderFallbackException  
UnicodeTranslateError   IP.O.UnicodeTranslateException
ReferenceError   IP.O.ReferenceException
SystemError   IP.O.PythonSystemError
MemoryError System.OutOfMemoryException  
Warning System.ComponentModel.WarningException  
UserWarning   IP.O.PythonUserWarning
DeprecationWarning   IP.O.PythonDeprecationWarning
PendingDeprecationWarning   IP.O.PythonPendingDeprecationWarning
SyntaxWarning   IP.O.PythonSyntaxWarning
OverflowWarning   IP.O.PythonOverflowWarning
RuntimeWarning   IP.O.PythonRuntimeWarning
FutureWarning   IP.O.PythonFutureWarning

 

下面又讲了一大堆,大概意思就是演示了一下如何在一段代码中既捕获.Net异常又捕获Python异常,最后证实了两种异常在CLS中,其实.net才是大哥,它才是参照标准。

Given that raise results in the creation of both a Python exception object and a .NET exception object, and given that rescue can catch both Python exceptions and .NET exceptions, a question arises of which of the exception objects will be used by the rescue keyword. The answer is that it is the type used in the rescue clause. i.e. if the rescue clause uses the Python exception, then the Python exception object will be used. If the rescue clause uses the .NET exception, then the .NET exception object will be used.

The following example shows how 1/0 results in the creation of two objects, and how they are linked to each other. The exception is first caught as a .NET exception. The .NET exception is raised again, but is then caught as a Python exception:

>>> import System
>>> try:
...     try:
...         1/0
...     except System.DivideByZeroException as e1:
...         raise e1
... except ZeroDivisionError as e2:
...     pass
>>> type(e1)
<type 'DivideByZeroException'>
>>> type(e2)
<type 'exceptions.ZeroDivisionError'>
>>> e2.clsException is e1
True

下面这段说的是如果Python用户定义了一个Python类继承.Net的Exception对象,然后代码中捕获到了这个异常,然后用.Net异常信息的读取方式去访问异常信息,你将啥也看不到,在下文中异常信息应该是"some message",但是用.net的方式访问,你只会看到

'Python Exception: MyException'

Python user-defined exceptions get mapped to System.Exception. If non-Python code catches a Python user-defined exception, it will be an instance of System.Exception, and will not be able to access the exception details:

>>> # since "Exception" might be System.Exception after "from System import *"
>>> if "Exception" in globals(): del Exception
>>> class MyException(Exception):
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)
>>> try:
...     raise MyException("some message")
... except System.Exception as e:
...     pass
>>> clr.GetClrType(type(e)).FullName
'System.Exception'
>>> e.Message
'Python Exception: MyException'

那么在.Net中调用IronPython脚本时,如何才能得知脚本运行过程中抛出的Python异常呢?可以使用ScriptEngine.GetService<ExceptionOperations>().GetExceptionMessage方法来获取

In this case, the non-Python code can use the ScriptEngine.GetService<ExceptionOperations>().GetExceptionMessage DLR Hosting API to get the exception message.

 

Enumerations

下面是枚举类型的使用示例

.NET enumeration types are sub-types of System.Enum. The enumeration values of an enumeration type are exposed as class attributes:

print System.AttributeTargets.All # access the value "All"

IronPython also supports using the bit-wise operators with the enumeration values:

也支持 位操作符

>>> import System
>>> System.AttributeTargets.Class | System.AttributeTargets.Method
<enum System.AttributeTargets: Class, Method>

Value types

下面这一大段说了一大堆,很复杂,机器翻译困难,看了半天也不知他讲什么,推测:大概是在讲,在IronPython中不要试图修改.Net类型中的值类型,否则会发生很多意想不到的结果

Python expects all mutable values to be represented as a reference type. .NET, on the other hand, introduces the concept of value types which are mostly copied instead of referenced. In particular .NET methods and properties returning a value type will always return a copy.

This can be confusing from a Python programmer’s perspective since a subsequent update to a field of such a value type will occur on the local copy, not within whatever enclosing object originally provided the value type.

While most .NET value types are designed to be immutable, and the .NET design guidelines recommend value tyeps be immutable, this is not enforced by .NET, and so there do exist some .NET valuetype that are mutable. TODO - Example.

For example, take the following C# definitions:

struct Point {
    # Poorly defined struct - structs should be immutable
    public int x;
    public int y;
}

class Line {
    public Point start;
    public Point end;

    public Point Start { get { return start; } }
    public Point End { get { return end; } }
}

If line is an instance of the reference type Line, then a Python programmer may well expect "line.Start.x = 1" to set the x coordinate of the start of that line. In fact the property Start returned a copy of the Point value type and it’s to that copy the update is made:

print line.Start.x    # prints ‘0’
line.Start.x = 1
print line.Start.x    # still prints ‘0’

This behavior is subtle and confusing enough that C# produces a compile-time error if similar code is written (an attempt to modify a field of a value type just returned from a property invocation).

Even worse, when an attempt is made to modify the value type directly via the start field exposed by Line (i.e. “`line.start.x = 1`”), IronPython will still update a local copy of the Point structure. That’s because Python is structured so that “foo.bar” will always produce a useable value: in the case above “line.start” needs to return a full value type which in turn implies a copy.

C#, on the other hand, interprets the entirety of the “`line.start.x = 1`” statement and actually yields a value type reference for the “line.start” part which in turn can be used to set the “x” field in place.

This highlights a difference in semantics between the two languages. In Python “line.start.x = 1” and “foo = line.start; foo.x = 1” are semantically equivalent. In C# that is not necessarily so.

So in summary: a Python programmer making updates to a value type embedded in an object will silently have those updates lost where the same syntax would yield the expected semantics in C#. An update to a value type returned from a .NET property will also appear to succeed will updating a local copy and will not cause an error as it does in the C# world. These two issues could easily become the source of subtle, hard to trace bugs within a large application.

In an effort to prevent the unintended update of local value type copies and at the same time preserve as pythonic and consistent a view of the world as possible, direct updates to value type fields are not allowed by IronPython, and raise a ValueError:

>>> line.start.x = 1 #doctest: +SKIP
Traceback (most recent call last):
   File , line 0, in input##7
ValueError Attempt to update field x on value type Point; value type fields can not be directly modified

This renders value types “mostly” immutable; updates are still possible via instance methods on the value type itself.

 

Proxy types

推测:大概是说在ironPython里不能直接使用C#里的System.MarshalByRefObject实例,但是可以通过非绑定类的实例来调用,这个文档通篇下来经常使用非绑定类这个词,一直看不明白到底指的是什么样的类。

IronPython cannot directly use System.MarshalByRefObject instances. IronPython uses reflection at runtime to determine how to access an object. However, System.MarshalByRefObject instances do not support reflection.

You can use unbound-class-instance-method syntax to call methods on such proxy objects.

 

Delegates

python方法可以被转化为委托,传给事件参数用

Python functions and bound instance methods can be converted to delegates:

>>> from System import EventHandler, EventArgs
>>> def foo(sender, event_args):
...     print event_args
>>> d = EventHandler(foo)
>>> d(None, EventArgs()) #doctest: +ELLIPSIS
<System.EventArgs object at ... [System.EventArgs]>

Variance

ironPython支持委托的参数签名和事件要求的委托参数签名不一致,比如下例,使用了Python语法里的“无限无名参数”

IronPython also allows the signature of the Python function or method to be different (though compatible) with the delegate signature. For example, the Python function can use keyword arguments:

>>> def foo(*args):
...     print args
>>> d = EventHandler(foo)
>>> d(None, EventArgs()) #doctest: +ELLIPSIS
(None, <System.EventArgs object at ... [System.EventArgs]>)

本来事件没有返回值,但是在这里也支持委托可以写返回值(这是Python的语法),但实际运作过程中,返回的值会被忽略

If the return type of the delegate is void, IronPython also allows the Python function to return any type of return value, and just ignores the return value:

>>> def foo(*args):
...     return 100 # this return value will get ignored
>>> d = EventHandler(foo)
>>> d(None, EventArgs())

如果委托实际返回的返回值类型和事件要求的返回值类型不符合,那么解释器会尝试强转后再返回

If the return value is different, IronPython will try to convert it:

>>> def foo(str1, str2):
...     return 100.1 # this return value will get converted to an int
>>> d = System.Comparison[str](foo)
>>> d("hello", "there")
100

TODO - Delegates with out/ref parameters

 

Subclassing .NET types

支持Python类继承或实现.net的类或接口

Sub-classing of .NET types and interfaces is supported using class. .NET types and interfaces can be used as one of the sub-types in the class construct:

>>> class MyClass(System.Attribute, System.ICloneable, System.IComparable):
...     pass

.Net里不支持多重继承,但Python里支持这么干,但是如下代码,你支持继承多个Python类,而不能继承多个.Net类,不管继承多少个,其中只能有一个.Net类

.NET does not support multiple inheritance while Python does. IronPython allows using multiple Python classes as subtypes, and also multiple .NET interfaces, but there can only be one .NET class (other than System.Object) in the set of subtypes:

>>> class MyPythonClass1(object): pass
>>> class MyPythonClass2(object): pass
>>> class MyMixedClass(MyPythonClass1, MyPythonClass2, System.Attribute):
...     pass

和.Net一样,可以利用反射来验证一个类是否是某个类的子类

Instances of the class do actually inherit from the specified .NET base type. This is important because this means that statically-typed .NET code can access the object using the .NET type. The following snippet uses Reflection to show that the object can be cast to the .NET sub-class:

>>> class MyClass(System.ICloneable):
...     pass
>>> o = MyClass()
>>> import clr
>>> clr.GetClrType(System.ICloneable).IsAssignableFrom(o.GetType())
True

下面又说Python并没有真正继承.Net子类,见类型映射表?  看着好玄乎

Note that the Python class does not really inherit from the .NET sub-class. See type-mapping.

 

Overriding methods

基类方法可以被用Python方法重写

Base type methods can be overriden by defining a Python method with the same name:

>>> class MyClass(System.ICloneable):
...    def Clone(self):
...        return MyClass()
>>> o = MyClass()
>>> o.Clone() #doctest: +ELLIPSIS
<MyClass object at ...>

推测:下面意思可能是说,Python语法虽然允许你不真正实现接口方法,编译上通过,但实际运行的时候会报错,说实例中并不存在这个方法,所以我们必须要写出具体实现

IronPython does require you to provide implementations of interface methods in the class declaration. The method lookup is done dynamically when the method is accessed. Here we see that AttributeError is raised if the method is not defined:

>>> class MyClass(System.ICloneable): pass
>>> o = MyClass()
>>> o.Clone()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'Clone'


相关内容推荐