《Python面向对象编程指南》——1.8 复合的组合对象

1.8 复合的组合对象

为了描述21点游戏中的发牌。以下代码定义了Hand类,用来模拟打牌策略。

class Hand:
  def __init__( self, dealer_card ):
    self.dealer_card= dealer_card
    self.cards= []
  def hard_total(self ):
    return sum(c.hard for c in self.cards)
  def soft_total(self ):
    return sum(c.soft for c in self.cards)

在本例中,定义了一个self.dealer_card变量,值由__init__()函数传入。可self.cards变量不基于任何参数来赋值,只是创建了一个空集合。使用如下代码可以创建一个Hand实例。

d = Deck()
h = Hand( d.pop() )
h.cards.append( d.pop() )
h.cards.append( d.pop() )

可是这段代码有个缺陷,需要用好几行代码来构造一个Hand对象。不但给序列化Hand对象带来了困难,而且再次创建对象又需要再重复以上过程。尽管再添加一个append()函数暴露给外面调用,也仍然需要很多步骤来创建集合对象。

可能会考虑使用流畅接口,但那样并不能简化实际问题。它只是在创建Hand对象的语法上做了一些改变。流畅接口依然会需要多个步骤来创建对象。在第2部分“持久化和序列化”中,我们需要一个接口完成类之间的调用,可以通过类中的一个函数完成,而这个函数最好是构造函数。在第9章“序列化和保存 ——JSON、YAML、Pickle、CSV和XML”中会详细深入介绍。

可以注意到hard_total函数和soft_total函数并没有完全符合21点的规则。在第2章“与Phthon无缝集成——基本特殊方法”中会对这个问题进行讨论。
完成组合对象的初始化

__init__()初始化方法应当返回一个完整的对象,这样是理想的情况。而这样也带来了一些复杂性,因为要创建的对象内部可能包含了集合,集合里面又包含了其他对象。如果可以一步完成对象创建的工作这样是最好的。

通常考虑使用一个流畅接口来完成逐个将对象添加到集合的操作,同时将集合对象作为构造函数的参数来完成初始化。

例如,如下代码段对类的实现。

class Hand2:
  def __init__( self, dealer_card, *cards ):
    self.dealer_card= dealer_card
    self.cards = list(cards)
  def hard_total(self ):
    return sum(c.hard for c in self.cards)
  def soft_total(self ):
    return sum(c.soft for c in self.cards)

代码中的初始化函数中完成了所有变量实例的赋值操作。其他函数的实现都是从上一个Hand类的版本中复制过来的。此处可以用两种方式来创建Hand2对象。第1种是一次加载一张牌。

d = Deck()
P = Hand2( d.pop() )
p.cards.append( d.pop() )
p.cards.append( d.pop() )

第2种使用*cards参数一次加载多张牌。

d = Deck()
h = Hand2( d.pop(), d.pop(), d.pop() )

第2种初始化方式给单元测试代码带来了便利,构造一个复合对象只需一步。更重要的是,一步构造复合对象也有利于后续要介绍的序列化。

时间: 2024-07-29 10:31:49

《Python面向对象编程指南》——1.8 复合的组合对象的相关文章

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

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

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

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

《Python面向对象编程指南》——1.7 简单的组合对象

1.7 简单的组合对象 一个组合对象也可以称作容器.我们会从一个简单的组合对象开始介绍:一副牌.这是一个基本的集合对象.我们的确可以简单地使用一个list来代替一副牌(deck)对象. 在设计一个类之前,我们需要考虑这样的一个问题:简单地使用list是合适的做法吗? 可以使用random.shuffle()函数完成洗牌操作,使用deck.pop()来完成发牌操作. 一些程序员可能会过早定义新类,正如像使用内置类一样,违反了一些面向对象的设计原则.比如像下面的这个设计. d= [card6(r+1

《Python面向对象编程指南》——1.9 不带__init__()方法的无状态对象

1.9 不带__init__()方法的无状态对象 以下是一个不需要__init__()方法的类定义.对于策略模式的对象来说这是常见的设计.一个策略对象以插件的形式复合在主对象上来完成一种算法或逻辑.它或许依赖主对象中的数据,策略对象自身并不携带任何数据.通常策略类会和享元设计模式一起使用:在策略对象中避免内部存储.所有需要的值都从策略对象的方法参数传入.策略对象自身是无状态的,可以把它看作是一系列函数的集合. 这里定义了一个类给Player实例提供游戏模式的选择,以下这个策略包括了拿牌和下调投注

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

2.7 __del__()方法 __del__()方法有一个让人费解的使用场景. 这个方法的目的是在将一个对象从内存中清除之前,可以有机会做一些清理工作.如果使用上下文管理对象或者with语句来处理这种需求会更加清晰,这也是第5章"可调用对象和上下文的使用"的内容.对于Python的垃圾回收机制而言,创建一个上下文比使用__del__()更加容易预判. 但是,如果一个Python对象包含了一些操作系统的资源,__del__()方法是把资源从程序中释放的最后机会.例如,引用了一个打开的文

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

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

《Python面向对象编程指南》——1.12 更多的__init__()技术

1.12 更多的__init__()技术 我们再来看一下其他一些更高级的__init__()技术的应用.相比前面的介绍,它们的应用场景不是特别常见. 以下是Player类的定义,初始化使用了两个策略对象和一个table对象.这个__init__()函数看起来不够漂亮. class Player: def __init__( self, table, bet_strategy, game_strategy ): self.bet_strategy = bet_strategy self.game_

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

2.3 __hash__()方法 内置的hash( )函数默认调用了__hash__()方法.哈希是一种将相对复杂的值简化为小整数的计算方式.理论上说,一个哈希值可以表示出源值的所有位.还有一些其他的哈希方法,会得出非常大的值,这样的算法通常用于密码学. Python中有两个哈希库.其中,hashlib可以提供密码级别的哈希函数,zlib模块包含两个高效的哈希函数:adler32()和crc32().对于相对简单的值,我们不使用这些内置的函数,对于复杂的或者很大的值,这些内置的函数可以提供很大的

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

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