Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9f01ebb

Browse files
Operator overloads support (#1324)
C# operator methods generate instance methods on the Python side like `__add__`. The arguments passed from Python are then processed by `MethodBinder`. Co-authored-by: Victor <[email protected]>
1 parent 96cc739 commit 9f01ebb

10 files changed

+612
-9
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- Benoît Hudson ([@benoithudson](https://github.com/benoithudson))
2525
- Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich))
2626
- Callum Noble ([@callumnoble](https://github.com/callumnoble))
27+
- Christabella Irwanto([@christabella](https://github.com/christabella))
2728
- Christian Heimes ([@tiran](https://github.com/tiran))
2829
- Christoph Gohlke ([@cgohlke](https://github.com/cgohlke))
2930
- Christopher Bremner ([@chrisjbremner](https://github.com/chrisjbremner))

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1010
### Added
1111

1212
- Ability to instantiate new .NET arrays using `Array[T](dim1, dim2, ...)` syntax
13+
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
1314

1415
### Changed
1516
- Drop support for Python 2, 3.4, and 3.5

src/embed_tests/TestOperator.cs

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
using NUnit.Framework;
2+
3+
using Python.Runtime;
4+
5+
using System.Linq;
6+
using System.Reflection;
7+
8+
namespace Python.EmbeddingTest
9+
{
10+
public class TestOperator
11+
{
12+
[OneTimeSetUp]
13+
public void SetUp()
14+
{
15+
PythonEngine.Initialize();
16+
}
17+
18+
[OneTimeTearDown]
19+
public void Dispose()
20+
{
21+
PythonEngine.Shutdown();
22+
}
23+
24+
public class OperableObject
25+
{
26+
public int Num { get; set; }
27+
28+
public OperableObject(int num)
29+
{
30+
Num = num;
31+
}
32+
33+
public static OperableObject operator ~(OperableObject a)
34+
{
35+
return new OperableObject(~a.Num);
36+
}
37+
38+
public static OperableObject operator +(OperableObject a)
39+
{
40+
return new OperableObject(+a.Num);
41+
}
42+
43+
public static OperableObject operator -(OperableObject a)
44+
{
45+
return new OperableObject(-a.Num);
46+
}
47+
48+
public static OperableObject operator +(int a, OperableObject b)
49+
{
50+
return new OperableObject(a + b.Num);
51+
}
52+
public static OperableObject operator +(OperableObject a, OperableObject b)
53+
{
54+
return new OperableObject(a.Num + b.Num);
55+
}
56+
public static OperableObject operator +(OperableObject a, int b)
57+
{
58+
return new OperableObject(a.Num + b);
59+
}
60+
61+
public static OperableObject operator -(int a, OperableObject b)
62+
{
63+
return new OperableObject(a - b.Num);
64+
}
65+
public static OperableObject operator -(OperableObject a, OperableObject b)
66+
{
67+
return new OperableObject(a.Num - b.Num);
68+
}
69+
public static OperableObject operator -(OperableObject a, int b)
70+
{
71+
return new OperableObject(a.Num - b);
72+
}
73+
74+
public static OperableObject operator *(int a, OperableObject b)
75+
{
76+
return new OperableObject(a * b.Num);
77+
}
78+
public static OperableObject operator *(OperableObject a, OperableObject b)
79+
{
80+
return new OperableObject(a.Num * b.Num);
81+
}
82+
public static OperableObject operator *(OperableObject a, int b)
83+
{
84+
return new OperableObject(a.Num * b);
85+
}
86+
87+
public static OperableObject operator /(int a, OperableObject b)
88+
{
89+
return new OperableObject(a / b.Num);
90+
}
91+
public static OperableObject operator /(OperableObject a, OperableObject b)
92+
{
93+
return new OperableObject(a.Num / b.Num);
94+
}
95+
public static OperableObject operator /(OperableObject a, int b)
96+
{
97+
return new OperableObject(a.Num / b);
98+
}
99+
100+
public static OperableObject operator %(int a, OperableObject b)
101+
{
102+
return new OperableObject(a % b.Num);
103+
}
104+
public static OperableObject operator %(OperableObject a, OperableObject b)
105+
{
106+
return new OperableObject(a.Num % b.Num);
107+
}
108+
public static OperableObject operator %(OperableObject a, int b)
109+
{
110+
return new OperableObject(a.Num % b);
111+
}
112+
113+
public static OperableObject operator &(int a, OperableObject b)
114+
{
115+
return new OperableObject(a & b.Num);
116+
}
117+
public static OperableObject operator &(OperableObject a, OperableObject b)
118+
{
119+
return new OperableObject(a.Num & b.Num);
120+
}
121+
public static OperableObject operator &(OperableObject a, int b)
122+
{
123+
return new OperableObject(a.Num & b);
124+
}
125+
126+
public static OperableObject operator |(int a, OperableObject b)
127+
{
128+
return new OperableObject(a | b.Num);
129+
}
130+
public static OperableObject operator |(OperableObject a, OperableObject b)
131+
{
132+
return new OperableObject(a.Num | b.Num);
133+
}
134+
public static OperableObject operator |(OperableObject a, int b)
135+
{
136+
return new OperableObject(a.Num | b);
137+
}
138+
139+
public static OperableObject operator ^(int a, OperableObject b)
140+
{
141+
return new OperableObject(a ^ b.Num);
142+
}
143+
public static OperableObject operator ^(OperableObject a, OperableObject b)
144+
{
145+
return new OperableObject(a.Num ^ b.Num);
146+
}
147+
public static OperableObject operator ^(OperableObject a, int b)
148+
{
149+
return new OperableObject(a.Num ^ b);
150+
}
151+
152+
public static OperableObject operator <<(OperableObject a, int offset)
153+
{
154+
return new OperableObject(a.Num << offset);
155+
}
156+
157+
public static OperableObject operator >>(OperableObject a, int offset)
158+
{
159+
return new OperableObject(a.Num >> offset);
160+
}
161+
}
162+
163+
[Test]
164+
public void OperatorOverloads()
165+
{
166+
string name = string.Format("{0}.{1}",
167+
typeof(OperableObject).DeclaringType.Name,
168+
typeof(OperableObject).Name);
169+
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
170+
171+
PythonEngine.Exec($@"
172+
from {module} import *
173+
cls = {name}
174+
a = cls(-2)
175+
b = cls(10)
176+
c = ~a
177+
assert c.Num == ~a.Num
178+
179+
c = +a
180+
assert c.Num == +a.Num
181+
182+
a = cls(2)
183+
c = -a
184+
assert c.Num == -a.Num
185+
186+
c = a + b
187+
assert c.Num == a.Num + b.Num
188+
189+
c = a - b
190+
assert c.Num == a.Num - b.Num
191+
192+
c = a * b
193+
assert c.Num == a.Num * b.Num
194+
195+
c = a / b
196+
assert c.Num == a.Num // b.Num
197+
198+
c = a % b
199+
assert c.Num == a.Num % b.Num
200+
201+
c = a & b
202+
assert c.Num == a.Num & b.Num
203+
204+
c = a | b
205+
assert c.Num == a.Num | b.Num
206+
207+
c = a ^ b
208+
assert c.Num == a.Num ^ b.Num
209+
");
210+
}
211+
212+
[Test]
213+
public void OperatorOverloadMissingArgument()
214+
{
215+
string name = string.Format("{0}.{1}",
216+
typeof(OperableObject).DeclaringType.Name,
217+
typeof(OperableObject).Name);
218+
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
219+
220+
Assert.Throws<PythonException>(() =>
221+
PythonEngine.Exec($@"
222+
from {module} import *
223+
cls = {name}
224+
a = cls(2)
225+
b = cls(10)
226+
a.op_Addition()
227+
"));
228+
}
229+
230+
[Test]
231+
public void ForwardOperatorOverloads()
232+
{
233+
string name = string.Format("{0}.{1}",
234+
typeof(OperableObject).DeclaringType.Name,
235+
typeof(OperableObject).Name);
236+
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
237+
238+
PythonEngine.Exec($@"
239+
from {module} import *
240+
cls = {name}
241+
a = cls(2)
242+
b = 10
243+
c = a + b
244+
assert c.Num == a.Num + b
245+
246+
c = a - b
247+
assert c.Num == a.Num - b
248+
249+
c = a * b
250+
assert c.Num == a.Num * b
251+
252+
c = a / b
253+
assert c.Num == a.Num // b
254+
255+
c = a % b
256+
assert c.Num == a.Num % b
257+
258+
c = a & b
259+
assert c.Num == a.Num & b
260+
261+
c = a | b
262+
assert c.Num == a.Num | b
263+
264+
c = a ^ b
265+
assert c.Num == a.Num ^ b
266+
");
267+
}
268+
269+
270+
[Test]
271+
public void ReverseOperatorOverloads()
272+
{
273+
string name = string.Format("{0}.{1}",
274+
typeof(OperableObject).DeclaringType.Name,
275+
typeof(OperableObject).Name);
276+
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
277+
278+
PythonEngine.Exec($@"
279+
from {module} import *
280+
cls = {name}
281+
a = 2
282+
b = cls(10)
283+
284+
c = a + b
285+
assert c.Num == a + b.Num
286+
287+
c = a - b
288+
assert c.Num == a - b.Num
289+
290+
c = a * b
291+
assert c.Num == a * b.Num
292+
293+
c = a / b
294+
assert c.Num == a // b.Num
295+
296+
c = a % b
297+
assert c.Num == a % b.Num
298+
299+
c = a & b
300+
assert c.Num == a & b.Num
301+
302+
c = a | b
303+
assert c.Num == a | b.Num
304+
305+
c = a ^ b
306+
assert c.Num == a ^ b.Num
307+
");
308+
309+
}
310+
[Test]
311+
public void ShiftOperatorOverloads()
312+
{
313+
string name = string.Format("{0}.{1}",
314+
typeof(OperableObject).DeclaringType.Name,
315+
typeof(OperableObject).Name);
316+
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
317+
318+
PythonEngine.Exec($@"
319+
from {module} import *
320+
cls = {name}
321+
a = cls(2)
322+
b = cls(10)
323+
324+
c = a << b.Num
325+
assert c.Num == a.Num << b.Num
326+
327+
c = a >> b.Num
328+
assert c.Num == a.Num >> b.Num
329+
");
330+
}
331+
}
332+
}

src/runtime/classmanager.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,19 @@ private static ClassInfo GetClassInfo(Type type)
470470

471471
ob = new MethodObject(type, name, mlist);
472472
ci.members[name] = ob;
473+
if (mlist.Any(OperatorMethod.IsOperatorMethod))
474+
{
475+
string pyName = OperatorMethod.GetPyMethodName(name);
476+
string pyNameReverse = OperatorMethod.ReversePyMethodName(pyName);
477+
MethodInfo[] forwardMethods, reverseMethods;
478+
OperatorMethod.FilterMethods(mlist, out forwardMethods, out reverseMethods);
479+
// Only methods where the left operand is the declaring type.
480+
if (forwardMethods.Length > 0)
481+
ci.members[pyName] = new MethodObject(type, name, forwardMethods);
482+
// Only methods where only the right operand is the declaring type.
483+
if (reverseMethods.Length > 0)
484+
ci.members[pyNameReverse] = new MethodObject(type, name, reverseMethods);
485+
}
473486
}
474487

475488
if (ci.indexer == null && type.IsClass)

0 commit comments

Comments
 (0)