Python3之运算符的重载

#运算符重载
让自定义类生成的对象能够使用运算符进行操作

作用:

  • 让自定义类的实例像内建对象一样进行运算符操作
  • 让程序简洁易读
  • 对自定义对象将运算符赋予新的规则

####算数运算符的重载

方法名(双下划线显示不出来)运算符和表达式说明
add(self, rhs)self + rhs加法
sub(self, rhs)self - rhs减法
mul(self, rhs)self * rhs乘法
truediv(self, rhs)self / rhs除法
floordiv(self, rhs)self // rhs地板除
mod(self, rhs)self % rhs求余
pow(self, rhs)self ** rhs求幂运算

说明:运算符重载方法及参数已经有固定的含义,不建议改变原有的运算符的含义及参数的意义

举一个栗子:

class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return 'MyList({})'.format(self.data)

    def __add__(self, rhs):
        return MyList(self.data + rhs.data)

    def __mul__(self, rhs):
        return MyList(self.data * rhs)

L1 = MyList([1, 2, 3])
L2 = MyList([4, 5, 6])

L3 = L1 + L2
print(L3)  # MyList([1, 2, 3, 4, 5, 6])

L4 = L2 + L1
print(L4)  # MyList([4, 5, 6, 1, 2, 3])

L5 = L1 * 3
print(L5)  # MyList([1, 2, 3, 1, 2, 3, 1, 2, 3])

####反向运算符的重载
当运算符的左侧为内建类型时,右侧为自定义类型进行算数运算符运算时会出现TypeError错误,因无法修改内建类型的代码实现运算符操作,此时需要使用反序运算符操作。

方法名(双下划线显示不出来)运算符和表达式说明
radd(self, lhs)lhs + self加法
rsub(self, lhs)lhs - self减法
rtruediv(self, lhs)lhs * self除法
rfloordiv(self, lhs)lhs / self地板除
rmod(self, lhs)lhs % self求余
rpow(self, lhs)lhs ** self求幂运算
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return 'MyList({})'.format(self.data)

    def __mul__(self, rhs):
        print('__mul__被调用')
        return MyList(self.data * rhs)

    def __rmul__(self, lhs):
        print('__rmul__被调用')
        return MyList(self.data * lhs)

L1 = MyList([1, 2, 3])
L2 = MyList([4, 5, 6])

L3 = 3 * L1  # 调用 __rmul__方法
print(L3)  # MyList([1, 2, 3, 1, 2, 3, 1, 2, 3])

L4 = L2 * 2  # 调用 __mul__方法
print(L4)  # MyList([4, 5, 6, 4, 5, 6])

####复合赋值算数运算符的重载
以 x += y为例,此运算符会优先调用x.iadd(y) 的方法,如果此方法不存在则会拆解为:x = x +y,然后调用x = x.add(y)方法,如果也不存在,则会触发TypeError类型的错误异常。

方法名(双下划线显示不出来)运算符和表达式说明
iadd(self, rhs)self += rhs加法
isub(self, rhs)self -= rhs减法
imul(self, rhs)self *= rhs乘法
itruediv(self, rhs)self /= rhs除法
ifloordiv(self, rhs)self //= rhs地板除
imod(self, rhs)self %= rhs求余
ipow(self, rhs)self **= rhs求幂运算
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return 'MyList({})'.format(self.data)

    def __add__(self, rhs):
        return MyList(self.data + rhs.data)
   
    def __iadd__(self, rhs):
        print('__iadd__被调用了')
        self.data += rhs.data
        return self

L1 = MyList([1, 2, 3])
L2 = MyList([4, 5, 6])

L2 += L1  # 调用 __iadd__方法
print(L2)  # MyList([1, 2, 3, 4, 5, 6])

####比较运算符的重载

方法名(双下划线显示不出来)运算符和表达式说明
lt(self, rhs)self < rhs小于
le(self, rhs)self <= rhs小于等于
gt(self, rhs)self > rhs大于
ge(self, rhs)self >= rhs大于等于
eq(self, rhs)self == rhs等于
ne(self, rhs)self != rhs不等于

注:返回的是布尔值(True或False)

def __eq__(self, rhs):
    return self.__data == rhs.__data

s1 = OrderSet([1, 2, 3, 4])
if s1 == OrderSet([3, 4, 5]):
    print('s1 == OrderSet([3, 4, 5])')
else:
    print('s1 != OrderSet([3, 4, 5])')  

以上只是一部分代码

####位运算符重载

方法名(双下划线显示不出来)运算符和表达式说明
and(self, rhs)self & rhs位与
or(self, rhs)self \ rhs(竖线)位或
xor(self, rhs)self ^ rhs位异或
lshift(self, rhs)self << rhs左移
rshift(self, rhs)self >> rhs右移
class OrderSet:
    def __init__(self, iterable=()):
        self.__data = iterable

    def __repr__(self):
        return 'OrderSet({})'.format(self.__data)

    def __and__(self, rhs):
        r = set(self.__data) & set(rhs.__data)
        return OrderSet(list(r))

    def __or__(self, rhs):
        r = set(self.__data) | set(rhs.__data)
        return OrderSet(list(r))

    def __xor__(self, rhs):
        r = set(self.__data) ^ set(rhs.__data)
        return OrderSet(list(r))

s1 = OrderSet([1, 2, 3, 4])
s2 = OrderSet([3, 4, 5])
print(s1 & s2)  # OrderSet([3, 4])
print(s1 | s2)  # OrderSet([1, 2, 3, 4, 5])
print(s1 ^ s2)  # OrderSet([1, 2, 5])

####反向位运算符重载

方法名(双下划线显示不出来)运算符和表达式说明
rand(self, lhs)lhs & self位与
ror(self, lhs)lhs \ self(竖线)位或
rxor(self, lhs)lhs ^ self位异或
rlshift(self, lhs)lhs << self左移
rrshift(self, lhs)lhs >> self右移

####复合赋值位运算符重载

方法名(双下划线显示不出来)运算符和表达式说明
iand(self, rhs)self &= rhs位与
ior(self, rhs)self = rhs(竖线)位或
ixor(self, rhs)self ^= rhs位异或
ilshift(self, rhs)self <<= rhs左移
irshift(self, rhs)self >>= rhs右移

####一元运算符的重载

方法名(双下划线显示不出来)运算符和表达式说明
neg(self)- self负号
pos(self)+ self正号
invert(self)~ self取反
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return 'MyList({})'.format(self.data)

    def __neg__(self):
        G = [-x for x in self.data]
        return MyList(G)

L1 = MyList([1, -2, 3, -4, 5])
L2 = -L1
print(L2)  # MyList([-1, 2, -3, 4, -5])

####in / not in 运算符重载
格式:

def __contains__(self, e):
	语句块
def __contains__(self, item):
	return item in self.__data

s1 = OrderSet([1, 2, 3, 4])
if 2 in s1:
    print('2 in s1 为真')

####索引和切片运算符的重载方法
让自定义的类型的对象能够支持索引和切片的操作

方法名(双下划线显示不出来)运算符和表达式说明
getitem(self, i)x = self[i]索引或切片取值
setitem(self, i, v)self[i] = v设置索引或切片
delitem(self, i)del self[i]删除索引或切片
class MyList:
    def __init__(self, iterable=()):
        self.__data = list(iterable)

    def __repr__(self):
        return 'MyList({})'.format(self.__data)

    def __getitem__(self, i):
        print('i的值是', i)  # i的值是 3
        return self.__data[i]

    def __setitem__(self, key, value):
        self.__data[key] = value

    def __delitem__(self, key):
        # del self.__data[key]
        self.__data.pop(key)

L1 = MyList([1, -2, 0, -4, 5])
x = L1[3]
print(x)  # 4
L1[3] = 2
print(L1)  # MyList([1, -2, 0, 2, 5])

del L1[2]
print(L1)  # MyList([1, -2, 2, 5])

print(L1[::2])  # i的值是 slice(None, None, 2)
                    # [1, 2]

slice构造函数:
作用:
用于创建一个slice对象,此对象用于切片操作的传值
格式:

slice(start=None, stop=None, step=None)

slice对象的实例属性
start 切片的起始值 默认为None
stop 切片的终止值 默认为None
step 切片的步长 默认为None

class MyList:
    def __init__(self, iterable=()):
        self.__data = list(iterable)

    def __repr__(self):
        return 'MyList({})'.format(self.__data)

    def __getitem__(self, i):
        print('i的值是', i)
        if type(i) is int:
            print('用户正在用索引取值:')
        elif type(i) is slice:
            print('用户正在用切片取值:')
            print('起始值是:', i.start)
            print('终止值是:', i.stop)
            print('步长值是:', i.step)
        elif type(i) is str:
            print('用户正在用字符串进行索引操作')
        return self.__data[i]

L1 = MyList([1, -2, 0, -4, 5])
print(L1[1:3:2])

结果:

i的值是 slice(1, 3, 2)
用户正在用切片取值:
起始值是: 1
终止值是: 3
步长值是: 2
[-2]

本节完

<think>好的,我现在需要回答用户关于Python和C++运算符重载差异的问题。首先,我得回忆一下这两种语言在运算符重载方面的基本概念和机制。 用户提供的参考资料里,引用[2]提到C++的运算符重载涉及深拷贝和浅拷贝的问题,还有动态内存管理的注意事项,而引用[3]和[4]则说明Python通过特殊方法如__add__、__getattr__等来实现运算符重载,并且更灵活,不需要处理内存管理。这说明两种语言在实现方式上有明显不同。 首先,我得比较它们的实现机制。C++需要使用成员函数或友元函数,运算符重载的语法更严格,比如operator+这样的形式。而Python则是通过定义类的特殊方法,例如__add__来重载加法运算符。这一点应该作为第一个区别点。 然后,内存管理方面,C++需要开发者自己处理深拷贝和动态分配的内存,否则可能导致问题,比如浅拷贝引发的错误。而Python有垃圾回收机制,开发者不需要手动管理内存,所以在运算符重载时不需要考虑这些,这部分差异也是重点。 接下来是重载范围。C++允许重载大部分运算符,包括一些像赋值运算符=,而Python不允许重载某些运算符,比如逻辑与和或,但可能允许更多的运算符通过特殊方法来实现。需要查证Python和C++各自允许重载运算符范围,比如C++可以重载new和delete,而Python没有这些运算符重载。 语法差异方面,C++的运算符重载可以是成员函数或全局函数,而Python必须将运算符重载定义为类的方法,比如__eq__方法必须接收两个参数(在实例方法中第一个是self,第二个是其他对象)。这点需要注意,比如在Python中,比较运算符需要两个参数,而C++的成员函数运算符重载可能只有一个参数,因为另一个是隐含的this指针。 类型限制也是一个方面。C++的运算符重载通常需要明确的类型声明,类型安全较强,而Python是动态类型语言,可以在重载时处理不同类型的对象,更加灵活。例如,Python的__add__方法可以返回任何类型,而C++需要返回特定类型。 错误处理方面,C++可能在编译时就检查运算符重载的正确性,而Python在运行时才会发现错误,比如未实现的特殊方法。比如,在Python中如果两个对象没有实现相应的__add__方法,尝试相加时会抛出错误,而C++在编译时就会检查是否定义了对应的运算符。 另外,应用场景和设计哲学的不同。C++的运算符重载强调性能和精细控制,常用于自定义类型如字符串或矩阵的操作,而Python更注重可读性和简洁性,比如让自定义类支持类似内置类型的操作。 最后,可能还需要提到运算符重载的复杂性和学习曲线。C++由于涉及更多底层细节,如引用、指针、内存管理,运算符重载的实现相对复杂,而Python更简单,只需定义相应方法即可。 现在需要将这些点组织成结构清晰的回答,分点说明差异,并结合用户提供的引用资料中的例子,比如引用[2]中C++的深拷贝问题和引用[3][4]中Python的特殊方法。同时要确保使用正确的中文表达,符合格式要求,如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能不需要数学公式,但需要检查是否有需要的地方。 可能还需要生成相关问题,比如两种语言在运算符重载时的最佳实践,或者如何处理多态性,或者具体例子比较等。</think>### Python与C++运算符重载差异比较 #### 1. **实现机制** - **C++** 需要显式定义运算符重载函数,可以是类的成员函数或全局函数,语法为`operator+`等形式。例如: ```cpp class MyString { public: MyString operator+(const MyString& other) { /* 实现 */ } }; ``` 重载过程需考虑对象所有权和内存管理(如深拷贝与浅拷贝问题)[^2]。 - **Python** 通过预定义的特殊方法(如`__add__`、`__eq__`)实现,无需手动管理内存。例如: ```python class Vector: def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) ``` 动态类型特性允许更灵活的操作[^3][^4]。 #### 2. **内存管理** - **C++** 需显式处理动态内存分配和拷贝语义。例如,重载赋值运算符`=`时,必须区分浅拷贝与深拷贝,避免悬垂指针[^2]。 - **Python** 依赖垃圾回收机制,开发者无需关注内存释放,运算符重载仅需关注逻辑实现。 #### 3. **可重载运算符范围** - **C++** 支持重载绝大多数运算符(如`new`、`delete`、`()`等),甚至包括**流操作符**`<<`和`>>`。 - **Python** 不允许重载部分运算符(如逻辑运算符`and`/`or`),但支持通过`__getitem__`、`__call__`等方法模拟容器或函数行为[^4]。 #### 4. **语法与类型限制** - **C++** - 运算符重载函数需明确参数类型,例如`operator+`的参数和返回值类型需严格匹配。 - 支持运算符的**左值/右值引用**重载(C++11后)。 - **Python** - 动态类型允许运算符处理不同类型对象。例如,`__add__`可返回任意类型(如字符串拼接或数值相加)[^3]。 - 运算符方法需显式处理类型兼容性(如检查`other`是否为同类实例)。 #### 5. **错误处理** - **C++** 类型不匹配或未定义运算符会在**编译期**报错。 - **Python** 类型错误或未实现的运算符会在**运行时**抛出异常(如`TypeError`)。 #### 6. **设计哲学** - **C++** 强调性能与控制,运算符重载常用于实现数学对象(如矩阵运算)或资源管理(如智能指针)。 - **Python** 强调可读性与简洁性,运算符重载多用于使自定义类行为接近内置类型(如自定义列表的`+`操作)[^4]。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秒不可闫M先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值