《Python面向对象编程指南》——2.5 __bytes__()方法

2.5 __bytes__()方法

只有很少的情景需要我们把对象转换为字节。在第2部分“持久化和序列化”中,我们会详细探讨这个主题。

通常,应用程序会创建一个字符串,然后使用Python的IO类内置的编码方法将字符串转换为字节。对于大多数情况,这种方法就足够了。只有当我们自定义一种新的字符串时,我们会需要定义这个字符串的编码方法。

依据不同的参数,bytes()函数的行为也不同。

  • bytes(integer):返回一个不可变的字节对象,这个对象包含了给定数量的0x00值。

    • bytes(string):这个版本会将字符串编码为字节。其他的编码和异常处理的参数会定义编码的具体过程。
    • bytes(something):这个版本会调用something.__bytes__()创建字节对象。这里不用编码或者错误处理参数。

基本的object对象没有定义__bytes__()。这意味着所有的类在默认情况下都没有提供__bytes__()方法。

在一些特殊情况下,在写入文件之前,我们需要将一个对象直接编码成字节。通常使用字符串并且使用str类型为我们提供字符串的字节表示会更简单。要注意,当操作字节时,没有什么快捷方式可以解码文件或者接口中的字节。内置的bytes类只能解码字符串,对于我们的自定义对象,是无法解码的。在这种情况下,我们需要解析从字节解码出来的字符串,或者我们可以显式地调用struct模块解析字节,然后基于解析出来的值创建我们的自定义对象。

下面我们来看看如何把Card编码和解码为字节。由于Card只有52个可能的值,所以每一张牌都应该作为一个单独的字节。但是,我们已经决定用一个字符表示suit,用另外一个字符表示rank。此外,我们还需要适当地重构Card的子类,所以我们必须对下面这些项目进行编码。

  • Card的子类(AceCard、NumberCard、FaceCard)。
  • 子类的__init__()参数。
    

注意,我们有一些__init__()方法会将一个数值类型的rank转换为一个字符串,导致丢失了原始的数值。为了使字节编码可逆,我们需要重新创建rank的原始数值。

下面是__bytes__()的一种实现,返回了Card、rank和suit的UTF-8编码。

def __bytes__( self ):
   class_code= self.__class__.__name__[0]
   rank_number_str = {'A': '1', 'J': '11', 'Q': '12', 'K': '13'}.get( self.rank, self.rank )
   string= "("+" ".join([class_code, rank_number_str, self.suit,] ) + ")"
   return bytes(string,encoding="utf8")

这种实现首先用字符串表示Card对象,然后将字符串编码为字节。这通常是最简单也是最灵活的方法。

当我们拿到一串字节时,我们可以将这串字节解码为一个字符串,然后将字符串转换为一个新的Card对象。下面是基于字节创建Card对象的方法。

def card_from_bytes( buffer ):
   string = buffer.decode("utf8")
   assert string[0 ]=="(" and string[-1] == ")"
   code, rank_number, suit = string[1:-1].split()
   class_ = { 'A': AceCard, 'N': NumberCard, 'F': FaceCard }[code]
   return class_( int(rank_number), suit )

在上面的代码中,我们将字节解码为一个字符串。然后我们将字符串解析为数值。基于这些值,现在我们可以重建原始的Card对象。

我们可以像下面这样生成一个Card对象的字节表示。

b= bytes(someCard)

然后我们可以用生成的字节重新创建Card对象。

someCard = card_from_bytes(b)

需要特别注意的是,通常自己定义字节表示是非常有挑战性的,因为我们试图表示一个对象的状态。Python中已经内置了很多字节表示的方式,通常这些方法足够我们使用了。

如果需要定义一个对象底层的字节表示方式,最好使用pickle或者json模块。在第9章“序列化和保存——JSON、YAML、Pickle、CSV和XML”中,我们会详细探讨这个主题。

时间: 2024-11-05 06:20:06

《Python面向对象编程指南》——2.5 __bytes__()方法的相关文章

《Python面向对象编程指南》——第1部分 用特殊方法实现Python风格的类 第1章 __init__()方法 1.1 隐式的基类——object

第1部分 用特殊方法实现Python风格的类 init()方法 与Python无缝集成--基本特殊方法 属性访问.特性和修饰符 抽象基类设计的一致性 可调用对象和上下文的使用 创建容器和集合 创建数值类型 装饰器和Mixins--横切方面 用特殊方法实现 Python风格的类 通过重写特殊方法来完成对Python内部机制的调用,在Python中是很普遍的.例如len()函数就可以重写一个类的__len__()方法. 这意味着对于像(len(x))这样的通用公共接口,任何类(例如,声明一个类叫ti

《Python面向对象编程指南》——2.6 比较运算符方法

2.6 比较运算符方法 Python有6个比较运算符.这些运算符分别对应一个特殊方法的实现.根据文档,运算符和特殊方法的对应关系如下所示. x < y调用x.__lt__(y). x <=y调用x.__le__(y). x == y调用x.__eq__(y). x != y调用x.__ne__(y). x > y调用x.__gt__(y). x >= y调用x.__ge__(y). 我们会在第7章"创建数值类型"中再探讨比较运算符. 对于实际上使用了哪个比较运算

《Python面向对象编程指南》——导读

前 言 本书主要介绍Python语言的高级特性,特别是如何编写高质量的Python程序.这通常意味着编写高性能且拥有良好可维护性的程序.同时,我们也会探究不同的设计方案并确定究竟是哪种方案提供了最佳性能.而对于一些正在寻找解决方案的问题,这也是一种很好的方式. 本书的大部分内容将介绍一种给定设计的不同替代方案.一些方案性能更好,另一些方案更加简单或者更加适合于特定领域的问题.最重要的是,找到最好的算法和最优的数据结构,以最少的开销换取最大的价值.时间就是金钱,高效的程序会为它们的用户创造更多的价

《Python面向对象编程指南》——1.5 通过工厂函数调用__init()__

1.5 通过工厂函数调用__init()__ 我们可以使用工厂函数来完成所有Card对象的创建,这比枚举52张牌的方式好很多.在Python中,实现工厂有两种途径. 定义一个函数,返回不同类的对象. 定义一个类,包含了创建对象的方法.这是完整的工厂设计模式,正如设计模式书中提到的.在类似Java这样的语言里,工厂类层次结构是必需的,因为语言本身不支持可以脱离类而单独存在的函数. 在Python里,类定义不是必需的.仅当特别复杂的情形,工厂类才是不错的选择.Python的优势之一是,对于只需要简单

《Python面向对象编程指南》——第2章 与Python无缝集成——基本特殊方法 2.1 __repr__()和__str__()方法

第2章 与Python无缝集成--基本特殊方法 Python中有一些特殊方法,它们允许我们的类和Python更好地集成.在标准库参考(Standard Library Reference)中,它们被称为基本特殊方法,是与Python的其他特性无缝集成的基础. 例如,我们用字符串来表示一个对象的值.Object基类包含了__repr__()和__str__()的默认实现,它们提供了一个对象的字符串描述.遗憾的是,这些默认的实现不够详细.我们几乎总会想重写它们中的一个或两个.我们还会介绍__form

《Python面向对象编程指南》——2.8 __new__()方法和不可变对象

2.8 __new__()方法和不可变对象 __new__方法的一个用途是初始化不可变对象.__new__()方法中允许创建未初始化的对象.这允许我们在__init__()方法被调用之前先设置对象的属性. 由于不可变类的__init__()方法很难重载,因此__new__方法提供了一种扩展这种类的方法. 下面是一个错误定义的类,我们定义了float的一个包含单位信息的版本. class Float_Fail( float ): def __init__( self, value, unit ):

《Python面向对象编程指南》——2.2 __format__()方法

2.2 __format__()方法 string.format()和内置的format()函数都使用了__format__()方法.它们都是为了获得给定对象的一个符合要求的字符串表示. 下面是给__format__()传参的两种方式. someobject.__format__(""):当应用程序中出现format(someobject)或者"{0}".format(someobject)时,会默认以这种方式调用__format__().在这些情况下,会传递一个空

《Python面向对象编程指南》——1.3 在基类中实现init()方法

1.3 在基类中实现init()方法 通过实现__init()__方法来初始化一个对象.每当创建一个对象,Python会先创建一个空对象,然后调用该对象的__init()__函数.这个方法提供了对象内部变量以及其他一些一次性过程的初始化操作. 以下是关于一个Card类层次结构定义的一些例子.这里定义了一个基类和3个子类来描述Card类的基本信息.有两个变量是参数直接赋值的,另外两个参数是通过初始化方法计算来完成初始化的. class Card: def __init__( self, rank,

《Python面向对象编程指南》——1.4 使用__init()__方法创建常量清单

1.4 使用__init()__方法创建常量清单 我们可以为所有卡片的花色单独创建一个类.可在21点应用中,花色不是很重要,用一个字母来代替就可以. 这里使用花色的初始化作为创建常量对象的一个实例.很多情况下,应用会包括一个常量集合.静态常量也正构成了策略(Strategy)或状态(State)模式的一部分. 有些情况下,常量会在应用或配置文件的初始化阶段被创建.或者创建变量的行为是基于命令行参数的.我们会在第16章"使用命令行"中介绍应用初始化和启动的详细设计过程. Python中并