流畅的Python-1
2023-08-03 14:01:48

Chap1. The Python Data Model

The Python interpreter invokes special methods to perform basic object operations, often triggered by special syntax.

e.g. In order to evaluate my_collection[key],the interpreter calls my_collection.__getitem__(key)

magic method is slang for special method

Dunder , i.e. __*__

shortcut for “double underscore before and after

advantages using special methods to leverage the Python Data Model

  • Users of your classes don’t have to memorize arbitrary methods name for standard operations(like len(my_object) ) 类的使用者不需要额外记住一些标准操作的类方法
  • easier to benefit from the rich Python standard library and avoiding reinventing the wheel(like random.choice. this will be shown below)和Python标准库联动避免重复造轮子

Now let’s paly poker 🧐 But first we need a deck of cards

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import collections

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


class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()

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 is used to build classes of objects that are just bundles of attributes with no custom methods, like a database record.

namedtuple具名元组

用来构造只有属性、没有自定义方法的类,就像数据库表的记录

对于具名元组的元素,既可以用索引获取,也可以像获取对象属性一样去点访问

1
2
3
luck_card = Card('12', 'spades')
luck_card.rank = luck_card[0] # 12
luck_card.suit = luck_card[1] # spades

By implementing the special methods __len__ and __getitem__, FrenchDeck behaves like a standard Python sequence. 实现了这两个魔术方法,FrenchDeck将表现出标准的Python序列的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
deck = FrenchDeck()
len(deck) # 52 触发__len__

# __getitem__让类对象可以像集合一样操作

deck[0] # Card(rank='2', suit='spades')
deck[-1] # Card(rank='A', suit='hearts')

# slicing
deck[:3] # 获取前三张卡片

# 直接使用Python标准库
from random import choice
choice(deck) # 随机选择一张卡片

# for 循环 iterable
for card in deck:
print(card)

for card in reversed(deck):
print(card)

# in 操作符
# if collections has no __contains__ method the in operator does a sequential scan
Card(rank='10', suit='spades') in deck

# sorted
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)


def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank)
return rank_value * len(suit_values) + suit_values[card.suit]


for card in sorted(deck, key=spades_high):
print(card)

More usages about special methods:

  • Emulating Numeric Types
  • String Representation of an Object
  • Boolean Value of an Object

Here is a simple two-dimensional vector class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import math


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

def __repr__(self):
return f'Vector({self.x!r}, {self.y!r})'

def __abs__(self):
return math.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)


vector1 = Vector(3, 4)
vector2 = Vector(2, 1)
vector1 + vector2 # Vector(5, 5)
abs(vector1) # 5.0
vector1 * 3 # Vector(9, 12)

默认情况下,用户定义的类都是永真,除非类实现了__bool____len__

一般情况下,bool(x)调用x.__bool__(),如果__bool__没有实现,尝试调用x.__len__,若len返回0,则bool(x)返回False

这里若向量的大小为0,则返回Flase

2023-08-03 14:01:48
Next