Python运算符重载

运算符重载的作用是让用户定义的对象使用中缀运算符(如 + 和 |)或一元运算符(如 - 和 ~)。说得宽泛一些,在 Python 中,函数调用(())、属性访问(.)和元素访问 / 切片([])也是运算符。

我们为 Vector 类简略实现了几个运算符。addmul 方法是为了展示如何使用特殊方法重载运算符,不过有些小问题被我们忽视了。此外,我们定义的Vector2d.eq 方法认为 Vector(3, 4) == [3, 4] 是真的(True),这可能并不合理。

运算符重载基础

在某些圈子中,运算符重载的名声并不好。这个语言特性可能(已经)被滥用,让程序员困惑,导致缺陷和意料之外的性能瓶颈。但是,如果使用得当,API 会变得好用,代码会变得易于阅读。Python 施加了一些限制,做好了灵活性、可用性和安全性方面的平衡:

  • 不能重载内置类型的运算符
  • 不能新建运算符,只能重载现有的
  • 某些运算符不能重载——is、and、or 和 not(不过位运算符
  • &、| 和 ~ 可以)

前面的博文已经为 Vector 定义了一个中缀运算符,即 ==,这个运算符由__eq__ 方法支持。我们将改进 eq 方法的实现,更好地处理不是Vector 实例的操作数。然而,在运算符重载方面,众多比较运算符(==、!=、>、<、>=、<=)是特例,因此我们首先将在 Vector 中重载四个算术运算符:一元运算符 - 和 +,以及中缀运算符 + 和 *。

构造函数与表达式: _ _init_ _, _ _sub_ _

class Number(obj):
	def __init__(self, value):
		self.data = value
		
	def __sub__(self, value):
		return Number(self.data - value)

x = Number(10)
y = x - 5
Y.data

常见运算符重载方法

methodoverloadcall
init构造函数对象创建: X = Class(args)
del析构函数X对象收回
add云算法+如果没有_iadd_, X+Y, X+=Y
or运算符|如果没有_ior_,X
repr_, str打印,转换print(X),repr(X),str(X)
call函数调用X(*args, **kwargs)
getattr点号运算X.undefined
setattr属性赋值语句X.any=value
delattr属性删除del X.any
getattribute属性获取X.any
getitem索引运算X[key],X[i:j]
setitem索引赋值语句X[key],X[i:j]=sequence
delitem索引和分片删除del X[key],del X[i:j]
len长度len(X),如果没有__bool__,真值测试
bool布尔测试bool(X)
lt, gt, le, ge, eq, ne特定的比较X<Y,X>Y,X<=Y,X>=Y, X==Y,X!=Y 注释:(lt: less than, gt: greater than, le: less equal, ge: greater equal,eq: equal, ne: not equal )
radd右侧加法other+X
iadd实地(增强的)加法X+=Y(or else add)
iter, next迭代环境I=iter(X), next()
contains成员关系测试item in X(任何可迭代)
index整数值hex(X), bin(X), oct(X)
enter, exit环境管理器with obj as var:
get, set,delete描述符属性X.attr,X.attr=value, del X.attr
new创建在__init__之前创建对象

索引和分片: _ _getitem_ _, _ _setitem_ _

如果在类中定义的话,则对于实例的索引运算,会自动调用__getitem__。当实例X出现X[i]这样的索引运算时,Python会自动调用__getitem__方法

class Indexer(obj):
	def __getitem__(self, index):
		return in index ** 2

x = Indexer()
x[2]

拦截分片

 class Indexer(obj):
 	data = [4, 5, 6, 7, 8, 9]
 	def __getitem__(self, index):
 		print("get item", index)
 		return self.data[index]
x = Index()
print(x[1:]
# ("get item", slice(1, None, None))
# [5, 6, 7, 8, 9]
print(x[2:4])
# ("get item", slice(2, 4, None))
# [6, 7]
print(x[2:9])
# ("get item", slice(2, 9, None))
# [6, 7, 8, 9]
print(x[::2])
# ("get item", slice(None, None, 2))
# [4, 6, 8]

索引迭代: _ _getitem_ _

class Stepper(obj):
	def __int__(self, data):
		self.data = data
	def __getitem__(self, index):
		return self.data[index]

x = Stepper("hello world")
for item in x:
	print(item)

迭代器对象: iter, next

尽管上一节__getitem__是有效的,但它是迭代退而求其次的方法。Python所有的迭代环境会有优先尝试__iter__的方法,再尝试__getitem__。

从技术角度上讲,迭代环境是通过iter去尝试寻找__iter__方法来实现,而这种方法返回一个迭代器对象。如果已经提供了,python会重复调用迭代器对象的next()方法,直到发生StopIteration异常。如果没有找到__iter__,python会使用__getitem__机制。

class MyIterator(obj):
	def __init__(self, wrappaed):
		self.wrapped = wrapped
		self.offset = 0
	def next(self):
		if self.offset >= len(self.wrapped):
			raise StopIteration
		item = self.wrapper[self.offset]
		self.offset += 1
		return item

class MyClass(obj):
	def __init__(self, data)
		self.data = data
	def __iter__(self):
		return MyIterator(self.data)
		
x = MyClass("hello")
I = iter(x)
while True:
	try:
		next(I)
	except Exception as e:
		print(e)

__getattr__和__setattr__捕捉属性的的引用

__getattr__拦截属性.运算符

class Empty(obj):
	def __getattr__(self, attrname):
		if attrname == "age":
			return 27
		else:
			raise AttributeError, attrname

x = Empty()
x.age	# 27
x.name
'''
Tracebaack(most recent call last):
	File "<pyshell#101>", line1, in<module>
		x.name
	File "<pyshell#97>", line6, in __getattr__
		raise AttributeError, attrname
AttributeError, attrname
'''
# 
class Empty(obj):
	def __getattr__(self, attr):
		return self.__dict__[attr]
	def __setattr__(self, attr, value):
		self.__dict__[attr] = value

x = Empty()
x.age = 100
x.age	# 100

__repr__和__str__会返回字符串表达形式

class Number(obj):
	def __init__(self, data):
		self.data = data
	def __add__(self, data):
		return Number(self.data + data)
	def __radd__(self, data):
		return Number(self.data + data)
	def __sub__(self, data):
		return Number(self.data + data)
	def __str__(self):
		return "Value:%d" % self.data
	def __repr__(self):
		return "Value:%d" % self.data

x = Number(100)
y = x - 300
print(x, y)
# Value:100, Value:-200

__radd__处理右侧加法

class Number(obj):
	def __init__(self, data):
		self.data = data
	def __add__(self, data):
		return Number(self.data + data)
	def __radd__(self, data):
		return Number(self.data + data)
	def __sub__(self, data):
		return Number(self.data + data)
	def __str__(self):
		return "Value:%d" % self.data
	def __repr__(self):
		return "Value:%d" % self.data

x = Number(100)
y = 100 + x
print(x, y)
# Value:100, Value:200

__call__拦截调用

当实例调用时,使用__call__方法

class Prod(obj):
	def __init__(self, data):
		self.data = data
	def __call__(self, data):
		return self.data * data

x =Prod(10)
x(2)

__del__是析构器

当实例创建时,就会调用__init__构造方法。当实例空间被收回时,析构函数__del__就会自动执行。

class Life(obj):
	def __init__(self, name):
		self.name = name
		print("hello" + name)
	def __del__(self):
		print("goodbye" + self.name)

# 实例化对象
brian = Life("Brian")
# hello Brian
brian = "Brian"
# goodbye Brian
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值