You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Update notes on upgrading from ipy2 to cover PEP 237 (#1423)
* Update notes on upgrading from ipy2 to cover PEP 237
* Update Documentation/upgrading-from-ipy2.md
Co-authored-by: slozier <slozier@users.noreply.github.com>
* Update after review
Co-authored-by: slozier <slozier@users.noreply.github.com>
Copy file name to clipboardExpand all lines: Documentation/upgrading-from-ipy2.md
+176Lines changed: 176 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,3 +14,179 @@ In an effort to improve compatibility, `sys.platform` no longer returns `cli`. I
14
14
if sys.implementation.name =="ironpython":
15
15
print("IronPython!")
16
16
```
17
+
18
+
## `int` Type
19
+
20
+
One of the major backward incompatible changes in Python 3 is [PEP 237 – Unifying Long Integers and Integers][PEP 0237]: Essentially, `long` renamed to `int`. That is, there is only one built-in integral type, named `int`; but it behaves mostly like the old `long` type. From the pure Python perspective this means that `int` should be used wherever previously `long` was used. More consideration has to be applied in interop cases with .NET.
21
+
22
+
The Python `int` type in IronPython 3 is implemented as `System.Numerics.BigInteger` (and not as `System.Int32` as it was in IronPython 2). It can contain in theory an arbitrarily large integer (only limited by the 2 GByte memory boundary).
23
+
24
+
```pycon
25
+
>>> import clr
26
+
>>> clr.AddReference("System.Numerics")
27
+
>>> import System
28
+
>>> intis System.Numerics.BigInteger
29
+
True
30
+
>>> intis System.Int32
31
+
False
32
+
>>> clr.GetClrType(int).Name
33
+
'BigInteger'
34
+
```
35
+
36
+
This means that in interop cases, when the `int` type is used (think generics), it will mean `BigInteger` and not `Int32` (which was the case in IronPython 2). To retain IronPython 2 semantics, replace `int` with `System.Int32`.
As for instances of `int`, mostly for performance reasons, IronPython may use instances of `System.Int32` to hold smaller integers, while `BigInteger` instances are used for large integers. This is done transparently from the Python side, but again the distinction may become relevant for interop cases. Examples:
60
+
61
+
```python
62
+
i =1# instance of Int32
63
+
j =1<<31# instance of BigInteger
64
+
k = j -1# still BigInteger, as one of the arguments makes the result type BigInteger
65
+
```
66
+
67
+
This means that the type of `Int32` objects is always reported as `int` (which is the same as `BigInteger`). If it is important to check what is the actual type of a given integer object, test if the object is an instance of `System.Int32`. (An alternative way is a test for the presence of `MaxValue` or `MinValue`. For those properties to be visible, `System` has to be imported first.)
68
+
69
+
```pycon
70
+
>>> import System
71
+
>>> type(i)
72
+
<class 'int'>
73
+
>>> isinstance(i, System.Int32)
74
+
True
75
+
>>> type(j)
76
+
<class 'int'>
77
+
>>> isinstance(j, System.Int32)
78
+
False
79
+
>>> hex(i.MaxValue)
80
+
'0x7fffffff'
81
+
```
82
+
83
+
The creation of either `Int32` or `BigInteger` instances happens automatically by the `int` constructor. If for interop purposes it is important to create a `BigInteger` (despite the value fitting in 32 bits), use method `ToBigInteger`. It converts `Int32` values to `BigInteger` and leaves `BigInteger` values unaffected.
84
+
85
+
```pycon
86
+
>>> bi = i.ToBigInteger()
87
+
>>> isinstance(j, System.Int32)
88
+
False
89
+
```
90
+
91
+
In the opposite direction, if it is essential to create `Int32` objects, either use constructors for `int` or `Int32`. In the current implementation, the former converts an integer to `Int32` if the value fits in 32 bits, otherwise it leaves it as `BigInteger`. The latter throws an exception is the conversion is not possible. Although the behavior of the constructor `int` may or may not change in the future, it is always guaranteed to convert the value to the "canonical form" adopted for that version of IronPython.
92
+
93
+
```pycon
94
+
>>> # k is a BigInteger that fits in 32 bits
95
+
>>> isinstance(j, System.Int32)
96
+
False
97
+
>>> hex(k)
98
+
'0x7fffffff'
99
+
>>> ki =int(k) # converts k to Int32
100
+
>>> isinstance(ki, System.Int32)
101
+
True
102
+
>>> ki = System.Int32(k) # also converts k to Int32
103
+
>>> isinstance(ki, System.Int32)
104
+
True
105
+
>>> # j is a BigInteger that does not fit in 32 bits
106
+
>>> isinstance(j, System.Int32)
107
+
False
108
+
>>> hex(j)
109
+
'0x80000000'
110
+
>>> j =int(j) # no type change, j stays BigInteger
111
+
>>> isinstance(j, System.Int32)
112
+
False
113
+
>>> j = System.Int32(j) # conversion fails
114
+
Traceback (most recent call last):
115
+
File "<stdin>", line 1, in <module>
116
+
OverflowError: Arithmetic operation resulted in an overflow.
117
+
```
118
+
119
+
Such explicit conversions are in most cases unnecessary since the runtime recognizes `int`/`Int32` equivalence of instances and performs necessary conversions automatically.
>>> int32_list.Add((1).ToBigInteger()) # BigInteger instance converted to Int32
127
+
>>> int_list[0] == int32_list[0]
128
+
True
129
+
```
130
+
131
+
### Pickling and unpickling of `int`
132
+
133
+
When an `int` object is serialized using `pickle.dump(x, myfile)` and subsequently unpickled with `x = pickle.load(myfile)` (or `pickle.loads(pickle.dumps(x))`, this has the same effect as reconstructing the object using the `int` constructor, i.e. `x = int(x)`. In other words, if the `x` instance was `BigInteger` but the value fits in `Int32`, it will be reconstructed as `Int32`.
134
+
135
+
### BigIntegerV2 API
136
+
137
+
In IronPython 2, `long` type carries an obsolete `BigIntegerV2` API, accessible after importing `System`. In IronPython 3 this API is not available directly on `int` instances (regardless whether the instance is `Int32` or `BigInteger`), but is still accessible in some form through `Microsoft.Scripting.Utils.MathUtils` in `Microsoft.Dynamic.dll`.
138
+
139
+
```pycon
140
+
>>> # IronPython 2
141
+
>>> i =1# instance of Int32 (int)
142
+
>>> j =1<<64# instance of BigInteger (long)
143
+
>>> import System
144
+
>>> j.GetWords()
145
+
Array[UInt32]((0, 0, 1))
146
+
>>> i.GetWords()
147
+
Traceback (most recent call last):
148
+
File "<stdin>", line 1, in <module>
149
+
AttributeError: 'int' object has no attribute 'GetWords'
Another set of Python-hidden methods on `long` in IronPython 2 that are not available on `int` in IronPython 3 are conversion methods with names like `ToXxx`. The recommended way to perform type conversions like those is to use type constructors. The exception is the conversion to `BigInteger` itself, for the reasons explained above.
173
+
174
+
```python
175
+
# IronPython 2
176
+
j =long(1)
177
+
i64 = j.ToInt64()
178
+
```
179
+
180
+
```python
181
+
# IronPython 3
182
+
import System
183
+
j = (1).ToBigInteger()
184
+
i64 = System.Int64(j)
185
+
```
186
+
187
+
### `range`
188
+
189
+
IronPython's `range` is a generator that produces a sequence of `int` values. The values are instances of `Int32` or `BigInteger`, depending on the actual integer value they represent. When `range` is used in a LINQ context, it exposes interface `IEnumerable<Int32>` and all values generated are of type `Int32`. This limits the possible value to the range `Int32.MinValue` to `Int32.MaxValue`.
0 commit comments