數學和 cmath 內建的魔術方法和取冪

假設你有一個儲存純整數值的類:

class Integer(object):
    def __init__(self, value):
        self.value = int(value) # Cast to an integer
        
    def __repr__(self):
        return '{cls}({val})'.format(cls=self.__class__.__name__,
                                     val=self.value)
    
    def __pow__(self, other, modulo=None):
        if modulo is None:
            print('Using __pow__')
            return self.__class__(self.value ** other)
        else:
            print('Using __pow__ with modulo')
            return self.__class__(pow(self.value, other, modulo))
    
    def __float__(self):
        print('Using __float__')
        return float(self.value)
    
    def __complex__(self):
        print('Using __complex__')
        return complex(self.value, 0)

使用內建 pow 函式或**運算子始終呼叫 __pow__

Integer(2) ** 2                 # Integer(4)
# Prints: Using __pow__
Integer(2) ** 2.5               # Integer(5)
# Prints: Using __pow__
pow(Integer(2), 0.5)            # Integer(1)
# Prints: Using __pow__  
operator.pow(Integer(2), 3)     # Integer(8)
# Prints: Using __pow__
operator.__pow__(Integer(3), 3) # Integer(27)
# Prints: Using __pow__

__pow__() 方法的第二個引數只能通過使用 builtin-pow() 或直接呼叫方法來提供:

pow(Integer(2), 3, 4)           # Integer(0)
# Prints: Using __pow__ with modulo
Integer(2).__pow__(3, 4)        # Integer(0) 
# Prints: Using __pow__ with modulo  

雖然 math 函式總是將它轉換為 float 並使用浮點計算:

import math

math.pow(Integer(2), 0.5) # 1.4142135623730951
# Prints: Using __float__

cmath-functions 嘗試將其轉換為 complex,但如果沒有顯式轉換為 complex,也可以回退到 float

import cmath

cmath.exp(Integer(2))     # (7.38905609893065+0j)
# Prints: Using __complex__

del Integer.__complex__   # Deleting __complex__ method - instances cannot be cast to complex

cmath.exp(Integer(2))     # (7.38905609893065+0j)
# Prints: Using __float__

如果缺少 __float__() 方法,mathcmath 都不會起作用:

del Integer.__float__  # Deleting __complex__ method

math.sqrt(Integer(2))  # also cmath.exp(Integer(2))

TypeError:需要一個 float