The Python Data Model

Chung-Yi
程式設計之旅
Published in
8 min readNov 30, 2019

介紹

這個章節主要介紹python中的一些magic methods,顧名思義就是擁有魔法一樣,不需要去重新定義或架構一個方法,直接使用即可。例如,假如有一個a = [1, 2, 3],想知道它的長度,可以直接使用len(a)=3,這是因為type(a)是list,list這個物件的class裡面已經有定義 __len__這個function,所以只要使用len()就直接呼叫__len__這個function。這些magic method的命名方式都是用__x__。

若想知道某個class有哪些magic methods,可以這個程式碼查看,例如想知道int class的magic methods

dir(int)

可以得到下面的magic methods

['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

關於magic methods介紹可以參考:https://www.tutorialsteacher.com/python/magic-methods-in-python

建立一個 class A Pythonic Card Deck

以下是以撲克牌為例子:

class FrenchDeck:    ranks = [str(n) for n in range(2, 11)] + list(    'JQKA')]  # ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J',     
'Q', 'K', 'A']
suits = 'spades diamonds clubs hearts'.split() # ['spades',
'diamonds', 'clubs', 'hearts']
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits for
rank in self.ranks]

def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]

接下來使用collections.namedtuple來定義一個class,collections.namedtuple的好處是可以為class命名,增加可讀性

import collectionsCard = collections.namedtuple('Card', ['rank', 'suit'])print(Card)

可以得到:

Card(rank=’7', suit=’diamonds’)

接下來實體化FrenchDeck(),透過len(deck)可以得到撲克牌的所有花色及大小

deck = FrenchDeck()print(len(deck))

長度為52

這邊可以使用len()是因為在class FrenchDeck有定義__len__,若是將__len__拿掉會得到:

TypeError: object of type 'FrenchDeck' has no len()

若是使用 index查看deck裡的element:

print(deck[0])

可以得到Card(rank=’2', suit=’spades’),這也是因為class FrenchDeck有定義__getitem__

Card(rank=’2', suit=’spades’)

deck[:3]

[Card(rank=’2', suit=’spades’), Card(rank=’3', suit=’spades’), Card(rank=’4', suit=’spades’)]

for card in deck:
print(card)

Card(rank=’A’, suit=’hearts’) Card(rank=’K’, suit=’hearts’) Card(rank=’Q’, suit=’hearts’)

所以說當有一個字串’123’或是一個[1, 2, 3]可以使用len()或是index就是因為class string, class list有__len__及__getitem_,可以用dir(str), dir(list)查看。

How Special Methods Are Used

這些magic methods都有一個共通點,就是它是透過Python interpreter主動呼叫,而不是透過使用者自己呼叫,所以上面的例子我們只需要使用len(),而不需要deck.__len__()。但是其實我們在寫程式時,不太需要去直接去呼叫這些特殊的方法,有一個例外的方法就是__init__(),相信對於class有一點概念的大概都會知道,使用__init__()目的是為了做初始化的動作,可以看上面的class FrenchDeck,當實體化一個物件時,它會呼叫__init__()。

盡量避免隨意定義特殊方法,其實命名特殊發法也是一門學問,在這邊就不做介紹,詳細內容可以參考:https://aji.tw/python%E4%BD%A0%E5%88%B0%E5%BA%95%E6%98%AF%E5%9C%A8__%E5%BA%95%E7%B7%9A__%E4%BB%80%E9%BA%BC%E5%95%A6/

Emulating Numeric Types

首先定義一個class Vector

class Vector:    def __init__(self, x=0, y=0):        self.x = x        self.y = y
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
def __abs__(self):
return hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self, other): x = self.x + other.x y = self.y + other.y return Vector(x, y) def __mul__(self, scalar): return Vector(self.x * scalar, self.y * scalar)
Figure 1–1. Example of two-dimensional vector addition; Vector(2, 4) + Vector(2, 1) re‐ sults in Vector(4, 5)
v = Vector(3, 4)

Vector(3, 4)

abs(v)

5.0,這邊可以跟前面相呼應,直接使用abs() Python interpreter就會去呼叫__abs__()這個special method

v1 = Vector(2, 4)

Vector(2, 4)

v2 = Vector(2, 1)

Vector(2, 1)

v3 = v1 + v2

Vector(4, 5)

Overview of Special Methods

--

--

Chung-Yi
程式設計之旅

我思故我在。跨領域的麻瓜工程師,希望透過文字跟你/妳交流分享