特殊方法将改变您在 Python 中编写类的方式

每个人都知道__init__ 方法的作用。 它是一个内置方法,每次创建新对象时都会调用它。 然而,这并不是 Python 为我们创建的唯一特殊方法。 还有更多对于创建更强大的类非常有用的特殊方法。

在整篇文章中,我将使用一个类 Point 作为示例,它表示一个 二维 点。 这是__init__方法:

class Point:  
  def __init__(self, x, y):  
    self.x = x  
    self.y = y

1、类的描述

当我们想要表示一个类时,有两个特殊的方法会被调用:__repr__(self)__str__(self)

它们不同的用法:

  • __repr__ 理想情况下应该返回一个对象,该对象可用于重新创建类的当前状态。 返回的对象不必是字符串。 但是,如果它是一个字符串,则它的格式应该为 "<..description..>"
  • 另一方面,__str__ 必须始终返回一个字符串。 每当我们将对象转换为字符串时(例如,当我们使用 print 时),就会调用这个方法。

默认情况下,__str__ 方法定义为:

def __str__(self):  
  return self.__repr__(self):

所以我们只能重写 __repr__ ,只要它返回一个字符串。 例如,我们的 Point 类:

def __repr__(self):  
  return "<PointObject:x=" + str(self.x) + ",y=" + str(self.y) + ">"

但是如果我们想在调用 __repr__ 时返回一个不同的类型(比如元组)怎么办? 然后我们需要重写这两个方法:

def __repr__(self):  
  return (self.x, self.y)def __str__(self):  
  return "Point(" + str(self.x) + ", "+ str(self.y) + ")"

2、检查是否相等

你有没有想过用 == 来检查两个实例是否相等?

每次写 a == b 时,内部调用的方法是 a.__eq__(b)。 所以为了能够比较自定义类的对象,我们只需要重写这个方法。 让我们看看怎么做:

def __eq__(self, other):  
  if not isinstance(other, Point):  
    return False  
  return self.x == other.x and self.y == other.y

第一行用于检查另一个对象是否也是类 Point 的实例。 如果不是,该方法将始终返回 False。 否则,该方法将检查两个点的坐标是否相同。

还有另一种方法, __ne__ ,每当我们使用 != 时都会调用它。 但是,我们不需要实现它,因为默认情况下它使用 __eq__ 的定义来返回正确的结果。

3、比较不同的对象

现在我们可以检查两个对象是否相等,也许我们更想要对它们进行排序。 但要做到这一点,我们必须能够使用运算符 < 、 <= 、 > 、 >= 。 让我们看看它们在幕后是如何工作的。
与相等运算符一样,每当我们比较两个对象时,都会调用一些内置函数。 它们是:__lt____le____gt____ge__。 所以我们需要重写这四个方法。 下面是我们如何为我们的 Point 类实现 总排序

def __gt__(self, other):  
  if not isinstance(other, Point):  
    raise TypeError  
  return self.x > other.x or (self.x == other.x and self.y > self.y)

def __lt__(self, other):  
  if not isinstance(other, Point):  
    raise TypeError  
  return self.x < other.x or (self.x == other.x and self.y < self.y)

def __ge__(self, other):  
 # Defined recursively  
 return self > other or self == other

def __le__(self, other):  
  # Defined recursively  
  return self < other or self == other

请注意:

  1. 您可以定义其中之一,然后以递归方式指定其他,但这会使代码变慢。
  2. 你必须已经定义了 __eq__ ,否则这些方法将不起作用。

4、添加和减去对象

现在我们可能想对我们的对象进行一些算术运算。 这些也可以使用五种特殊方法来实现:

  • 当我们使用 a+b 时会调用 __add__(self, other)
  • 当我们使用 a-b 时会调用 __sub__(self, other)
  • 当我们使用 a*b 时会调用 __mul__(self, other)
  • 当我们使用 a/b 时会调用 __truediv__(self, other)
  • 当我们使用 a b a^b ab 时会调用 __pow__(self, other)

让我们看看如何加减平面中的两个点:

def __add__(self, other):  
  if not isinstance(other, Point):  
    raise TypeError  
  return Point(self.x+other.x, self.y+other.y)def __sub__(self, other):  
  if not isinstance(other, Point):  
    raise TypeError  
  return Point(self.x-other.x, self.y-other.y)

请记住始终检查其他对象是否具有正确的类型。 此外,这些方法应该更改任何现有对象:它们必须返回正确类型的新对象。

5、使类可散列

假设您想将您的类用作字典中的键。 如果你这样尝试,你会得到这样的错误:

TypeError: unhashable type: 'Point'

事实上,如果一个对象 t 是可散列的,则对象是可以被用作字段中的键的。 但是如何使对象可散列呢? 您需要实现 __hash__(self) 方法。

最简单的方法是创建一个包含定义对象的所有字段的元组,然后返回其哈希值。 请注意,用于散列的属性应该是常量。 因此,我们还需要重新定义 __init__ 方法以将 xy 设为私有。 此外,定义__hash__ 的类也必须定义__eq__

class Point:  
  def __init__(self, x, y):  
    self.__x = x  
    self.__y = y
  
  @property    
  def x(self):  
    return self.__x 
  
  @property  
  def y(self):  
    return self.__y 
  
  def __eq__(self, other):  
    if not isinstance(other, Point):  
      return False  
    return self.x == other.x and self.y == other.y 
  
  def __hash__(self):  
    return hash((self.x, self.y))

6、对象的长度

我们知道如何使用 len(x) 方法来获取列表的长度。 但是如果我们想用它来获取自定义对象的长度呢? 在这种情况下,我们需要重写 __len__(self) 方法。 唯一的限制是它必须是整数。

例如,我们希望将“Point”对象的长度定义为距原点的曼哈顿距离。 所以我们可以实现这个方法:

def __len__(self):  
  return abs(self.x)+abs(self.y)

现在我们可以使用 len 方法来获取曼哈顿距离

>>> len( Point(1, -3) )  
4

7、转换为布尔值

有时我们想将我们的类用作布尔值。 例如,如果一个点不是原点,或者它在某个区域之外,则它可能是“真”。 为此,我们将对象转换为 bool,我们使用 bool(Point(1, 2))。 这个方法在内部调用了 __bool__(self) 方法。 所以我们需要重写这个方法来创建一个自定义行为:

def __bool__(self):  
  # 如果该点是原点,则返回 False   
  return self.x != 0 or self.y != 0

注意:,如果定义了 __len__ 而未定义 __bool__,则布尔值实现为 len(self) > 0

翻译自:https://python.plainenglish.io/special-methods-that-will-change-how-you-build-classes-in-python-cd0226b52eb6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值