編寫高質量python代碼:通過

想象有這麼一個應用場景:你想編寫一個多邊形的基類,並在子類繼承它的時候,要驗證子類的邊數是否大於等於 3 。

在python3.6之前,通用的做法是通過編寫元類來驗證子類:

<code>class ValidatePolygon(type):
def __new__(meta, name, bases, class_dict):
if bases:
if class_dict['sides'] < 3:
raise ValueError('Polygons need 3+ sides')
return type.__new__(meta, name, bases, class_dict)

class Polygon(metaclass=ValidatePolygon):
sides = None

@classmethod
def interior_angles(cls):
return (cls.sides - 2) * 180

class Triangle(Polygon):
sides = 3

class Rectangle(Polygon):
sides = 4

class Nonagon(Polygon):
sides = 9

assert Triangle.interior_angles() == 180
assert Rectangle.interior_angles() == 360
assert Nonagon.interior_angles() == 1260/<code>

但是上面的寫法有兩個不好的地方:

  • 四個字:難寫,難讀。
  • 直接多繼承會出現問題,只有通過一定的繼承技巧才不出現問題。

python3.6之後,引入了 __init_subclass__,這個時候上面的寫法就可以變成:

<code>class BetterPolygon: 

sides = None

def __init_subclass__(cls):
super().__init_subclass__()
if cls.sides < 3:
raise ValueError('Polygons need 3+ sides')

@classmethod
def interior_angles(cls):
return (cls.sides - 2) * 180

class Hexagon(BetterPolygon):
sides = 6

assert Hexagon.interior_angles() == 720/<code>

當需要多繼承的時候:

<code>class Ploygon:
sides = None

def __init_subclass__(cls):
super().__init_subclass__()
print(f'Ploygon for {cls}')
if cls.sides < 3:
raise ValueError('Ploygons need 3+ sides')

@classmethod
def interior_angles(cls):
return (cls.sides - 2) * 180


class Filled:
color = None

def __init_subclass__(cls):
super().__init_subclass__()
if cls.color not in ('red', 'green', 'blue'):
raise ValueError('Fills need a valid color for red, green and blue')


class RedTriangle(Filled, Ploygon):
color = 'red'
sides = 3/<code>

下面來驗證一下,當指定的邊數和填充顏色不合法的時候會出現什麼情況。

邊數:

<code>print('Before class')

class BlueLine(Filled, Polygon):
color = 'blue'
sides = 2

print('After class')

>>>
Before class
Traceback ...
ValueError: Polygons need 3+ sides/<code>

填充顏色:

<code>print('Before class')

class BeigeSquare(Filled, Polygon):
color = 'beige'
sides = 4
print('After class')

>>>
Before class
Traceback ...
ValueError: Fills need a valid color for red, green and blue/<code>

要點

  • python3.6開始就可以通過 __init_subclass__ 驗證子類了。
  • 在__init_subclass__中要調用super().__init_subclass__()


分享到:


相關文章: