zhwz 发表于 2018-8-14 13:37:54

Python面向对象运算符重载

  运算符重载的概念如下:

[*]  运算符重载让类拦截常规的Python运算;
[*]  类可重载所有Python表达式运算符;
[*]  类也可重载打印、函数调用、属性点号运算等内置运算;
[*]  重载是类实例的行为想内置类型;
[*]  重载是通过提供特殊名称的类方法来实现的;
常见的运算符重载方法
方法重载调用__init__构造函数对象建立:X =>所有重载方法的名称前后都有两个下划线字符,以便把同类中定义的变量名区别开来。构造函数和表达式:__init__和__sub__

[*]
  >>>>
[*]  ...   def __init__(self, start):
[*]  ...   self.data = start
[*]  ...   def __sub__(self, other):
[*]  ...   return Number(self.data - other)
[*]  ...
[*]  >>> X = Number(5)
[*]  >>> Y = X - 2
[*]  >>> Y
[*]  <__main__.Number object at 0x10224d550>
[*]  >>> Y.data
[*]  3
索引和分片: __getitem__和__setitem__
  基本索引

[*]
  >>>>
[*]  ...   def __getitem__(self, item):
[*]  ...         return item ** 2
[*]  ...
[*]  >>>
[*]  >>> for i in range(5):
[*]  ...   I = Index()
[*]  ...   print(I, end=' ')
[*]  ...
[*]  0 1 4 9 16
  切片索引

[*]
  >>>>
[*]  ...   data =
[*]  ...   def __getitem__(self, item):
[*]  ...   print('getitem: ', item)
[*]  ...   return self.data
[*]  ...   def __setitem__(self, key, value):
[*]  ...   self.data = value
[*]  ...
[*]  >>> X = Index()
[*]  >>> print(X)
[*]  getitem:slice(1, 4, None)
[*]  
[*]  >>> X = (1, 1, 1)
[*]  >>> print(X)
[*]  getitem:slice(1, 4, None)
[*]  
索引迭代:__getitem__
  如果重载了这个方法,for循环每次循环时都会调用类的getitem方法;

[*]
  >>>>
[*]  ...   def __getitem__(self, item):
[*]  ...         return self.data.upper()
[*]  ...
[*]  >>>
[*]  >>> X = stepper()
[*]  >>> X.data = 'ansheng'
[*]  >>> for item in X:
[*]  ...   print(item)
[*]  ...
[*]  A
[*]  N
[*]  S
[*]  H
[*]  E
[*]  N
[*]  G
迭代器对象:__iter__和__next__

[*]
  >>>>
[*]  ...   def __init__(self, start, stop):
[*]  ...         self.value = start - 1
[*]  ...         self.stop = stop
[*]  ...   def __iter__(self):
[*]  ...         return self
[*]  ...   def __next__(self):
[*]  ...         if self.value == self.stop:
[*]  ...             raise StopIteration
[*]  ...         self.value += 1
[*]  ...         return self.value ** 2
[*]  ...
[*]  >>> for i in Squares(1, 5):
[*]  ...   print(i)
[*]  ...
[*]  1
[*]  4
[*]  9
[*]  16
[*]  25
成员关系:__contains__、__iter__和__getitem__

[*]  class Iters:
[*]  def __init__(self, value):
[*]  self.data = value
[*]
[*]  def __getitem__(self, item):
[*]  print('get[%s]' % item, end='')
[*]  return self.data
[*]
[*]  def __iter__(self):
[*]  print('iter>==', end='')
[*]  self.ix = 0
[*]  return self
[*]
[*]  def __next__(self):
[*]  print('next:', end='')
[*]  if self.ix == len(self.data): raise StopIteration
[*]  item = self.data
[*]  self.ix += 1
[*]  return item
[*]
[*]  def __contains__(self, item):
[*]  print('contains: ', end=' ')
[*]  return item in self.data
[*]
[*]
[*]  X = Iters()
[*]  print(3 in X)
[*]  for i in X:
[*]  print(i, end='|')
[*]
[*]  print()
[*]  print(list(map(bin, X)))
[*]
[*]  I = iter(X)
[*]  while True:
[*]  try:
[*]  print(next(I), end=' @')
[*]  except StopIteration as e:
[*]  break
属性引用:__getattr__和__setattr__
  当通过未定义的属性名称和实例通过点号进行访问时,就会用属性名称作为字符串调用这个方法,但如果类使用了继承,并且在超类中可以找到这个属性,那么就不会触发。

[*]
  >>>>
[*]  ...   def __getattr__(self, item):
[*]  ...         if item == 'age':
[*]  ...             return 40
[*]  ...         else:
[*]  ...             raise AttributeError(item)
[*]  ...
[*]  >>>
[*]  >>> x = empty()
[*]  >>> print(x.age)
[*]  40
[*]  >>> print(x.name)
[*]  Traceback (most recent call last):
[*]  File "<stdin>", line 1, in <module>
[*]  File "<stdin>", line 6, in __getattr__
[*]  AttributeError: name

[*]
  >>>>
[*]  ...   def __setattr__(self, key, value):
[*]  ...         if key == 'age':
[*]  ...             self.__dict__ = value
[*]  ...         else:
[*]  ...             raise AttributeError(key + ' not allowed')
[*]  ...
[*]  >>>
[*]  >>> x = accesscontrol()
[*]  >>> x.age = 40
[*]  >>> print(x.age)
[*]  40
[*]  >>> x.name = 'Hello'
[*]  Traceback (most recent call last):
[*]  File "<stdin>", line 1, in <module>
[*]  File "<stdin>", line 6, in __setattr__
[*]  AttributeError: name not allowed
__repr__和__str__会返回字符串表达式
  __repr__和__str__都是为了更友好的显示,具体来说,如果在终端下print(Class)则会调用__repr__,非终端下会调用__str__方法,且这两个方法只能返回字符串;

[*]  class adder:
[*]  def __init__(self, value=0):
[*]  self.data = value
[*]
[*]  def __add__(self, other):
[*]  self.data += other
[*]
[*]  def __repr__(self):
[*]  return 'addrepr(%s)' % self.data
[*]
[*]  def __str__(self):
[*]  return 'N: %s' % self.data
[*]
[*]
[*]  x = adder(2)
[*]  x + 1
[*]  print(x)
[*]  print((str(x), repr(x)))
右侧加法和原处加法: __radd__和__iadd__
  只有当+右侧的对象是类实例,而左边对象不是类实例的时候,Python才会调用__radd__

[*]  class Commuter:
[*]  def __init__(self, val):
[*]  self.val = val
[*]
[*]  def __add__(self, other):
[*]  print('add', self.val, other)
[*]  return self.val + other
[*]
[*]  def __radd__(self, other):
[*]  print('radd', self.val, other)
[*]  return other + self.val
[*]
[*]
[*]  x = Commuter(88)
[*]  y = Commuter(99)
[*]  print(x + 1)
[*]  print('')
[*]  print(1 + y)
[*]  print('')
[*]  print(x + y)
  使用__iadd__进行原处加法

[*]  class Number:
[*]  def __init__(self, val):
[*]  self.val = val
[*]
[*]  def __iadd__(self, other):
[*]  self.val += other
[*]  return self
[*]
[*]
[*]  x = Number(5)
[*]  x += 1
[*]  x += 1
[*]  print(x.val)
[*]
[*]
[*]  class Number:
[*]  def __init__(self, val):
[*]  self.val = val
[*]
[*]  def __add__(self, other):
[*]  return Number(self.val + other)
[*]
[*]
[*]  x = Number(5)
[*]  x += 1
[*]  x += 1
[*]  print(x.val)
Call表达式:__call__
  当调用类实例时执行__call__方法

[*]  class Callee:
[*]  def __call__(self, *args, **kwargs):
[*]  print('Callee:', args, kwargs)
[*]
[*]
[*]  C = Callee()
[*]  C(1, 2, 3)
[*]  C(1, 2, 3, x=1, y=2, z=3)
[*]
[*]
[*]  class Prod:
[*]  def __init__(self, value):
[*]  self.value = value
[*]
[*]  def __call__(self, other):
[*]  return self.value * other
[*]
[*]
[*]  x = Prod(3)
[*]  print(x(3))
[*]  print(x(4))
比较:__lt__,__gt__和其他方法
  类可以定义方法来捕获所有的6种比较运算符:<、>、<=、>=、==和!=

[*]  class C:
[*]  data = 'spam'
[*]
[*]  def __gt__(self, other):
[*]  return self.data > other
[*]
[*]  def __lt__(self, other):
[*]  return self.data < other
[*]
[*]  x = C()
[*]  print(x > 'han')
[*]  print(x < 'han')
布尔值测试:bool和len

[*]  class Truth:
[*]  def __bool__(self):
[*]  return True
[*]
[*]
[*]  X = Truth()
[*]  if X: print('yes')
[*]
[*]
[*]  class Truth:
[*]  def __bool__(self):
[*]  return False
[*]
[*]  X = Truth()
[*]  print(bool(X))
  如果没有这个方法,Python退而求其次的求长度,因为一个非空对象看作是真:

[*]
  >>>>
[*]  ...   def __len__(self): return 0
[*]  ...
[*]  >>> X = Truth()
[*]  >>> if not X: print('no')
[*]  ...
[*]  no
  如果两个方法都有,__bool__会胜过__len__:

[*]
  >>>>
[*]  ...   def __bool__(self): return True
[*]  ...   def __len__(self): return 0
[*]  ...
[*]  >>> X = Truth()
[*]  >>> bool(X)
[*]  True
  如果两个方法都没有定义,对象毫无疑义的看作为真:

[*]
  >>>>
[*]  ...
[*]  >>> bool(Truth)
[*]  True
对象解析函数:__del__
  每当实例产生时,就会调用init构造函数,每当实例空间被收回时,它的对立面__del__,也就是解析函数,就会自动执行;

[*]  class Life:
[*]  def __init__(self, name='unknown'):
[*]  print('Hello, ', name)
[*]  self.name = name
[*]
[*]  def __del__(self):
[*]  print('Goodbye', self.name)
[*]
[*]
[*]  brian = Life('Brian')
[*]  brian = 'loretta'
页: [1]
查看完整版本: Python面向对象运算符重载