30面向对象5_运算符重载-容器相关方法-可调用对象
创新互联客户idc服务中心,提供成都服务器托管、成都服务器、成都主机托管、成都双线服务器等业务的一站式服务。通过各地的服务中心,我们向成都用户提供优质廉价的产品以及开放、透明、稳定、高性价比的服务,资深网络工程师在机房提供7*24小时标准级技术保障。
目录
运算符重载...1
容器相关方法...4
可调用对象...6
运算符重载
可大大提高类的可用性;
锦上添花的东西;
好的类的设计应提供类似的方法;
Operator模块提供以下特殊方法,可将类的实例使用下面的操作符来操作:
<,<=,==,>,>=,!= | __lt__,__le__,__eq__,__gt__,__ge__,__ne__ | 比较运算符,自定义类时用 |
+,-,*,/,%,//,**,divmod | __add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__ | 算术运算符,移位、位运算符也有对应的方法 |
+=,-=,*=,/=,%=,//=,**= | __iadd__,__isub__,__imul__,__itruediv__,__imod__,__ifloordiv__,__ipow__ |
注:
移位、位运算符也有对应的方法,在python中没提升性能,但也没降低性能,其它语言中会大大提升性能;
Int类,几乎实现了所有操作符,可作为参考;
在pycharm中,输入int,ctrl+单击,可查看运算符魔术方法的帮助;
运算符重载应用场景:
往往是用面向对象实现的类,需要做大量的运算;
提供运算符重载(__add__()),比直接提供加法方法(Point类中的add())要更加适合该领域内使用者的习惯,用户在日常用的运算符是在数学上最常见的表达方式,如print(p1 + p2),实现了Point类的二元操作(向量的加法);
例:
class A:
def __init__(self,x):
self.x = x
def __sub__(self, other):
# return self.x - other.x
# return A(self.x - other.x) #返回一个新对象(new一个新对象)
self.x = self.x - other.x
return self #就地修改,根据业务场景选择使用
# def __ne__(self, other):
# return self.x != other.x
def __eq__(self, other): #判断两对象是否相等用__eq__,一般很少用__ne__
return self.x == other.x
def __repr__(self):
return str(self.x)
__str__ = __repr__ #技巧
def __lt__(self, other): #也可用__gt__,在测试时缺哪个补哪个;只有实现该方法,才可用sorted()和reversed(),否则报错TypeError: '<' not supported between instances of 'A' and 'A';一般把比较大小的函数写在类内,这样更优雅
return self.x < other.x
def __iadd__(self, other):
# return A(self.x + other.x)
self.x = self.x + other.x
return self #就地修改
a1 = A(4)
a2 = A(5)
print(a1)
print(a1 - a2) #等价于print(a1.__sub__(a2))
print(a1)
print(a1.__sub__(a2))
print(a1)
print(a1 == a2)
a3 = A(2)
lst = [a1,a2,a3]
print(lst)
print(sorted(lst))
print(list(reversed(sorted(lst))))
a1 += a2
print(a1)
输出:
4
-1
-1
-6
-6
False
[-6, 5, 2]
[-6, 2, 5]
[5, 2, -6]
-1
习题:
完成Point类设计,实现判断点相等的方法,并完成向量的加法;
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return Point(self.x + other.x,self.y + other.y) #根据使用者习惯设计
# def __add__(self, other):
# return (self.x + other.x,self.y + other.y)
def __str__(self):
return 'Point:{},{}'.format(self.x,self.y)
__repr__ = __str__
def add(self,other):
return (self.x + other.x,self.y + other.y)
p1 = Point(1,1)
p2 = Point(1,1)
print(p1,p2)
points = (p1,p2)
print(points[0].add(points[1]))
print(points[0] + points[1]) #类似pathlib.Path中的/
print(Point(*(points[0].add(points[1]))))
# print(Point(*(points[0] + points[1])))
print(p1 == p2)
print(p1 is p2)
输出:
Point:1,1 Point:1,1
(2, 2)
Point:2,2
Point:2,2
True
False
容器相关方法
__len__,内建函数len(),返回对象的长度,>=0的整数,即使把对象当作容器类型看,如list、dict,bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回,非0为真;
__iter__,迭代容器时调用,要求返回一个新的迭代器对象iterator;
__contains__,in成员运算符,没有实现就调用__iter__方法遍历;
__getitem__,实现self[key]访问,序列对象,key接受整数为索引,或切片,对于set、dict,key为可hashable,key不存在引发KeyError异常;
__setitem__,类似__getitem__,是设置值的方法;
__missing__,字典使用__getitem__()调用时,key不存在执行该方法,如class MyDict(dict): pass;
__len__和__size__并不对等:
__len__,容器中元素的个数;
__size__,容器的大小,第三方库中,当容量快满时,会把size扩展;
__len__和__iter__:
有时并不关心元素有多少个,直接取元素,__iter__用的多些;
注:
single linkedlist中的__getitem__,仅用于容器,提供一种方便的接口,如索引或其它方式来用;
函数中的属性,foo.__defaults__,用元组保存位置参数默认值;
函数中的属性,foo.__kwdefaults__,用元组保存关键字参数默认值;
习题:
将购物车类,改造成方便操作的容器类;
class Color:
RED = 0
GREEN = 1
BLUE = 2
GOLDEN = 3
BLACK = 4
OTHER = 1000
class Item:
def __init__(self,**kwargs):
self.__spec = kwargs
def __repr__(self):
# return str(sorted(self.__spec.items()))
return str(self.__spec)
__str__ = __repr__
class Cart:
def __init__(self):
self.items = []
def additem(self,item:Item): #兼容__add__,看用户使用要求自定义,要不要return,一般用__add__
self.items.append(item)
def getall(self):
return self.items
def __len__(self):
return len(self.items)
def __add__(self, other):
self.items.append(other)
return self
def __iter__(self): #要求必须返回迭代器iterator,简化,让使用者觉得实例就是可迭代对象
return iter(self.items)
def __getitem__(self, item): #该例容器为列表,此处item为索引
return self.items[item]
def __setitem__(self, key, value): #key为index,value为字典
self.items[key] = value
# self[key] = value
def __repr__(self):
return str(self.items)
__str__ = __repr__
mycart = Cart()
myphone = Item(mark = 'sony',color = Color.BLACK,price=2250)
mybicycle = Item(mark='decathlan',color=Color.BLACK,price=1599)
mykindle = Item(mark='amazon',color=Color.OTHER,price=498)
mycart.additem(myphone)
print(mycart.getall())
print(len(mycart))
print(mycart + mybicycle + mykindle) #链式编程实现加法,等价于mycart.__add__(mybicycle).__add__(mykindle)
# print(mycart.__add__(mybicycle).__add__(mykindle))
print(len(mycart))
# for x in mycart.items: #类中没有__iter__方法时使用此种方式迭代实例中的容器
# print(x)
for x in mycart: #类中有__iter__方法后,实例就成了可迭代对象,简化,让用户觉得实例就是可迭代对象
print(x)
print(mycart[1])
mycart[1] = {'mark': 'giant', 'color': 4, 'price': 1599} #此处的value为字典
print(mycart[1])
输出:
[{'mark': 'sony', 'color': 4, 'price': 2250}]
1
[{'mark': 'sony', 'color': 4, 'price': 2250}, {'mark': 'decathlan', 'color': 4, 'price': 1599}, {'mark': 'amazon', 'color': 1000, 'price': 498}]
3
{'mark': 'sony', 'color': 4, 'price': 2250}
{'mark': 'decathlan', 'color': 4, 'price': 1599}
{'mark': 'amazon', 'color': 1000, 'price': 498}
{'mark': 'decathlan', 'color': 4, 'price': 1599}
{'mark': 'giant', 'color': 4, 'price': 1599}
可调用对象
python中一切皆对象,函数也不例外;
__call__,类中实现,实例就可像函数一样调用;
定义一个类,并实例化得到其实例,将实例像函数一样调用;
一个实例可将其当作函数,进而可当装饰器来用;
例:
def foo(x): #函数即对象,对象foo加上(),就是调用对象的__call__()方法
print(x)
print(callable(foo))
foo(4) #等价于foo.__call__(4)
foo.__call__(4)
print(foo.__name__)
print(foo.__doc__)
print(foo.__dict__)
print(foo.__call__)
print(dir(foo))
输出:
True
4
4
foo
None
{}
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
例:
class A:
def __call__(self, *args, **kwargs): #__call__写在类中,该类的实例就可调用
print(5)
A()() #先实例化再调用,等价于a = A();a()
a = A()
a()
a(4,5,6)
输出:
5
5
5
例:
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __call__(self, *args, **kwargs):
return 'Point({},{})'.format(self.x,self.y)
p = Point(4,5)
print(p)
print(p())
输出:
<__main__.Point object at 0x7fbc9e10c710>
Point(4,5)
例:
class Adder:
def __call__(self, *args):
ret = 0
for x in args:
ret += x
self.ret = ret
return ret
adder = Adder()
print(adder(4,5,6))
print(adder.ret)
输出:
15
15
习题:
定义一个fibonacci数列的类,方便调用,计算第n项;
使用类来实现fibonacci数列,可缓存数据,便于检索;
方1:
class Fib:
def __init__(self):
self.items = [0,1,1]
def __call__(self, index):
if index < len(self.items):
return self.items
# return self.items[index]
if index < 0:
raise IndexError('wrong index')
for i in range(3,index+1):
self.items.append(self.items[i-1] + self.items[i-2])
return self.items
# return self.items[index]
print(Fib()(8))
输出:
[0, 1, 1, 2, 3, 5, 8, 13, 21]
方2:
class Fib:
def __init__(self):
self.items = [0,1,1]
def __call__(self,index):
return self[index]
def __iter__(self):
return iter(self.items)
def __len__(self):
return len(self.items)
def __getitem__(self, index):
if index < len(self.items):
return self.items[index]
if index < 0:
raise IndexError('wrong index')
for i in range(len(self),index+1): #使用len(self),要加__len__
self.items.append(self.items[i-2] + self.items[i-1])
return self.items[index]
def __str__(self):
return str(self.items)
__repr__ = __str__
fib = Fib()
print(fib(8),len(fib))
for x in fib:
print(x)
print(fib[8],fib[7])
print(fib[-8])
输出:
21 9
0
1
1
2
3
5
8
13
21
21 13
1
本文名称:30面向对象5_运算符重载-容器相关方法-可调用对象
网站路径:http://cdiso.cn/article/gghdig.html