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