《Python面向对象编程指南》——1.11 多策略的__init__()方法

简介:

本节书摘来自异步社区《Python面向对象编程指南》一书中的第1章,第1.11节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.11 多策略的__init__()方法

有些对象的创建来自多个来源。例如,我们也许需要克隆一个对象作为备忘录模式的一部分,或者冻结一个对象以使它可以用来作为字典的键或放入哈希集合;这也是set和fronezenset类的实现方式。

有很多全局的设计模式使用了多种方式来创建对象。其中一个为多策略初始化,__init__()函数的实现逻辑较为复杂,也会用到类层次结构中不同的(静态)构造函数。

它们是非常不同的实现方式,在接口的定义上就有根本区别。


4dbcaecc61f0fbad0b1eb8570b440847be77d43c

之前的例子可以被高效的克隆是因为它们非常简单,在下一章中会进行展开描述。然而,为了更详细地说明更多关于对象克隆的基本技巧,我们会讨论一下如何把可变的Hand对象冻结成为不可变的Hand对象。

以下代码演示了两种创建Hand对象的例子。

class Hand3:
   def __init__( self, *args, **kw ):
     if len(args) == 1 and isinstance(args[0],Hand3):
       # Clone an existing hand; often a bad idea
       other= args[0]
       self.dealer_card= other.dealer_card
       self.cards= other.cards
     else:
       # Build a fresh, new hand.
       dealer_card, *cards = args
       self.dealer_card= dealer_card
       self.cards= list(cards)

第1种方式,Hand3实例从已有的Hand3对象创建。第2种方式,Hand3对象的创建基于Card实例。

一个fronzenset对象的创建可以基于已有的实例,或基于已存在集合对象。下一章会具体介绍创建不可变对象。基于已有的Hand,创建一个Hand对象,可用于创建一个Hand对象的备忘录模式,例如下面这段实现。

h = Hand( deck.pop(), deck.pop(), deck.pop() )
memento= Hand( h )

我们使用mem ento变量来保存Hand对象。可以用来比较当前对象和之前被处理的对象,我们也可以冻结它用于集合或映射。

1.11.1 更复杂的初始化方式

为了将多策略应用于初始化,通常要被迫放弃显式命名的参数。这样的设计虽然获得了灵活性,却使得参数名不够透明,意图不够明显,需要针对不同的使用场景分别提供文档进行解释说明。

也可以扩展初始化的实现来分离Hand对象。要分离的Hand对象只需修改构造函数。以下代码段演示了如何分离一个Hand对象。

class Hand4:
   def __init__( self, *args, **kw ):
     if len(args) == 1 and isinstance(args[0],Hand4):
       # Clone an existing handl often a bad idea
       other= args[0]
       self.dealer_card= other.dealer_card
       self.cards= other.cards
     elif len(args) == 2 and isinstance(args[0],Hand4) and 'split' 
in kw:
       # Split an existing hand
       other, card= args
       self.dealer_card= other.dealer_card
       self.cards= [other.cards[kw['split']], card]
     elif len(args) == 3:
       # Build a fresh, new hand.
       dealer_card, *cards = args
       self.dealer_card= dealer_card
       self.cards= list(cards)
     else:
       raise TypeError( "Invalid constructor args={0!r} 
kw={1!r}".format(args, kw) )
   def __str__( self ):
     return ", ".join( map(str, self.cards) )

这个设计需要传入更多的纸牌对象来创建合适的、分离的Hand对象。当我们从一个Hand4对象中分离出另一个Hand4对象时,使用split参数作为索引从原Hand4对象中读取Card对象。以下代码演示了我们怎样分离出一个Hand对象。

d = Deck()
h = Hand4( d.pop(), d.pop(), d.pop() )
s1 = Hand4( h, d.pop(), split=0 )
s2 = Hand4( h, d.pop(), split=1 )

我们初始化了一个Hand4类的实例然后再分离出其他的Hand4实例,命名为s1和s2,然后将Card对象传入每个Hand对象。在21点的规则中,只有当手中两张牌大小相等的时候才可允许分牌。可以看到__init__()函数的逻辑已经非常复杂,优势在于,它可以基于已有集合同时创建多个像fronzenset这样的对象。然而也将需要更多的注释和文档来说明这些行为。

1.11.2 静态函数的初始化

当我们有多种方式来创建一个对象时,有时使用静态函数好过使用复杂的__init__()函数。

也可以考虑使用类函数作为初始化的另一种选择,然而将依赖的对象作为参数传入函数会更好。当冻结或分离一个Hand对象时,我们或许希望创建两个新的静态函数来完成任务。使用静态函数作为代理构造函数在语法上略有差别,但是在代码的组织上却有明显的优势。

以下是Hand类的实现,使用了静态函数来完成初始化,从已有的Hand实例创建两个新实例。

class Hand5:
   def __init__( self, dealer_card, *cards ):
     self.dealer_card= dealer_card
     self.cards = list(cards)
   @staticmethod
   def freeze( other ):
     hand= Hand5( other.dealer_card, *other.cards )
     return hand
   @staticmethod
   def split( other, card0, card1 ):
     hand0= Hand5( other.dealer_card, other.cards[0], card0 )
     hand1= Hand5( other.dealer_card, other.cards[1], card1 )
     return hand0, hand1
   def __str__( self ):
     return ", ".join( map(str, self.cards) )

使用一个函数完成了冻结和备忘录模式,用另一个函数将Hand5对象分离为两个子实例。

这样既可以增强可读性,也不必使用参数名称来解释接口意图。

以下代码段演示了我们如何把Hand5对象进行分离:

d = Deck()
h = Hand5( d.pop(), d.pop(), d.pop() )
s1, s2 = Hand5.split( h, d.pop(), d.pop() )

我们创建了一个Hand5类的h实例,把它分为另外两个Hand实例,名为s1和s2,然后分别为它们赋值。而使用__init__()函数实现同样的功能时,split()静态函数的实现版本简化了很多。然而它并没有遵守一个原则:使用已有的set对象来创建fronzenset对象。

相关文章
|
1月前
|
机器学习/深度学习 Python
堆叠集成策略的原理、实现方法及Python应用。堆叠通过多层模型组合,先用不同基础模型生成预测,再用元学习器整合这些预测,提升模型性能
本文深入探讨了堆叠集成策略的原理、实现方法及Python应用。堆叠通过多层模型组合,先用不同基础模型生成预测,再用元学习器整合这些预测,提升模型性能。文章详细介绍了堆叠的实现步骤,包括数据准备、基础模型训练、新训练集构建及元学习器训练,并讨论了其优缺点。
57 3
|
20天前
|
安全
Python-打印99乘法表的两种方法
本文详细介绍了两种实现99乘法表的方法:使用`while`循环和`for`循环。每种方法都包括了步骤解析、代码演示及优缺点分析。文章旨在帮助编程初学者理解和掌握循环结构的应用,内容通俗易懂,适合编程新手阅读。博主表示欢迎读者反馈,共同进步。
|
1月前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
28天前
|
JSON 安全 API
Python调用API接口的方法
Python调用API接口的方法
148 5
|
1月前
|
算法 决策智能 Python
Python中解决TSP的方法
旅行商问题(TSP)是寻找最短路径,使旅行商能访问每个城市一次并返回起点的经典优化问题。本文介绍使用Python的`ortools`库解决TSP的方法,通过定义城市间的距离矩阵,调用库函数计算最优路径,并打印结果。此方法适用于小规模问题,对于大规模或特定需求,需深入了解算法原理及定制策略。
43 15
|
1月前
|
机器学习/深度学习 人工智能 算法
强化学习在游戏AI中的应用,从基本原理、优势、应用场景到具体实现方法,以及Python在其中的作用
本文探讨了强化学习在游戏AI中的应用,从基本原理、优势、应用场景到具体实现方法,以及Python在其中的作用,通过案例分析展示了其潜力,并讨论了面临的挑战及未来发展趋势。强化学习正为游戏AI带来新的可能性。
107 4
|
1月前
|
Python
Python编程中的魔法方法(Magic Methods)
【10月更文挑战第40天】在Python的世界中,魔法方法就像是隐藏在代码背后的神秘力量。它们通常以双下划线开头和结尾,比如 `__init__` 或 `__str__`。这些方法定义了对象的行为,当特定操作发生时自动调用。本文将揭开这些魔法方法的面纱,通过实际例子展示如何利用它们来增强你的类功能。
22 1
|
1月前
|
算法 数据处理 开发者
超越传统:Python二分查找的变种策略,让搜索效率再上新台阶!
本文介绍了二分查找及其几种Python实现的变种策略,包括经典二分查找、查找第一个等于给定值的元素、查找最后一个等于给定值的元素以及旋转有序数组的搜索。通过调整搜索条件和边界处理,这些变种策略能够适应更复杂的搜索场景,提升搜索效率和应用灵活性。
38 5
|
1月前
|
Python
不容错过!Python中图的精妙表示与高效遍历策略,提升你的编程艺术感
本文介绍了Python中图的表示方法及遍历策略。图可通过邻接表或邻接矩阵表示,前者节省空间适合稀疏图,后者便于检查连接但占用更多空间。文章详细展示了邻接表和邻接矩阵的实现,并讲解了深度优先搜索(DFS)和广度优先搜索(BFS)的遍历方法,帮助读者掌握图的基本操作和应用技巧。
39 4
|
1月前
|
算法 IDE API
Python编码规范与代码可读性提升策略####
本文探讨了Python编码规范的重要性,并深入分析了如何通过遵循PEP 8等标准来提高代码的可读性和可维护性。文章首先概述了Python编码规范的基本要求,包括命名约定、缩进风格、注释使用等,接着详细阐述了这些规范如何影响代码的理解和维护。此外,文章还提供了一些实用的技巧和建议,帮助开发者在日常开发中更好地应用这些规范,从而编写出更加清晰、简洁且易于理解的Python代码。 ####