Improve cmath.complex dunder methods and tests; align lru_cache with stdlib

- Add __rtruediv__, __rpow__, __pos__, __bool__, __hash__, and coercion errors
- Extend __pow__ for complex exponents and real-only complex unwrap
- Tests for rdiv, rpow, bool, pos, float/int errors, hash, complex**complex
- lru_cache: accept typed= and **kwargs for host-Python compatibility when PYTHONPATH shadows this module
- Regenerate _generated.c from prebuild.py

Made-with: Cursor
This commit is contained in:
Osama Alwaly 2026-03-24 18:20:41 +02:00
parent 005a2725a1
commit caa416e5e2
4 changed files with 78 additions and 5 deletions

View File

@ -81,7 +81,22 @@ class complex:
return complex(self.real / other, self.imag / other)
return NotImplemented
def __pow__(self, other: int | float):
def __pow__(self, other):
if type(other) is complex and other.imag == 0:
other = other.real
if type(other) is complex:
r = self.__abs__()
th = phase(self)
lr = math.log(r) if r > 0 else float("-inf")
ox, oy = other.real, other.imag
wx = ox * lr - oy * th
wy = ox * th + oy * lr
if r == 0:
if oy == 0 and ox > 0:
return complex(0.0, 0.0)
return complex(float("nan"), float("nan"))
ex = math.exp(wx)
return complex(ex * math.cos(wy), ex * math.sin(wy))
if type(other) in (int, float):
return complex(self.__abs__() ** other * math.cos(other * phase(self)),
self.__abs__() ** other * math.sin(other * phase(self)))
@ -93,6 +108,39 @@ class complex:
def __neg__(self):
return complex(-self.real, -self.imag)
# New methods added below
def __rtruediv__(self, other):
if type(other) is complex:
return other / self
if type(other) in (int, float):
return complex(other) / self
return NotImplemented
def __rpow__(self, other):
if type(other) in (int, float):
return complex(other) ** self
if type(other) is complex:
return other ** self
return NotImplemented
def __pos__(self):
return complex(self.real, self.imag)
def __bool__(self):
return self.real != 0 or self.imag != 0
def __complex__(self):
return complex(self.real, self.imag)
def __float__(self):
raise TypeError("can't convert complex to float")
def __int__(self):
raise TypeError("can't convert complex to int")
def __index__(self):
raise TypeError("complex cannot be used as an index")
def __hash__(self):
return hash((self.real, self.imag))
@ -184,4 +232,4 @@ tau = 2 * pi
inf = math.inf
infj = complex(0, inf)
nan = math.nan
nanj = complex(0, nan)
nanj = complex(0, nan)

View File

@ -9,7 +9,7 @@ class cache:
return self.cache[args]
class lru_cache:
def __init__(self, maxsize=128):
def __init__(self, maxsize=128, typed=False, **kwargs):
self.maxsize = maxsize
self.cache = {}

File diff suppressed because one or more lines are too long

View File

@ -50,3 +50,28 @@ assert repr(-infj) == '(-0.0-infj)', -infj
assert math.log(1) == 0.0
assert isclose(log(10+5j), 2.4141568686511508+0.4636476090008061j)
# __rtruediv__, __rpow__, __pos__, __bool__, coercions, __hash__
assert isclose(10 / c1, complex(1.2, -1.6))
assert isclose(2 ** complex(3, 0), complex(8, 0))
ref_i = complex(math.cos(math.log(2)), math.sin(math.log(2)))
assert isclose(2 ** 1j, ref_i)
assert isclose((1 + 1j) ** (0.5 + 0.5j), complex(0.6777725052404345, 0.4306022701168375))
assert not complex(0, 0)
assert complex(0, 1)
assert +c1 == c1
try:
float(1 + 2j)
assert False, "expect TypeError for float(complex)"
except TypeError:
pass
try:
int(1 + 2j)
assert False, "expect TypeError for int(complex)"
except TypeError:
pass
assert len({c1, complex(3, 4)}) == 1