Python进阶01_数据模型

本文探讨了Python数据模型的概念,展示了如何通过实现特殊方法使自定义类的行为更接近内置类型。通过具体示例,如Card类和FrenchDeck类,说明了如何利用特殊方法如__init__、__len__和__getitem__来增强类的功能。此外,还介绍了如何使用__repr__和__str__方法来定制对象的字符串表示,以及如何通过__add__和__mul__等方法实现自定义的算术运算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据模型

python的纸牌风格

通过实现特殊方法来利用 Python 数据模型的两个好处:

  • 作为你的类的用户,他们不必去记住标准操作的各式名称
  • 可以更加方便地利用Python的标准库,比如random.choice 函数,从而不用重新发明轮子
import collections

# namedtuple 用以构建只有少数属性但是没有方法的对象
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]
    

# namedtuple,我们可以很轻松地得到一个纸牌对象
beer_card = Card('7','diamonds')
beer_card
Card(rank='7', suit='diamonds')

# __getitem__ 提供的方法
deck = FrenchDeck()
print(len(deck))
# deck[0]是由 __getitem__ 方法提供
# 如果去掉会报错
print(deck[0])
print(deck[-1])
52
Card(rank='2', suit='spades')
Card(rank='A', suit='hearts')
# 随机抽取一张纸牌 random.choice
from random import choice
choice(deck)
Card(rank='5', suit='hearts')
#切片操作
print(deck[:3])
#先抽出索引是 12 的那张牌,然后每隔 13 张牌拿 1 张
print(deck[12::13])
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
# 可迭代
for card in deck:
    print(card)
    
for card in reversed(deck):
    print(card)
    
# 一个集合类型没有实现 __contains__ 方法,那么 in 运算符就会按顺序做一次迭代搜索
print(Card('Q','hearts') in deck)


# 排序
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)

特殊方法的使用

特殊方法,即便其他程序要使用这个类的这些方法,也不会直接调用它们。如下面的实例。

repr 字符串表示形式

repr 就是通过 repr 这个特殊方法来得到一个对象的字符串表示形式的。
例如下面的例子,会返回Vector(1,2)

  • repr 的实现中,用到了 %r 来获取对象各个属性的标准字符串表示形式
  • repr 所返回的字符串应该准确、无歧义,并且尽可能表达出如何用代码创建出这个被打印的对象。
  • reprstr 的区别在于,后者是在 str() 函数被使用,,repr 是更好的选择

算术运算符

通过__add__ 和__mul__,示例为向量类带来了 + 和 * 这两个算术运算符。

算术运算符的基本原则就是不改变操作对象,而是产出一个新的值。

自定义的布尔值

上任何对象都可以用于需要布尔值的上下文中
我们自己定义的类的实例总被认为是真的,除非这个类对 bool 或者 __
len__ 函数有自己的实现。bool(x) 的背后是调用 x.bool() 的结果;如果不存在 bool 方法,那么 bool(x) 会尝试调用 x.len()。若返回 0,则 bool 会返回 False;否则返回 True。

特殊方法一览

跟运算符无关的特殊方法

跟运算符相关的特殊方法

from math import hypot

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)
v1 = Vector(2,4)
v2 = Vector(1,2)
print(v1+v2)
print(abs(v1))
print(v2 * 2)

Vector(3,6)
4.47213595499958
Vector(2,4)

小结

  • 通过实现特殊方法,自定义数据类型可以表现得跟内置类型一样
  • 通过 repr 和__str__ 来满足字符串表示形式。前者方便我们调试和记录日志,后者则是给终端用户看的
  • 对序列数据类型的模拟是特殊方法用得最多的地方
  • 通过运算符重载这一模式提供了丰富的数值类型

分享关于人工智能,机器学习,深度学习以及计算机视觉的好文章,同时自己对于这个领域学习心得笔记。想要一起深入学习人工智能的小伙伴一起结伴学习吧!扫码上车!

瓦力人工智能 - 扫我吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值