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_运算符重载-容器相关方法-可调用对象
URL分享:http://ybzwz.com/article/gghdig.html