翻译
5 Unknown Tricks for Python Classes

Python 有许多弱小的个性,在解决类时提供了极大的灵活性。 在这里,我将向你展现五个高级个性,它们能够帮忙你编写更好的代码。

创立一个常量值

假如咱们正在创立一个 Circle 类。 咱们可能须要一种计算面积和一种计算周长的办法:

class Circle():    def __init__(self, radius):        self.radius = radius      def area(self):        return 3.14 * self.radius * self.radius       def perimeter(self):        return 2 * 3.14 * self.radius

这个实现仿佛可行,然而如果咱们想将 pi 的值更改为更准确怎么办? 咱们须要在两个中央更改它。 一个更好的主见是将 pi 的值保留在一个变量中:

class Circle():    def __init__(self, radius):        self.radius = radius        self.pi = 3.14        def area(self):        return self.pi * self.radius * self.radius       def perimeter(self):        return 2 * self.pi * self.radius

然而,当初任何人都能够更改 pi 的值。 这可不是什么坏事:如果用户编写如下内容怎么办:

c = Circle(4)c.pi = 5

这将毁坏咱们所有的性能! 显然,咱们必须对此做些什么。 可怜的是,Python 不容许创立常量。 咱们能够应用一个函数,该函数带有标签 @property,并返回 pi。 这样,函数就能够像变量一样被调用而无需括号。

class Circle():    def __init__(self, radius):        self.radius = radius       @property    def pi(self):        return 3.14    def area(self):        return self.pi * self.radius * self.radius       def perimeter(self):        return 2 * self.pi * self.radius

当初 pi 的值不能被用户更改,它只存储在一个中央。

多个类构造函数

在 Python 中,类的构造函数是用 __init__ 办法定义的。 可怜的是,不容许函数重载,因而咱们只能定义一个这样的函数。 然而,有一种办法能够应用相似于类构造函数的办法。 这能够应用 @classmethod 标签来实现。

例如,假如咱们正在创立一个 Date 类 。 第一个构造函数是:

class Date():    def __init__(self, day, month, year):        self.day = day        self.month = month        self.year = year

当初咱们可能想要增加另一个构造函数,从 dd/mm/yyyy 格局的字符串中获取日期。 咱们能够在 Date 类中创立一个类办法:

@classmethoddef fromString(obj, s):    day = int(s[:2])    month = int(s[3:5])    year = int(s[6:])    return obj(day, month, year) # Return a new object

当初咱们能够通过调用 fromString 办法创立一个新的 Date 对象:

d = Date.fromString("21/07/2020")print(d.day, d.month, d.year)Output: 21 7 2020

创立枚举

假如你的程序中有一个代表星期几的变量。 你可能会将它示意为一个整数,例如星期一是 1,星期二是 2 等等。然而,这不是最佳的,因为你必须记住你是从 0 开始还是从 1 开始。

此外,当你打印该值时,它将只是一个整数,因而你须要一个函数将其转换为正确的名称。

侥幸的是,咱们能够应用 enum 库中的 Enum 类。 当你创立 Enum 的子类时,它蕴含一组变量,每个变量都有本人的值,你能够在程序中应用这些变量。 例如,在前一种状况下,你将编写如下内容:

from enum import Enumclass WeekDay(Enum):    Monday = 1    Tuesday = 2    Wednesday = 3    Thursday = 4    Friday = 5    Saturday = 6    Sunday = 7

当初将变量设置为值 Friday 你能够写

day = WeekDay.Friday

并且你还能够获取 Friday 示意的整数值:

intDay = day.value # This will be 5

迭代器

迭代器是容许你迭代数据结构中的所有元素的类。 迭代器的一个例子是 range 函数:你能够迭代某个范畴内的所有整数值(例如,通过应用 for 循环)。

如果你想创立你本人的迭代器,你只须要实现 __next__ __iter__ 办法。

  • __iter__ 应该返回迭代器对象(所以它在大多数状况下返回 self)
  • __next__ 应该返回数据结构的下一个元素

假如咱们正在创立一个迭代器来向后循环遍历一个列表。 正确的做法是:

class Backward():    def __init__(self, data):        self.data = data # The list we want to iterate         self.index = len(self.data)        def __iter__(self):        return self        def __next__(self):        if(self.index == 0):            raise StopIteration        else:            self.index -= 1            return self.data[self.index]

如你所见,如果没有更多元素要迭代, __next__ 函数应该引发 StopIteration 谬误。 否则,它将返回下一个要查看的元素。 当初咱们能够在 for 循环中应用 Backward 对象:

bw = Backward([1,2,3,4,5])for elem in bw:    print(elem)

输入后果将是:

54321

以列表的模式拜访类

当咱们应用列表时,咱们能够应用方括号拜访特定的值。 可能为咱们本人的类实现相似的货色会很好。 咱们能够应用 __getitem____setitem__ 来做到这一点。 当咱们应用方括号时,列表会调用以下办法:

  • x = l[idx] 等价 x = l.__getitem__(idx)
  • l[idx] = x 等价 l.__setitem__(idx, x)

所以如果咱们在咱们的类中实现这两个办法,咱们就能够像应用列表一样应用方括号。

这是一个非常简单的例子(它是一个大小不能扭转的列表,其索引从 1 开始):

class MyList():    def __init__(self, dimension):        self.l = [0 for i in range(dimension)]        def __getitem__(self, idx):            return self.l[idx-1]        def __setitem__(self, idx, data):            self.l[idx-1]=dataml = MyList(5)ml[1] = 50 # Set the first element of ml to 50ml[2] = 100 # Set the second element of ml to 100x = ml[1] # x is now 50

总结

  • 应用 @property 创立常量
  • 应用 @classmethod 创立多个类构造函数
  • 应用 Enum 类创立枚举
  • 应用 __next__ 办法创立迭代器
  • 像解决列表一样拜访类,应用 __setitem____getitem__