许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  《Fluent Python》笔记:数据模型详解

《Fluent Python》笔记:数据模型详解

阅读数 1
点赞 0
article_banner

数据模型

python 最好的品质之一就是一致性,当使用python工作一会儿之后,就可以正确的猜出语句的意思。而这都归功于python的数据 模型  

数据模型其实是对 python框架  的描述,它规范了这门语言自身 构建模块 的接口,这些模块包括序列、迭代器、函数、类和上下文管理器。

python解释器遇到特殊句法时,会使用特殊方法激活对象的基本操作,这些特殊方法的格式为:__xxx__。比如obj[key]背后的方法其实是调用了obj.__getitem__(key)

这种特殊方法,可以使我们自己的对象,实现和支持以下的语言框架,并与之交互:迭代、集合类、属性访问、运算符重载、函数和方法调用、对象创建和销毁、字符串表示形式和格式化、管理上下文。



一、一副纸牌

接下来以实现一摞纸牌的例子,来展示如何实现__getitem____len__这两个特殊方法,见识到特殊方法的强大。

import collections
from random import choice

Card = namedTuple('Card',['rank', 'suit'])    # 定义类型Card

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

    def __init__(self):
        self._card = [Card(rank, suit) for suit in self.suits
                                       for rank in self.ranks]
    def __len__(self):
        return self.len(_card)
    def __getitem__(self,pos):
        return self._card[pos]

obj = CardGenerator()
print(len(obj))          # 获取长度
print(obj[0])            # 获取第一张牌
print(obj[-5:-3])        # 切片
print(choice(obj))       # 随机获取一张牌
print(obj[12::13])       # 先抽索引为12的那张牌,之后每13张抽一张牌

for card in reversed(obj):     # 反向遍历
    print(card)

首先,我们定义了一个有些像C结构体的类型Card,用来描述一张牌。然后,定义了CardGenerator类,用来描述一副纸牌。

我们都知道,自定义类型在没有重载运算符的情况下,不能够输入输出,不能够用标准库中的方法,更不可以迭代。而CardGenerator类,巧妙的运用了列表和定义特殊方法,只用了几行代码就使CardGenerator对象变成了一个,可获取长度的、可访问的、可迭代的、可以使用标准库方法的对象。

suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
def card_weight(card):
    rank_values = CardGenerator.ranks.index(card.rank)
    return rank_values * len(suit_values) + suit_values[card.suit]

for card in sorted(obj,key=card_weight):
    print card

在定义了每张牌的权值之后,也可以通过调用sorted函数对其进行排序。



二、如何使用特殊方法

首先我们需要明确,特殊方法的存在是为了python解释器调用的,而不是我们自己调用。就比如上述代码中的len(obj),实际上是解释器调用了由我们自己实现的 CardGenerator类中的__len__()特殊方法。

而对于python的内置类型如:list、str、bytearray等,len(list)实际上不会调用__len__(),而是会抄近路去调用,由C语言实现的PyVarObject结构体中的ob_size属性。直接调用这个属性会比调用函数快很多哦~

一般而言,许多特殊方法都是隐式调用的,比如for i in obj:这个语句,其实隐式的调用了obj.__iter__()特殊方法。并且大多数特殊方法是通过len、str这些内置函数调用的。

对于熟悉C++的各位,可以把python的特殊方法理解为虚函数

1. 模拟数值类型

我们通过实现一个二维向量类Vector来看一看数值类型是如何定义的。

from math import hypot   # 引入取模函数

class Vector2(object):

    def __init__(self, x, y):
        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 __add__(self, other):
        return Vector2(self.x + other.x, self.y + other.y)

    def __mul__(self, scalar):
        return Vector2(self.x * scalar, self.y * scalar)

    def __bool__(self):
        return bool(abs(self))

首先,值得一提的是,我们定义了__repr__()方法,该方法返回一个对象的字符串以描述这个对象。在没有定义这个方法之前,我们在控制台输入print(Vector2(1, 2))会得到对象的地址,而不是输出对象的内容。在定义了这个特殊方法之后,输入同样内容,在没有定义__str__()的情况下,解释器会调用repr()内置函数,而repr内置函数则会调用对象的__repr__()方法。

之后,我们还定义了__bool__()方法,当我们调用bool()函数时,__bool__()特殊方法便会被调用。对于一个新的数值类型,难免会在条件语句中进行判断,因此__bool__()方法必不可少。当我们需要一个类型的bool值时,python解释器首先会看该类型内有没有__bool__()方法,如果没有则会根据__len__()方法的返回值确定bool值。



3. 总结
  • 特殊方法是为了使自定义类型表现的与内置类型一样而存在的。
  • 特殊方法是python解释器调用的,基本不需要自行调用
  • 特殊方法的调用栈一般为:某函数->解释器自动调用内置函数->内置函数调用特殊方法


关于str与repr
class test(object):
    def __init__(self, val):
        self.val = val
    def __str__(self):
        return 'test(%r)' % (self.val)

a = test(10)

a
>>> <__main__.a at 0x7fa91c314e50>
print(a)
>>> test(10)


class test(object):
    def __init__(self, val):
        self.val = val
    def __repr__(self):
        return 'test(%r)' % (self.val) 

a = test(10)

a
>>> test(10)
print(a)
>>> test(10)

可以看出,对于__str__()而言,输入对象名并不执行该函数,输出的依然是对象地址,输入print时才会执行。

当我们输入print(obj)时,python解释器首先调用str()内置函数,str()调用__str__()特殊方法。

如果当前类没有定义__str__()特殊方法,那么解释器会调用repr()内置函数,调用__repr__()特殊方法。

可以看出,str是处于外层的,repr是处于底层的,若是定义了__str__()__repr__()会被覆盖。因此,str一般面向用户,而repr面向程序员。


免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删


相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空