《Python面向对象编程指南》——1.10 一些其他的类定义

1.10 一些其他的类定义

正如前面所提到的,玩家有两种策略:下注和打牌。每个Player实例会和模拟器进行很多交互。我们这里把这个模拟器命名为Table类。

Table类的职责需要配合Player实例完成以下事件。

  • 玩家必须基于玩牌策略初始化一个牌局。
  • 随后玩家会得到一手牌。
  • 如果手中的牌是可以拆分的,玩家需要在基于当前玩法的情况下决定是否分牌。这会创建新的Hand对象。在一些场合中,新分出去的牌是可以再分的。
  • 对于每个Hand实例,玩家必须基于当前玩法决定叫牌、双倍还是停叫。
  • 然后玩家会收到账单,他们可以根据输赢情况来决定之后的游戏策略。

基于以上需求,我们可以看出Table类需要提供一些API函数来获取牌局、创建Hand对象、分牌、提供单手和多手策略以及支付,这个对象的职责很多,用于追踪与Players集合所有相关操作的状态。

以下是Table类中投注和牌的逻辑处理的相关代码。

class Table:
   def __init__( self ):
     self.deck = Deck()
   def place_bet( self, amount ):
     print( "Bet", amount )
   def get_hand( self ):
     try:
       self.hand= Hand2( d.pop(), d.pop(), d.pop() )
       self.hole_card= d.pop()
     except IndexError:
       # Out of cards: need to shuffle.
       self.deck= Deck()
       return self.get_hand()
     print( "Deal", self.hand )
     return self.hand
   def can_insure( self, hand ):
     return hand.dealer_card.insure

Table类会被Player类调用,从而接受牌局、创建Hand对象,然后决定手中的牌是否为保险下注。此外,还需要提供一些可以被Player类用来获取牌和支付的函数。

在get_hand()函数中的异常处理部分,并没有准确的模拟玩牌时的真实场景。这可能会导致统计不正确。更好的模拟方式是,在牌用尽的情况下需要新建一副牌并洗牌,而不是抛出异常。

为了更适当地交互设计并模拟真实的游戏场景,Player类需要一个下注策略。下注策略是一个状态对象,它决定了初始的下注级别,通常当每局游戏输赢之后可以再次选择不同的下注策略。

理想情况下,希望有多个下注策略对象。Python中有一个模块包含了很多装饰器,可以用来创建抽象基类。一种非正式的创建策略对象的方式是在基类函数中抛出异常,用以标识一些方法必须在子类中提供实现。

以下代码包含了一个抽象基类和一个子类,用来定义一种下注策略。

class BettingStrategy:
   def bet( self ):
     raise NotImplementedError( "No bet method" )
   def record_win( self ):
     pass
   def record_loss( self ):
     pass
class Flat(BettingStrategy):
   def bet( self ):
     return 1

基类中定义了带有默认返回值的方法。抽象基类中的bet()方法抛出异常,子类必须给出 bet()方法的实现。其他方法可以选择是否使用基类的默认实现。前面给出的游戏策略加上这个下注策略,可以模拟出Play类中更复杂的__init__()函数的使用场景。

我们可以使用abc模块来丰富抽象基类的实现,如以下代码段所示。

import abc
class BettingStrategy2(metaclass=abc.ABCMeta):
   @abstractmethod
   def bet( self ):
     return 1
   def record_win( self ):
     pass
   def record_loss( self ):
     pass

它有两个好处:首先,它阻止了对抽象基类BettingStrategy2的实例化,其次任何没有提供bet()方法实现的子类也是不能被实例化的。如果我们试图创建一个类的实例,而这个类并没有提供抽象方法的实现,程序就会抛出一个异常。

当然,如果基类的抽象方法提供了实现,那么就是合法的,而且可以通过super().bet()来调用。

时间: 2024-10-24 07:33:34

《Python面向对象编程指南》——1.10 一些其他的类定义的相关文章

《Python面向对象编程指南》——1.2 基类中的__init__()方法

1.2 基类中的__init__()方法 对象的生命周期主要包括了创建.初始化和销毁.后面章节会详细讨论对象的创建和销毁,本章专注于对象的初始化. object作为所有类的基类,已经为__init__()方法提供了默认实现,一般情况下不需要重写这个函数.如果没有对它进行重写,那么在创建对象时将不会产生其他变量的实例.在某些情况下,这种默认行为是可以接受的. 对于继承自object的子类,总可以对它的属性进行扩展.例如,对于下面这个类,实例化就不对函数(area)所需要的变量(width和leng

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

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

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

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

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

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

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

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

《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.9 new()方法和元类型

2.9 new()方法和元类型 __new__()方法的另一种用途,作为元类型的一部分,主要是为了控制如何创建一个类.这和之前的如何用__new__()控制一个不可变对象是完全不同的. 一个元类型创建一个类.一旦类对象被创建,我们就可以用这个类对象创建不同的实例.所有类的元类型都是type,type()函数被用来创建类对象. 另外,type()函数还可以被用作显示当前对象类型. 下面是一个很简单的例子,直接使用type()作为构造器创建了一个新的但是几乎完全没有任何用处的类: Useless=

《Python面向对象编程指南》——2.10 总结

2.10 总结 我们已经介绍了许多基本的特殊方法,它们是我们在设计任何类时的基本特性.这些方法已经包含在每个类中,只是它们的默认行为不一定能满足我们的需求. 我们几乎总是需要重载__repr__().__str__().和__format__().这些方法的默认实现不是非常有用. 我们几乎不需要重载__bool__()方法,除非我们想自定义集合.这是第6章"创建容器和集合"的主题. 我们常常需要重载比较运算符和__hash__()方法.默认的实现只适合于比较简单不可变对象,但是不适用于

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

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