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

常见运算符重载方法

method overload call
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

猜你喜欢

转载自blog.csdn.net/qq_41875876/article/details/83243228