无标题自用临时文档.C# | python交互
IronPython的目标是成为Python语言的完全兼容的实现。同时,与CPython不同的单独实现的价值是使用.NET库生态系统。IronPython通过开源.NET概念作为Python实体。现有的Python语法和新的Python库(比如clr)用来做。IronPython代码可用的. NET特性。
载入 .NET 程序集
import clr
clr.AddReference("System.Xml")
由IronPython加载的程序集的完整列表在 clr.中可用。参考:
"System.Xml" in [assembly.GetName().Name for assembly in clr.References]
True
所有. NET程序集都有一个唯一的版本号,允许使用给定程序集的特定版本。以下代码将随着. NET 2.0和.NET 3.5加载附带的System.Xml.dll版本:
import clr
clr.AddReference("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
默认加载的程序集
使用 .NET 类型
一旦加载了一个程序集,就可以从IronPython代码中访问该程序集包含的命名空间和类型。
导入 .NET 命名空间
.NET命名空间和加载程序集的子命名空间作为Python模块公开:
import System
System #doctest: +ELLIPSIS
<module 'System' (CLS module, ... assemblies loaded)>
System.Collections #doctest: +ELLIPSIS
<module 'Collections' (CLS module, ... assemblies loaded)>
import System
System.Environment
<type 'Environment'>
就像普通的Python模块一样,您也可以使用所有其他形式的导入:
from System import Environment
Environment
<type 'Environment'>
from System import *
Environment
<type 'Environment'>
root namespaces作为模块存储在sys.modules中:
import System
import sys
sys.modules["System"] #doctest: +ELLIPSIS
<module 'System' (CLS module, ... assemblies loaded)>
相对于Python模块的导入优先级
import 给予.py文件优先作为导入对象。例如,如果路径中存在一个名为System.py的文件,它将被导入,而不是系统命名空间:
# 在当前的文件夹中创建 System.py 文件
f = open("System.py", "w")
f.write('print "Loading System.py"')
f.close()
# 卸载系统命名空间(如果已加载)
import sys
if sys.modules.has_key("System"):
... sys.modules.pop("System") #doctest: +ELLIPSIS
<module 'System' (CLS module, ... assemblies loaded)>
import System
Loading System.py
System #doctest: +ELLIPSIS
<module 'System' from '...System.py'>
import os
os.remove("System.py")
sys.modules.pop("System") #doctest: +ELLIPSIS
<module 'System' from '...System.py'>
import System
System #doctest: +ELLIPSIS
<module 'System' (CLS module, ... assemblies loaded)>
访问泛型类型
>>> from System.Collections.Generic import List, Dictionary
>>> int_list = List[int]()
>>> str_float_dict = Dictionary[str, float]()
>>> from System import EventHandler, EventArgs
>>> EventHandler # this is the combo type object
<types 'EventHandler', 'EventHandler[TEventArgs]'>
>>> # 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', ...
[1] |
This refers to the user-friendly name. Under the hoods, the .NET type name includes the number of type parameters:>>> clr.GetClrType(EventHandler[EventArgs]).Name 'EventHandler 1’ `
|
---|---|
Accessing nested types
>>> from System.Environment import SpecialFolder
>>> SpecialFolder
<type 'SpecialFolder'>
Importing .NET members from a type
.NET类型作为Python类公开。像Python类一样,您通常不能导入的所有属性。使用from <name> import *
:
>>> from System.Guid import *
Traceback (most recent call last):File "<stdin>", line 1, in <module>
ImportError: no module named Guid
>>> from System.Guid import NewGuid, ToByteArray
>>> g = NewGuid()
>>> ToByteArray(g) #doctest: +ELLIPSIS
Array[Byte](...
请注意,如果导入静态属性,您将在导入执行时导入该值,而不是像您可能错误预期的那样每次使用时都要评估的命名对象:
>>> from System.DateTime import Now
>>> Now #doctest: +ELLIPSIS
<System.DateTime object at ...>
>>> # 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
Importing all .NET members from a static type
>>> from System.Environment import *
>>> Exit is System.Environment.Exit
True
>>> SpecialFolder is System.Environment.SpecialFolder
True
>>> OSVersion
Traceback (most recent call last):File "<stdin>", line 1, in <module>
NameError: name 'OSVersion' is not defined
System.Environment.OSVersion #doctest: +ELLIPSIS
`[Out]:`
<System.OperatingSystem object at …>
### [Type-system unification (type and System.Type)](https://ironpython.net/documentation/dotnet/dotnet.html#id40).NET represents types using [System.Type](http://msdn.microsoft.com/en-us/library/system.type.aspx). However, when you access a .NET type in Python code, you get a Python type object [[2\]](https://ironpython.net/documentation/dotnet/dotnet.html#id5):```python
>>> from System.Collections import BitArray
>>> ba = BitArray(5)
>>> isinstance(type(ba), type)
[Out]:
True
这允许一个统一的Python和. NET类型 (Pythonic) 视图。例如,isinstance与.NET类型:
>>> from System.Collections import BitArray
>>> isinstance(ba, BitArray)
True
如果需要得到系统。的类型实例。NET类型,需要使用clr.GetClrType。反过来也可以使用clr。获取系统对应的类型对象。类型对象。
统一还扩展到其他类型的系统实体,如方法。.NET方法作为方法的实例公开:
>>> type(BitArray.Xor)
<type 'method_descriptor'>
>>> type(ba.Xor)
<type 'builtin_function_or_method'>
[2] |
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.
|
---|---|
Similarity with builtin types
>>> del list.append
Traceback (most recent call last):File "<stdin>", line 1, in <module>
AttributeError: cannot delete attribute 'append' of builtin type 'list'
>>>
>>> import System
>>> del System.DateTime.ToByteArray
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'DateTime'
Instantiating .NET types
>>> from System.Collections import BitArray
>>> ba = BitArray(5) # Creates a bit array of size 5
>>> 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)
Invoking .NET methods
Invoking .NET instance methods
>>> 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:
>>> 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
Argument conversions
>>> 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
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 |
function, method | System.Delegate and any of its sub-classes |
方法重载
>>> 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)
>>> ba = BitArray(5.0)
但是,请注意,如果存在到多个重载的转换,IronPython将引发类型错误:
>>> BitArray((1, 2, 3))
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: Multiple targets could match: BitArray(Array[Byte]), BitArray(Array[bool]), BitArray(Array[int])
>>> 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 "<stdin>", line 1, in <module>
TypeError: __new__() takes exactly 2 arguments (1 given)
TODO - Example of indexing Overloads with an Array, byref, etc using Type.MakeByrefType
使用未绑定的类实例方法
>>> import System
>>> System.ICloneable.Clone("hello") # same as : "hello".Clone()
'hello'
>>> 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
调用 explicitly-implemented 接口方法
>>> from Microsoft.Win32 import RegistryKey
>>> clr.GetClrType(RegistryKey).GetMethod("Flush") #doctest: +ELLIPSIS
<System.Reflection.RuntimeMethodInfo object at ... [Void Flush()]>
>>> clr.GetClrType(RegistryKey).GetMethod("Dispose")
>>>
在这种情况下,IronPython试图使用它的简单名称来公开这个方法——如果没有歧义的话:
>>> from Microsoft.Win32 import Registry
>>> rkey = Registry.CurrentUser.OpenSubKey("Software")
>>> rkey.Dispose()
>>> rkey = Registry.CurrentUser.OpenSubKey("Software")
>>> System.IDisposable.Dispose(rkey)
Invoking static .NET methods
>>> System.GC.Collect()
像Python静态方法一样.NET静态方法也可以作为子类型的属性来访问:
>>> System.Object.ReferenceEquals is System.GC.ReferenceEquals
True
Invoking generic methods
>>> from System import Activator, Guid
>>> guid = Activator.CreateInstance[Guid]()
Type parameter inference while invoking generic methods
>>> 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
[3] | System.Core.dll is part of .NET 3.0 and higher. |
---|---|
ref and out parameters
>>> d = { "a":100.1, "b":200.2, "c":300.3 }
>>> from System.Collections.Generic import Dictionary
>>> d = Dictionary[str, float](d)
>>> d.TryGetValue("b")
>>> d.TryGetValue("z")
>>> import clr
>>> r = clr.Reference[float]()
>>> d.TryGetValue("b", r)
>>> r.Value
扩展方法
IronPython目前不支持扩展方法。因此,不能像实例方法一样调用它们。相反,它们必须像静态方法一样被调用。
Accessing .NET indexers
.NET indexers暴露为__getitem__
和__setitem__
。因此,Python索引语法可用于索引。NET集合(以及任何带有索引器的类型):
>>> from System.Collections import BitArray
>>> ba = BitArray(5)
>>> ba[0]
>>> ba[0] = True
>>> ba[0]
True
>>> BitArray.__getitem__(ba, 0)
True
Non-default .NET indexers
See property-with-parameters for information on non-default indexers.
Accessing .NET properties
.NET 属性的公开方式类似于Python属性。在hood下, .NET属性被实现为一对获取和设置属性的方法,IronPython根据您是读取还是写入属性来调用适当的方法:
>>> from System.Collections import BitArray
>>> ba = BitArray(5)
>>> ba.Length # calls "BitArray.get_Length()"
False
5
>>> ba.Length = 10 # calls "BitArray.set_Length()"
为了使用未绑定的类实例方法语法调用get或set方法,IronPython在属性描述符上公开了名为GetValue和SetValue的方法。上面的代码相当于以下代码:
>>> ba = BitArray(5)
>>> BitArray.Length.GetValue(ba)
5
>>> BitArray.Length.SetValue(ba, 10)
Properties with parameters
>>> ba.Item[0]
False
Accessing .NET events
>>> 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")
>>> 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")
[4] | 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. |
---|---|
Special .NET types
.NET arrays
System.Array[int]
<type 'Array[int]'>
a = System.Array[int]([1, 2, 3])
a[2]
3
>>> 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
Similarly, slicing is also supported:
>>> a[1:3]
Array[int]((2, 3))
Multi-dimensional arrays
.NET Exceptions
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.
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...
>>>
The underlying .NET exception object
>>> 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'
>>> import clr
>>> try:
... 1/0
... except ZeroDivisionError as e:
... pass
>>> type(e)
<type 'exceptions.ZeroDivisionError'>
>>> type(e.clsException)
<type 'DivideByZeroException'>
[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 ...>
|
---|---|
[6] | … except via the DLR Hosting API ScriptEngine.GetService().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 |
Revisiting the rescue keyword
>>> 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
User-defined exceptions
>>> # 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'
Enumerations
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
For example, take the following C# definitions:
struct Point {# Poorly defined struct - structs should be immutablepublic 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; } }
}
print line.Start.x # prints ‘0’
line.Start.x = 1
print line.Start.x # still prints ‘0’
>>> 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
Proxy types
You can use unbound-class-instance-method syntax to call methods on such proxy objects.
Delegates
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
>>> def foo(*args):
... print args
>>> d = EventHandler(foo)
>>> d(None, EventArgs()) #doctest: +ELLIPSIS
(None, <System.EventArgs object at ... [System.EventArgs]>)
>>> 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
>>> class MyClass(System.Attribute, System.ICloneable, System.IComparable):
... pass
>>> class MyPythonClass1(object): pass
>>> class MyPythonClass2(object): pass
>>> class MyMixedClass(MyPythonClass1, MyPythonClass2, System.Attribute):
... pass
>>> class MyClass(System.ICloneable):
... pass
>>> o = MyClass()
>>> import clr
>>> clr.GetClrType(System.ICloneable).IsAssignableFrom(o.GetType())
True
Note that the Python class does not really inherit from the .NET sub-class. See type-mapping.
Overriding methods
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 ...>
>>> 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'
Methods with multiple overloads
>>> import clr
>>> import System
>>> StringComparer = System.Collections.Generic.IEqualityComparer[str]
>>>
>>> class MyComparer(StringComparer):
... def GetHashCode(self, *args):
... if len(args) == 0:
... # Object.GetHashCode() called
... return 100
...
... if len(args) == 1 and type(args[0]) == str:
... # StringComparer.GetHashCode() called
... return 200
...
... assert("Should never get here")
...
>>> comparer = MyComparer()
>>> getHashCode1 = clr.GetClrType(System.Object).GetMethod("GetHashCode")
>>> args = System.Array[object](["another string"])
>>> getHashCode2 = clr.GetClrType(StringComparer).GetMethod("GetHashCode")
>>>
>>> # Use Reflection to simulate a call to the different overloads
>>> # from another .NET language
>>> getHashCode1.Invoke(comparer, None)
100
>>> getHashCode2.Invoke(comparer, args)
200
Methods with ref or out parameters
>>> import clr
>>> import System
>>> StrFloatDictionary = System.Collections.Generic.IDictionary[str, float]
>>>
>>> class MyDictionary(StrFloatDictionary):
... def TryGetValue(self, key, value):
... if key == "yes":
... value.Value = 100.1 # set the *out* parameter
... return True
... else:
... value.Value = 0.0 # set the *out* parameter
... return False
... # Other methods of IDictionary not overriden for brevity
...
>>> d = MyDictionary()
>>> # Use Reflection to simulate a call from another .NET language
>>> tryGetValue = clr.GetClrType(StrFloatDictionary).GetMethod("TryGetValue")
>>> args = System.Array[object](["yes", 0.0])
>>> tryGetValue.Invoke(d, args)
True
>>> args[1]
100.1
Generic methods
// csc /t:library /out:convert.dll convert.cs
public interface IMyConvertible {T1 Convert<T1, T2>(T2 arg);
}
The following code overrides the generic method Convert:
>>> import clr
>>> clr.AddReference("convert.dll")
>>> import System
>>> import IMyConvertible
>>>
>>> class MyConvertible(IMyConvertible):
... def Convert(self, t2, T1, T2):
... return T1(t2)
>>>
>>> o = MyConvertible()
>>> # Use Reflection to simulate a call from another .NET language
>>> type_params = System.Array[System.Type]([str, float])
>>> convert = clr.GetClrType(IMyConvertible).GetMethod("Convert")
>>> convert_of_str_float = convert.MakeGenericMethod(type_params)
>>> args = System.Array[object]([100.1])
>>> convert_of_str_float.Invoke(o, args)
'100.1'
Calling from Python
>>> result, value = d.TryGetValue("yes")
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: TryGetValue() takes exactly 3 arguments (2 given)
Overriding properties
>>> import clr
>>> import System
>>> StringCollection = System.Collections.Generic.ICollection[str]
>>> prop_info = clr.GetClrType(StringCollection).GetProperty("Count")
>>> prop_info.GetGetMethod().Name
'get_Count'
>>> prop_info.GetSetMethod() # None because this is a read-only property
>>>
>>>
>>> class MyCollection(StringCollection):
... def get_Count(self):
... return 100
... # Other methods of ICollection not overriden for brevity
>>>
>>> c = MyCollection()
>>> # Use Reflection to simulate a call from another .NET language
>>> prop_info.GetGetMethod().Invoke(c, None)
100
Overiding events
>>> from System.ComponentModel import IComponent
>>> import clr
>>> event_info = clr.GetClrType(IComponent).GetEvent("Disposed")
>>> event_info.GetAddMethod().Name
'add_Disposed'
>>> event_info.GetRemoveMethod().Name
'remove_Disposed'
To override events, you need to define methods with the name of the underlying methods:
>>> class MyComponent(IComponent):
... def __init__(self):
... self.dispose_handlers = []
... def Dispose(self):
... for handler in self.dispose_handlers:
... handler(self, EventArgs())
...
... def add_Disposed(self, value):
... self.dispose_handlers.append(value)
... def remove_Disposed(self, value):
... self.dispose_handlers.remove(value)
... # Other methods of IComponent not implemented for brevity
>>>
>>> c = MyComponent()
>>> def callback(sender, event_args):
... print event_args
>>> args = System.Array[object]((System.EventHandler(callback),))
>>> # Use Reflection to simulate a call from another .NET language
>>> event_info.GetAddMethod().Invoke(c, args)
>>>
>>> c.Dispose() #doctest: +ELLIPSIS
<System.EventArgs object at ... [System.EventArgs]>
Calling base constructor
>>> import System
>>> class MyException(System.Exception):
... def __new__(cls, *args):
... # This could be implemented as:
... # return System.Exception.__new__(cls, *args)
... # but is more verbose just to make a point
... if len(args) == 0:
... e = System.Exception.__new__(cls)
... elif len(args) == 1:
... message = args[0]
... e = System.Exception.__new__(cls, message)
... elif len(args) == 2:
... message, inner_exception = args
... if hasattr(inner_exception, "clsException"):
... inner_exception = inner_exception.clsException
... e = System.Exception.__new__(cls, message, inner_exception)
... return e
>>> e = MyException("some message", IOError())
Accessing protected members of base types
>>> import clr
>>> import System
>>> o = System.Object()
>>> o.MemberwiseClone()
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: cannot access protected member MemberwiseClone without a python subclass of object
>>> class MyClass(System.Object):
... pass
>>> o = MyClass()
>>> o.MemberwiseClone() #doctest: +ELLIPSIS
<MyClass object at ...>
Declaring .NET types
Relationship of classes in Python code and normal .NET types
>>> import clr
>>> class MyClass(object):
... pass
>>> o = MyClass()
>>> o.GetType().FullName #doctest: +ELLIPSIS
'IronPython.NewTypes.System.Object_...'
>>> [field.Name for field in o.GetType().GetFields()]
['.class', '.dict', '.slots_and_weakref']
>>> o.GetType().GetField(".class").GetValue(o) == MyClass
True
>>> class MyClass2(MyClass):
... pass
>>> o2 = MyClass2()
>>> o.GetType() == o2.GetType()
True
Also see Type-system unification (type and System.Type)
[7] | These field names are implementation details, and could change. |
---|---|
clrtype
Accessing Python code from other .NET code
Using the DLR Hosting APIs
Compiling Python code into an assembly
dynamic
Integration of Python and .NET features
Type system integration.
- See “Type-system unification (type and System.Type)”
- Also see extensions-to-python-types and extensions-to-dotnet-types
List comprehension works with any .NET type that implements IList
with works with with any System.Collections.IEnumerable or System.Collections.Generic.IEnumerable
pickle and ISerializable
doc on .NET types and members:
doc uses XML comments if available. XML comment files are installed if TODO. As a result, help can be used:
>>> help(System.Collections.BitArray.Set) #doctest: +NORMALIZE_WHITESPACE Help on method_descriptor: Set(...)Set(self, int index, bool value)Sets the bit at a specificposition in the System.Collections.BitArray tothe specified value. <BLANKLINE>index:The zero-based index of thebit to set. <BLANKLINE>value:The Boolean value to assignto the bit.
If XML comment files are not available, IronPython generates documentation by reflecting on the type or member:
>>> help(System.Collections.Generic.List.Enumerator.Current) #doctest: +NORMALIZE_WHITESPACE Help on getset descriptor System.Collections.Generic in mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.Enumerator.Current: <BLANKLINE> CurrentGet: T Current(self)
Extensions to Python types
import clr exposes extra functionality on some Python types to make .NET features accessible:
method
objects of any builtin or .NET types:
- instance method
- Overloads(t1 [, t2…])
- instance method
type
objects
- instance method
- getitem(t1 [, t2…]) - creates a generic instantiation
- instance method
Extensions to .NET types
Types with op_Implicit
- TODO
Types with op_Explicit
- TODO
Types inheriting from a .NET class or interface
.NET base-type Synthesized Python method(s) System.Object all methods of object eg. class, str, hash, setattr System.IDisposable enter, exit System.Collections.IEnumerator next System.Collections.ICollection System.Collections.Generic.ICollection len System.Collections.IEnumerable System.Collections.Generic.IEnumerable System.Collections.IEnumerator System.Collections.Generic.IEnumerator iter System.IFormattable format System.Collections.IDictionary System.Collections.Generic.IDictionary<TKey, TValue> System.Collections.Generic.ICollection System.Collections.Generic.IList System.Collections.IEnumerable System.Collections.Generic.IEnumerable System.Collections.IEnumerator System.Collections.Generic.IEnumerator contains System.Array Class methods:Indexing of the type object with a type object to access a specific array type__new__(l) where l is IList (or supports getitem?)getitem, setitem, slice System.Delegate Class method : new(type, function_or_bound_method)call System.Enum or TODO ? Types with a .NET operator method name
.NET operator method Synthesized Python method op_Addition, Add add Compare cmp get_ [8] getitem set_ [9] setitem
[8] | where the type also has a property , and a DefaultMemberAttribute for |
---|---|
[9] | where the type also has a property , and a DefaultMemberAttribute for |
---|---|
Equality and hashing
TODO - This is currently just copied from IronRuby, and is known to be incorrect
When Python code calls eq and hash
- If the object is a Python object, the default implementations of eq and hash get called. The default implementations call System.Object.ReferenceEquals and System.Runtime.CompileServices.RuntimeHelpers.GetHashCode respectively.
- If the object is a CLR object, System.Object.Equals and System.Object.GetHashCode respectively get called on the .NET object.
- If the object is a Python subclass object inheriting from a CLR class, the CLR’s class’s implementation of System.Object.Equals and System.Object.GetHashCode will get called if the Python subclass does not define eq and hash. If the Python subclass defines eq and hash, those will be called instead.
When static MSIL code calls System.Object.Equals and System.Object.GetHashCode
- If the object is a Python objects, the Python object will direct the call to eq and hash. If the Python object has implementations for these methods, they will be called. Otherwise, the default implementation mentioned above gets called.
- If the object is a Python subclass object inheriting from a CLR class, the CLR’s class’s implementation of System.Object.Equals and System.Object.GetHashCode will get called if the Python subclass does not define eq and hash. If the Python subclass defines eq and hash, those will be called instead.
Hashing of mutable objects
System.Object.ToString, repr and str
ToString on Python objects
>>> class MyClass(object):
... def __str__(self):
... return "__str__ result"
>>> o = MyClass()
>>> # Use Reflection to simulate a call from another .NET language
>>> o.GetType().GetMethod("ToString").Invoke(o, None) #doctest: +ELLIPSIS
'IronPython.NewTypes.System.Object_...'
repr/str on .NET objects
All Python user types have repr and str:
>>> class MyClass(object):
... pass
>>> o = MyClass()
>>> o.__repr__() #doctest: +ELLIPSIS
'<MyClass object at ...>'
>>> o.__str__() #doctest: +ELLIPSIS
'IronPython.NewTypes.System.Object_...'
>>> str(o) #doctest: +ELLIPSIS
'<MyClass object at ...>'
>>> from System.Collections import BitArray
>>> ba = BitArray(5)
>>> ba.ToString() # BitArray inherts System.Object.ToString()
'System.Collections.BitArray'
>>> ba.__repr__() #doctest: +ELLIPSIS
'<System.Collections.BitArray object at ... [System.Collections.BitArray]>'
>>> ba.__str__() #doctest: +ELLIPSIS
'<System.Collections.BitArray object at ... [System.Collections.BitArray]>'
>>> e = System.Exception()
>>> e.ToString()
"System.Exception: Exception of type 'System.Exception' was thrown."
>>> e.__repr__() #doctest: +ELLIPSIS
"<System.Exception object at ... [System.Exception: Exception of type 'System.Exception' was thrown.]>"
>>> e.__str__() #doctest:
"System.Exception: Exception of type 'System.Exception' was thrown."
For Python types that override ToString, str is mapped to the ToString override:
>>> class MyClass(object):
... def ToString(self):
... return "ToString implemented in Python"
>>> o = MyClass()
>>> o.__repr__() #doctest: +ELLIPSIS
'<MyClass object at ...>'
>>> o.__str__()
'ToString implemented in Python'
>>> str(o) #doctest: +ELLIPSIS
'<MyClass object at ...>'
[10] | There is some inconsistency in handling of str that is tracked by http://ironpython.codeplex.com/WorkItem/View.aspx?WorkItemId=24973 |
---|---|
OleAutomation and COM interop
IronPython supports accessing OleAutomation objects (COM objects which support dispinterfaces).
Creating a COM object
The first approach is to use System.Type.GetTypeFromProgID and System.Activator.CreateInstance . This method works with any registered COM object:
>>> import System >>> t = System.Type.GetTypeFromProgID("Excel.Application") >>> excel = System.Activator.CreateInstance(t) >>> wb = excel.Workbooks.Add() >>> excel.Quit()
The second approach is to use clr.AddReferenceToTypeLibrary to load the type library (if it is available) of the COM object. The advantage is that you can use the type library to access other named values like constants:
>>> import System >>> excelTypeLibGuid = System.Guid("00020813-0000-0000-C000-000000000046") >>> import clr >>> clr.AddReferenceToTypeLibrary(excelTypeLibGuid) >>> from Excel import Application >>> excel = Application() >>> wb = excel.Workbooks.Add() >>> excel.Quit()
Finally, you can also use the interop assembly. This can be generated using the tlbimp.exe tool. The only advantage of this approach was that this was the approach recommeded for IronPython 1. If you have code using this approach that you developed for IronPython 1, it will continue to work:
>>> import clr >>> clr.AddReference("Microsoft.Office.Interop.Excel") >>> from Microsoft.Office.Interop.Excel import ApplicationClass >>> excel = ApplicationClass() >>> wb = excel.Workbooks.Add() >>> excel.Quit()
Using COM objects
Properties
Methods with out parameters
>>> import clr
>>> from System import Type, Activator
>>> command_type = Type.GetTypeFromProgID("ADODB.Command")
>>> command = Activator.CreateInstance(command_type)
>>> records_affected = clr.Reference[int]()
>>> command.Execute(records_affected, None, None) #doctest: +SKIP
>>> records_affected.Value
0
[11] | Note that the Office APIs in particular do have “VARIANT*” parameters, but these methods do not update the value of the VARIANT. The only reason they were defined with “VARIANT*” parameters was for performance since passing a pointer to a VARIANT is faster than pushing all the 4 DWORDs of the VARIANT onto the stack. So you can just treat such parameters as “in” parameters. |
---|---|
Accessing the type library
Non-automation COM objects
[12] | This was supported in IronPython 1, but the support was dropped in version 2. |
---|---|
Miscellaneous
Security model
Execution model and call frames
IronPython code can be executed by any of the following techniques:
- Interpretation
- Compiling on the fly using DynamicMethod
- Compiling on the fly using DynamicMethod
- Ahead-of-time compilation to an assembly on disk using ipyc
- A combination of the above - ie. a method might initially be interpreted, and can later be compiled once it has been called a number of times.
Accessing non-public members
Mapping between Python builtin types and .NET types
Python type | .NET type |
---|---|
object | System.Object |
int | System.Int32 |
long | System.Numeric.BigInteger [13] |
float | System.Double |
str, unicode | System.String |
bool | System.Boolean |
[13] | This is true only in CLR 4. In previous versions of the CLR, long is implemented by IronPython itself. |
---|---|
import clr and builtin types
>>> hasattr(object, "__hash__")
True
>>> # Note that this assumes that "import clr" has not yet been executed
>>> hasattr(object, "GetHashCode") #doctest: +SKIP
False
However, once you do import clr, object has both hash as well as GetHashCode:
>>> import clr
>>> hasattr(object, "__hash__")
True
>>> hasattr(object, "GetHashCode")
True
LINQ
LINQ-to-objects
Python’s list comprehension provides similar functionality, and is more Pythonic. Hence, it is recommended to use list comprehension itself.
DLinq - This is currently not supported.
Feature by feature comparison
- C# and VB.NET lambda function - Python supports lambda functions already.
- Anonymous types - Python has tuples which can be used like anonymous types.
- Extension methods - See
- Generic method type parameter inference - See
- Expression trees - This is not supported. This is the main reason DLinq does not work.
Appendix - Type conversion rules
Python argument type | .NET method parameter type |
---|---|
int | System.Byte, System.SByte, System.UInt16, System.Int16 |
User object with int method | Same as int |
str or unicode of size 1 | System.Char |
User object with str method | Same as str |
float | System.Float |
tuple with T-typed elements | System.Collections.Generic.IEnumerable or System.Collections.Generic.IList |
function, method | System.Delegate and any of its sub-classes |
dict with K-typed keys and V-typed values | System.Collections.Generic.IDictionary<K,V> |
type | System.Type |
Appendix - Detailed method overload resolution rules
Roughly equivalent to VB 11.8.1 with additional level of preferred narrowing conversions
- Start with the set of all accessible members
- Keep only those members for which the argument types can be assigned to the parameter types by a widening conversion
- If there is one or more member in the set find the best member
- If there is one best member then call it
- If there are multiple best members then throw ambiguous
- If there is one or more member in the set find the best member
- Add in those members for which the argument types can be assigned to the parameter types by either a preferred narrowing or a widening conversion
- If there is one applicable member then call it
- If there is more than one applicable member then throw ambiguous
- Add in those members for which the argument types can be assigned to the parameter types by any narrowing or a widening conversion
- If there is one applicable member then call it
- If there is more than one applicable member then throw ambiguous
- Otherwise throw no match
Applicable Members By Number of Arguments – Phase 1
- The number of arguments is identical to the number of parameters
- The number of arguments is less than the number of parameters, but all parameters without an argument are optional – have a non-DbNull default value.
- The method includes a parameter array and the params-expanded form of the method is applicable to the arguments
- The params-expanded form is constructed by replacing the parameter array in the declaration with zero or more value parameters of the element type of the parameter array such that the number of arguments matches the number of parameters in the expanded form
- The method includes byref parameters and the byref-reduced form of the method is applicable to the arguments
- The byref-reduced form is constructed by removing all out parameters from the list and replacing all ref parameters with their target type. The return information for such a match will be provided in a tuple of return values.
Applicable Members By Type Of Arguments – Phase 2
- If a conversion of the given type exists from the argument object to the type of the parameter for every argument then the method is applicable
- For ref or out parameters, the argument must be an instance of the appropriate Reference class – unless the byref-reduced form of the method is being used
Better Member (same as C# 7.4.2.2)
Parameter Types : Given an argument list A with a set of types {A1, A1, …, An} and type applicable parameter lists P and Q with types {P1, P2, …, Pn} and {Q1, Q2, …, Qn} P is a better member than Q if
- For each argument, the conversion from Ax to Px is not worse than the conversion from Ax to Qx, and
- For at least one argument, the conversion from Ax to Px is better than the conversion from Ax to Qx
Parameter Modifications : The method that uses the minimal conversions from the original method is considered the better match. The better member is the one that matches the earliest rule in the list of conversions for applicable methods. If both members use the same rules, then the method that converts the fewest of its parameters is considered best. For example, if multiple params methods have identical expanded forms, then the method with the most parameters prior to params-expanded form will be selected
Static vs. instance methods : When comparing a static method and an instance method that are both applicable, then the method that matches the calling convention is considered better. If the method is called unbound on the type object then the static method is preferred; however, if the method is called bound to an instance than the instance method will be preferred.
Explicitly implemented interface methods: Methods implemented as public methods on a class are considered better than methods that are private on the declaring class which explicitly implement an interface method.
Generic methods: Non-generic methods are considered better than generic methods.
Better Conversion (same as C# 7.4.2.3)
- If T1 == T2 then neither conversion is better
- If S is T1 then C1 is the better conversion (and vice-versa)
- If a conversion from T1 to T2 exists, and no conversion from T2 to T1 exists, then C1 is the better conversion (and vice versa)
- Conversion to a signed numeric type is preferred over conversion to a non-signed type of equal or greater size (this means that sbyte is preferred over byte)
Special conversion rule for ExtensibleFoo: An ExtensibleFoo has a conversion to a type whenever there is an appropriate conversion from Foo to that type.
Implicit Conversions
- Implicit numeric conversions (C# 6.1.2)
- Implicit reference conversions (C# 6.1.4) == Type.IsAssignableFrom
- null -> Nullable
- COM object to any interface type
- User-defined implicit conversions (C# 6.1.7)
- Conversion from DynamicType -> Type
Narrowing Conversions (see VB 8.9 but much more restrictive for Python) are conversions that cannot be proved to always succeed, conversions that are known to possibly lose information, and conversions across domains of types sufficiently different to merit narrowing notation. The following conversions are classified as narrowing conversions:
Preferred Narrowing Conversions
- BigInteger -> Int64 – because this is how Python represents numbers larger than 32 bits
- IList -> IList
- IEnumerator -> IEnumerator
- IDictionary<object,object> -> IDictionary<K,V>
Narrowing Conversions
- Bool -> int
- Narrowing conversions of numeric types when overflow doesn’t occur
- String(length == 1) -> char and Char -> string(length == 1)
- Generic Python protocols to CLS types
- Callable (or anything?) -> Delegate
- Object (iterable?) -> IEnumerator?
- int to int, float, complex
- Troubling conversions planning to keep
- Object -> bool (nonzero)
- Double -> int – this is standard Python behavior, albeit deprecated behavior
- Tuple -> Array
All of the below will require explicit conversions
- Enum to numeric type – require explicit conversions instead
- From numeric types to char (excluded by C#)
- Dict -> Hashtable
- List -> Array, List and ArrayList
- Tuple -> List and ArrayList
Rules for going the other direction when C# methods are overridden by Python or delegates are implemented on the Python side:
- This change alters our rules for how params and by ref parameters are handled for both overridden methods and delegates.
- by ref (ref or out) parameters are always passed to Python as an instance of clr.Reference. The Value property on these can be used to get and set the underlying value and on return from the method this will be propogated back to the caller.
- params parameters are ignored in these cases and the underlying array is passed to the Python function instead of splitting out all of the args.
- The principle behind this change is to present the most direct reflection of the CLS signature to the Python programmer when they are doing something where the signature could be ambiguous. For calling methods with by ref parameters we support both explicit Reference objects and the implicit skipped parameters. When overriding we want to support the most direct signature to remove ambiguity. Similarly for params methods we support both calling the method with an explicit array of args or with n-args. To remove the ambiguity when overriding we only support the explicit array.
- I’m quite happy with this principle in general. The one part that sucks for me is that these methods are now not callable from Python in the non-explict forms any more. For example, if I have a method void Foo(params object[] args) then I will override it with a Python method Foo(args) and not Foo(*args). This means that the CLS base type’s method can be called as o.Foo(1,2,3) but the Python subclass will have to be called as o.Foo( (1,2,3) ). This is somewhat ugly, but I can’t come up with any other relatively simple and clear option here and I think that because overriding overloaded methods can get quite complicated we should err on the side of simplicity.
无标题自用临时文档.C# | python交互相关推荐
- 文档化Python代码完全指南(翻译)
阅读代码比编写代码更多,有良好丰富文档的项目会吸引更多人使用和参与开发贡献.本教程旨在详细阐述如何将 Python 代码实现"文档化",介绍了注释用法.类型提示.文档字符串.在项目 ...
- html500错误原因1003无标题,web工程中404/500错误页面配置+404页面模板
[实例简介] web工程中404/500错误页面配置+404页面模板 [实例截图] [核心代码] 247959a9-c3ea-4360-8e57-105d680b29f0 ├── 404页面模板 │ ...
- Linux微信1001无标题,微信个性签名1001无标题
摘要: 本篇微信个性签名1001无标题,是喜欢签名的网友提供的微信朋友圈签名中的微信个性签名1001无标题,可能下文中的微信个性签名1001无标题有你心爱的微信个性签名. 微信个性签名1001无标题, ...
- 关于WM_NCHITTEST消息(移动无标题对话框多个)
我为了移动一个无标题栏的窗体,使用了WM_NCHITTEST消息,这个消息大概如下: 通常,我们拖动对话框窗口的标题栏来移动窗口,但有时候,我们想通过鼠标在客户区上拖动来移动窗口. 一个容易想到的方案 ...
- mysql输入命令1002无标题_Linux下远程连接MySQL数据库的方法
步骤 1.在服务器端开启远程访问 首先进入mysql数据库,然后输入下面两个命令: grant all privileges on *.* to 'root'@'%' identified by 'p ...
- C# 系统应用之无标题窗体移动的两种方法
在做项目界面设计中,常常为了美观需要设置窗体属性"FormBorderStyle"(窗体边框和标题栏外观)为None无标题窗口.此时隐藏标题的窗口怎样实现移动呢?我根据自己的项目从 ...
- Android 置Activity全屏和无标题
今天,实在没有什么好些写的内容,所以在网上找了很久,才决定写这个博客.比较简单,还是想保持写博客的习惯. 一.在代码里设置全屏. Activity设置全屏和无标题栏,要用到andorid.view.W ...
- ActionBarActivity设置全屏无标题
新建的Activity继承自ActionBarActivity,设置全屏无标题本来非常easy的事,可是没想到app居然无缘无故的挂,要么就是白屏一片,要么就是黑屏.坑了我一个多小时.!! 原因是Ac ...
- Win7打印时文档被挂起的解决方法
在很多时候我们都会需要打印文档,但是打印过程中会有一些问题出现,如打印文档里无法打印,右下角显示文档被挂起,那么Win7打印时文档被挂起怎么解决呢,下面小编和大家分享下具体Win7打印时文档被挂起的解 ...
最新文章
- 通信电子线路期末复习第一章和第二章上
- Intel Realsense D435 python wrapper pyrealsense.pipeline类
- php中MySQL数据库导入与导出_Mysql数据库导出和导入
- WPF入门教程系列四——Dispatcher介绍
- 结构体内元素不确定_氮长叶,磷长果,那么生物菌肥能长啥?看完终于不纠结了...
- python中beautifulsoup_面向新手解析python Beautiful Soup基本用法
- Jmeter上传文件
- Flutter基础—绘画效果之不透明度
- __init__.py的作用是什么?
- java实现简单的文字pk的小游戏
- Java线程同步和锁定
- cmd 打开资源监视器
- html怎么设置火狐ie兼容模式,火狐浏览器兼容模式如何设置?火狐浏览器兼容模式设置方法分享...
- python入门教程百度云资源-python教程大全,全套视频教程学习资料通过百度云网盘下载...
- 开学至此时总结。(月末总结好像一直没写)
- 开发那点事(六)php抓取北京实时公交数据
- Android CPU 深度睡眠,处理器的深度和深度睡眠状态之间的差异
- 【vue3】ref获取v-for循环渲染的元素
- 【UCIe】UCIe DLP/DLLP 介绍
- 【深度学习之美02】深度学习的方法论是什么?