Python面向?qū)ο筮\(yùn)算符重載

2018-06-08 17:30 更新


運(yùn)算符重載的概念如下:

  1. 運(yùn)算符重載讓類攔截常規(guī)的Python運(yùn)算;

  2. 類可重載所有Python表達(dá)式運(yùn)算符;

  3. 類也可重載打印、函數(shù)調(diào)用、屬性點(diǎn)號(hào)運(yùn)算等內(nèi)置運(yùn)算;

  4. 重載是類實(shí)例的行為想內(nèi)置類型;

  5. 重載是通過提供特殊名稱的類方法來實(shí)現(xiàn)的;

常見的運(yùn)算符重載方法

方法重載調(diào)用
__init__構(gòu)造函數(shù)對(duì)象建立:X = Class(args)
__del__解析函數(shù)X對(duì)象收回
__add__運(yùn)算符+如果沒有__iadd__,X+Y,X+=Y
__or__運(yùn)算符或如果沒有__ior__
__repr__,__str__打印、轉(zhuǎn)換print(X)、repr(X)、str(X)
__call__函數(shù)調(diào)用X(args, *kwargs)
__getattr__點(diǎn)號(hào)運(yùn)算X.undefined
__setattr__屬性賦值語句X.any = value
__delattr__屬性刪除del X.any
__getattribute__屬性獲取X.any
__getitem__索引運(yùn)算X[key],X[i:j],沒__iter__時(shí)的for循環(huán)和其他迭代器
__setitem__索引賦值語句X[key]=value,X[i:k]=sequence
__delitem__索引和分片刪除del X[key], del X[i:j]
__len__長(zhǎng)度len(X),如果沒有__bool__,真值測(cè)試
__bool__布爾測(cè)試bool(X),真測(cè)試
__lt__,__gt__,__le__,__ge__,__eq__,__ne__特定的比較XY…,x>
__radd__右側(cè)加法Other + X
__iadd__增強(qiáng)的加法X += Y
__iter__,__next__迭代環(huán)境I=iter(X),next(I)
__contains__成員關(guān)系測(cè)試item in X(任何可迭代對(duì)象)
__index__整數(shù)值hex(X),bin(X),oct(X),o[X],O[X:]
__enter__,__exit__環(huán)境管理器with obj as var:
__get__,__set__,__delete__描述符屬性X.attr,X.attr=Value,del X.attr
__new__創(chuàng)建在__init__之前創(chuàng)建對(duì)象

所有重載方法的名稱前后都有兩個(gè)下劃線字符,以便把同類中定義的變量名區(qū)別開來。

構(gòu)造函數(shù)和表達(dá)式:__init__和__sub__

  1. >>> class Number:

  2. ...   def __init__(self, start):

  3. ...     self.data = start

  4. ...   def __sub__(self, other):

  5. ...     return Number(self.data - other)

  6. ...

  7. >>> X = Number(5)

  8. >>> Y = X - 2

  9. >>> Y

  10. <__main__.Number object at 0x10224d550>

  11. >>> Y.data

  12. 3

索引和分片: __getitem__和__setitem__

基本索引

  1. >>> class Index:

  2. ...     def __getitem__(self, item):

  3. ...         return item ** 2

  4. ...

  5. >>>

  6. >>> for i in range(5):

  7. ...     I = Index()

  8. ...     print(I[i], end=' ')

  9. ...

  10. 0 1 4 9 16

切片索引

  1. >>> class Index:

  2. ...   data = [5, 6, 7, 8, 9]

  3. ...   def __getitem__(self, item):

  4. ...     print('getitem: ', item)

  5. ...     return self.data[item]

  6. ...   def __setitem__(self, key, value):

  7. ...     self.data[key] = value

  8. ...

  9. >>> X = Index()

  10. >>> print(X[1:4])

  11. getitem:  slice(1, 4, None)

  12. [6, 7, 8]

  13. >>> X[1:4] = (1, 1, 1)

  14. >>> print(X[1:4])

  15. getitem:  slice(1, 4, None)

  16. [1, 1, 1]

索引迭代:__getitem__

如果重載了這個(gè)方法,for循環(huán)每次循環(huán)時(shí)都會(huì)調(diào)用類的getitem方法;

  1. >>> class stepper:

  2. ...     def __getitem__(self, item):

  3. ...         return self.data[item].upper()

  4. ...

  5. >>>

  6. >>> X = stepper()

  7. >>> X.data = 'ansheng'

  8. >>> for item in X:

  9. ...     print(item)

  10. ...

  11. A

  12. N

  13. S

  14. H

  15. E

  16. N

  17. G

迭代器對(duì)象:__iter__和__next__

  1. >>> class Squares:

  2. ...   def __init__(self, start, stop):

  3. ...         self.value = start - 1

  4. ...         self.stop = stop

  5. ...   def __iter__(self):

  6. ...         return self

  7. ...   def __next__(self):

  8. ...         if self.value == self.stop:

  9. ...             raise StopIteration

  10. ...         self.value += 1

  11. ...         return self.value ** 2

  12. ...

  13. >>> for i in Squares(1, 5):

  14. ...   print(i)

  15. ...

  16. 1

  17. 4

  18. 9

  19. 16

  20. 25

成員關(guān)系:__contains__、__iter__和__getitem__

  1. class Iters:

  2.    def __init__(self, value):

  3.        self.data = value

  4.    def __getitem__(self, item):

  5.        print('get[%s]' % item, end='')

  6.        return self.data[item]

  7.    def __iter__(self):

  8.        print('iter>==', end='')

  9.        self.ix = 0

  10.        return self

  11.    def __next__(self):

  12.        print('next:', end='')

  13.        if self.ix == len(self.data): raise StopIteration

  14.        item = self.data[self.ix]

  15.        self.ix += 1

  16.        return item

  17.    def __contains__(self, item):

  18.        print('contains: ', end=' ')

  19.        return item in self.data

  20. X = Iters([1, 2, 3, 4, 5])

  21. print(3 in X)

  22. for i in X:

  23.    print(i, end='|')

  24. print([i ** 2 for i in X])

  25. print(list(map(bin, X)))

  26. I = iter(X)

  27. while True:

  28.    try:

  29.        print(next(I), end=' @')

  30.    except StopIteration as e:

  31.        break

屬性引用:__getattr__和__setattr__

當(dāng)通過未定義的屬性名稱和實(shí)例通過點(diǎn)號(hào)進(jìn)行訪問時(shí),就會(huì)用屬性名稱作為字符串調(diào)用這個(gè)方法,但如果類使用了繼承,并且在超類中可以找到這個(gè)屬性,那么就不會(huì)觸發(fā)。

  1. >>> class empty:

  2. ...     def __getattr__(self, item):

  3. ...         if item == 'age':

  4. ...             return 40

  5. ...         else:

  6. ...             raise AttributeError(item)

  7. ...

  8. >>>

  9. >>> x = empty()

  10. >>> print(x.age)

  11. 40

  12. >>> print(x.name)

  13. Traceback (most recent call last):

  14.  File "<stdin>", line 1, in <module>

  15.  File "<stdin>", line 6, in __getattr__

  16. AttributeError: name


  1. >>> class accesscontrol:

  2. ...     def __setattr__(self, key, value):

  3. ...         if key == 'age':

  4. ...             self.__dict__[key] = value

  5. ...         else:

  6. ...             raise AttributeError(key + ' not allowed')

  7. ...

  8. >>>

  9. >>> x = accesscontrol()

  10. >>> x.age = 40

  11. >>> print(x.age)

  12. 40

  13. >>> x.name = 'Hello'

  14. Traceback (most recent call last):

  15.  File "<stdin>", line 1, in <module>

  16.  File "<stdin>", line 6, in __setattr__

  17. AttributeError: name not allowed

__repr__和__str__會(huì)返回字符串表達(dá)式

__repr__和__str__都是為了更友好的顯示,具體來說,如果在終端下print(Class)則會(huì)調(diào)用__repr__,非終端下會(huì)調(diào)用__str__方法,且這兩個(gè)方法只能返回字符串;

  1. class adder:

  2.    def __init__(self, value=0):

  3.        self.data = value

  4.    def __add__(self, other):

  5.        self.data += other

  6.    def __repr__(self):

  7.        return 'addrepr(%s)' % self.data

  8.    def __str__(self):

  9.        return 'N: %s' % self.data

  10. x = adder(2)

  11. x + 1

  12. print(x)

  13. print((str(x), repr(x)))

右側(cè)加法和原處加法: __radd__和__iadd__

只有當(dāng)+右側(cè)的對(duì)象是類實(shí)例,而左邊對(duì)象不是類實(shí)例的時(shí)候,Python才會(huì)調(diào)用__radd__

  1. class Commuter:

  2.    def __init__(self, val):

  3.        self.val = val

  4.    def __add__(self, other):

  5.        print('add', self.val, other)

  6.        return self.val + other

  7.    def __radd__(self, other):

  8.        print('radd', self.val, other)

  9.        return other + self.val

  10. x = Commuter(88)

  11. y = Commuter(99)

  12. print(x + 1)

  13. print('')

  14. print(1 + y)

  15. print('')

  16. print(x + y)

使用__iadd__進(jìn)行原處加法

  1. class Number:

  2.    def __init__(self, val):

  3.        self.val = val

  4.    def __iadd__(self, other):

  5.        self.val += other

  6.        return self

  7. x = Number(5)

  8. x += 1

  9. x += 1

  10. print(x.val)

  11. class Number:

  12.    def __init__(self, val):

  13.        self.val = val

  14.    def __add__(self, other):

  15.        return Number(self.val + other)

  16. x = Number(5)

  17. x += 1

  18. x += 1

  19. print(x.val)

Call表達(dá)式:__call__

當(dāng)調(diào)用類實(shí)例時(shí)執(zhí)行__call__方法

  1. class Callee:

  2.    def __call__(self, *args, **kwargs):

  3.        print('Callee:', args, kwargs)

  4. C = Callee()

  5. C(1, 2, 3)

  6. C(1, 2, 3, x=1, y=2, z=3)

  7. class Prod:

  8.    def __init__(self, value):

  9.        self.value = value

  10.    def __call__(self, other):

  11.        return self.value * other

  12. x = Prod(3)

  13. print(x(3))

  14. print(x(4))

比較:__lt__,__gt__和其他方法

類可以定義方法來捕獲所有的6種比較運(yùn)算符:<、>、<=、>=、==和!=

  1. class C:

  2.    data = 'spam'

  3.    def __gt__(self, other):

  4.        return self.data > other

  5.    def __lt__(self, other):

  6.        return self.data < other

  7. x = C()

  8. print(x > 'han')

  9. print(x < 'han')

布爾值測(cè)試:bool和len

  1. class Truth:

  2.    def __bool__(self):

  3.        return True

  4. X = Truth()

  5. if X: print('yes')

  6. class Truth:

  7.    def __bool__(self):

  8.        return False

  9. X = Truth()

  10. print(bool(X))

如果沒有這個(gè)方法,Python退而求其次的求長(zhǎng)度,因?yàn)橐粋€(gè)非空對(duì)象看作是真:

  1. >>> class Truth:

  2. ...   def __len__(self): return 0

  3. ...

  4. >>> X = Truth()

  5. >>> if not X: print('no')

  6. ...

  7. no


如果兩個(gè)方法都有,__bool__會(huì)勝過__len__:


  1. >>> class Truth:

  2. ...   def __bool__(self): return True

  3. ...   def __len__(self): return 0

  4. ...

  5. >>> X = Truth()

  6. >>> bool(X)

  7. True

如果兩個(gè)方法都沒有定義,對(duì)象毫無疑義的看作為真:

  1. >>> class Truth: pass

  2. ...

  3. >>> bool(Truth)

  4. True

對(duì)象解析函數(shù):__del__

每當(dāng)實(shí)例產(chǎn)生時(shí),就會(huì)調(diào)用init構(gòu)造函數(shù),每當(dāng)實(shí)例空間被收回時(shí),它的對(duì)立面__del__,也就是解析函數(shù),就會(huì)自動(dòng)執(zhí)行;

  1. class Life:

  2.    def __init__(self, name='unknown'):

  3.        print('Hello, ', name)

  4.        self.name = name

  5.    def __del__(self):

  6.        print('Goodbye', self.name)

  7. brian = Life('Brian')

  8. brian = 'loretta'


本文出自 “一盞燭光” 博客,謝絕轉(zhuǎn)載!

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)