• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >python > 详细解析Python中__init__()方法的高级应用

详细解析Python中__init__()方法的高级应用

作者: 字体:[增加 减小] 来源:互联网

通过本文主要向大家介绍了python中 init 方法,python init 方法,python中 init,python super init,python init等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

通过工厂函数对 __init__() 加以利用

我们可以通过工厂函数来构建一副完整的扑克牌。这会比枚举所有52张扑克牌要好得多,在Python中,我们有如下两种常见的工厂方法:

  •     定义一个函数,该函数会创建所需类的对象。
  •     定义一个类,该类有创建对象的方法。这是一个完整的工厂设计模式,正如设计模式书所描述的那样。在诸如Java这样的语言中,工厂类层次结构是必须的,因为该语言不支持独立的函数。
  • 在Python中,类并不是必须的。只是当有相关的工厂非常复杂的时候才会显现出优势。Python的优势就是当一个简单的函数可以做的更好的时候我们决不强迫使用类层次结构。
  •     虽然这是一本关于面向对象编程的书,但函数真是一个好东西。这在Python中是常见的也是最地道的。

如果需要的话,我们总是可以将一个函数重写为适当的可调用对象。我们可以将一个可调用对象重构到我们的工厂类层次结构中。我们将在第五章《使用可调用对象和上下文》中学习可调用对象。

一般,类定义的优点是通过继承实现代码重用。工厂类的函数就是包装一些目标类层次结构和复杂对象的构造。如果我们有一个工厂类,当扩展目标类层次结构的时候,我们可以添加子类到工厂类中。这给我们提供了多态性工厂类;不同的工厂类定义具有相同的方法签名,可以交替使用。

这类水平的多态性对于静态编译语言如Java或C++非常有用。编译器可以解决类和方法生成代码的细节。

如果选择的工厂定义不能重用任何代码,则在Python中类层次结构不会有任何帮助。我们可以简单的使用具有相同签名的函数。

以下是我们各种Card子类的工厂函数:

def card(rank, suit):
  if rank == 1:
    return AceCard('A', suit)
  elif 2 <= rank < 11: 
    return NumberCard(str(rank), suit)

  elif 11 <= rank < 14:
    name = {11: 'J', 12: 'Q', 13: 'K' }[rank]
    return FaceCard(name, suit)
  else:
    raise Exception("Rank out of range")

</div>

这个函数通过数值类型的rank和suit对象构建Card类。我们现在可以非常简单的构建牌了。我们已经封装构造问题到一个单一的工厂函数中,允许应用程序在不知道精确的类层次结构和多态设计是如何工作的情况下进行构建。

下面是一个如何通过这个工厂函数构建一副牌的示例:

deck = [card(rank, suit) for rank in range(1,14) for suit in (Club, Diamond, Heart, Spade)]

</div>

它枚举了所有的牌值和花色来创建完整的52张牌。
1. 错误的工厂设计和模糊的else子句

注意card()函数里面的if语句结构。我们没有使用“包罗万象”的else子句来做任何处理;我们只是抛出异常。使用“包罗万象”的else子句会引出一个小小的辩论。

一方面,从属于else子句的条件不能不言而喻,因为它可能隐藏着微妙的设计错误。另一方面,一些else子句确实是显而易见的。

重要的是要避免含糊的else子句。

考虑下面工厂函数定义的变体:

def card2(rank, suit):
  if rank == 1: 
    return AceCard('A', suit)
  elif 2 <= rank < 11: 
    return NumberCard(str(rank), suit)
  else:
    name = {11: 'J', 12: 'Q', 13: 'K'}[rank]
    return FaceCard(name, suit)

</div>

以下是当我们尝试创建整副牌将会发生的事情:

deck2 = [card2(rank, suit) for rank in range(13) for suit in (Club, Diamond, Heart, Spade)]

</div>

它起作用了吗?如果if条件更复杂了呢?

一些程序员扫视的时候可以理解这个if语句。其他人将难以确定是否所有情况都正确执行了。

对于高级Python编程,我们不应该把它留给读者去演绎条件是否适用于else子句。对于菜鸟条件应该是显而易见的,至少也应该是显示的。

何时使用“包罗万象”的else

尽量的少使用。使用它只有当条件是显而易见的时候。当有疑问时,显式的并抛出异常。

避免含糊的else子句。
2. 简单一致的使用elif序列

我们的工厂函数card()是两种常见工厂设计模式的混合物:

  •     if-elif序列
  •     映射

为了简单起见,最好是专注于这些技术的一个而不是两个。

我们总是可以用映射来代替elif条件。(是的,总是。但相反是不正确的;改变elif条件为映射将是具有挑战性的。)

以下是没有映射的Card工厂:

def card3(rank, suit):
  if rank == 1: 
    return AceCard('A', suit)
  elif 2 <= rank < 11: 
    return NumberCard(str(rank), suit)
  elif rank == 11:
    return FaceCard('J', suit)
  elif rank == 12:
    return FaceCard('Q', suit)
  elif rank == 13:
    return FaceCard('K', suit)
  else:
    raise Exception("Rank out of range")

</div>

我们重写了card()工厂函数。映射已经转化为额外的elif子句。这个函数有个优点就是它比之前的版本更加一致。
3. 简单的使用映射和类对象

在一些示例中,我们可以使用映射来代替一连串的elif条件。很可能发现条件太复杂,这个时候或许只有使用一连串的elif条件来表达才是明智的选择。对于简单示例,无论如何,映射可以做的更好且可读性更强。

因为class是最好的对象,我们可以很容易的映射rank参数到已经构造好的类中。

以下是仅使用映射的Card工厂:

 def card4(rank, suit):
  class_= {1: AceCard, 11: FaceCard, 12: FaceCard, 13: FaceCard}.get(rank, NumberCard)
  return class_(rank, suit)

</div>

我们已经映射rank对象到类中。然后,我们传递rank值和suit值到类来创建最终的Card实例。

最好我们使用defaultdict类。无论如何,对于微不足道的静态映射不会比这更简单了。看起来像下面代码片段那样:

defaultdict(lambda: NumberCard, {1: AceCard, 11: FaceCard, 12: FaceCard, 12: FaceCard})

注意:defaultdict类默认必须是零参数的函数。我们已经使用了lambda创建必要的函数来封装常量。这个函数,无论如何,都有一些缺陷。对于我们之前版本中缺少1到A和13到K的转换。当我们试图增加这些特性时,一定会出现问题的。

我们需要修改映射来提供可以和字符串版本的rank对象一样的Card子类。对于这两部分的映射我们还可以做什么?有四种常见解决方案:

  1.     可以做两个并行的映射。我们不建议这样,但是会强调展示不可取的地方。
  2.     可以映射个二元组。这个同样也会有一些缺点。
  3.     可以映射到partial()函数。partial()函数是functools模块的一个特性。
  4.     可以考虑修改我们的类定义,这种映射更容易。可以在下一节将__init__()置入子类定义中看到。

我们来看看每一个具体的例子。
3.1. 两个并行映射

以下是两个并行映射解决方案的关键代码:

class_= {1: AceCard, 11: FaceCard, 12: FaceCard, 13: FaceCard}.get(rank, NumberCard)
rank_str= {1:'A', 11:'J', 12:'Q', 13:'K'}.get(rank, str(rank))
return class_(rank_str, suit)

</div>

这并不可取的。它涉及到重复映射键1、11、12和13序列。重复是糟糕的,因为在软件更新后并行结构依然保持这种方式。

不要使用并行结构

并行结构必须使用元组或一些其他合适的集合来替代。
3.2. 映射到元组的值

以下是二元组映射的关键代码:

class_, rank_str= {
  1: (AceCard,'A'),
  11: (FaceCard,'J'),
  12: (FaceCard,'Q'),
  13: (FaceCard,'K'),
}.get(rank, (NumberCard, str(rank)))
return class_(rank_str, suit)

</div>

这是相当不错的。不需要过多的代码来分类打牌中的特殊情况。当我们需要改变Card类层次结构来添加额外的Card子类时,我们将看到它如何被修改或被扩展。

将rank值映射到一个类对象的确让人感觉奇怪,且只有类初始化所需两个参数中的其中之一。将牌值映射到一个简单的类或没有提供一些混乱参数(但不是所有)的函数对象似乎会更合理。
3.3. partial函数解决方案

相比映射到二元组函数和参数之一,我们可以创建一个partial()函数。这是一个已经提供一些(但不是所有)参数的函数。我们将从functools库中使用partial()函数来创建一个带有rank参数的partial类。

以下是一个映射rank到partial()函数,可用于对象创建:

from functools import partial
part_class= {
  1: partial(AceCard, 'A'),
  11: partial(FaceCard, 'J'),
  12: partial(FaceCard, 'Q'),
  13: partial(FaceCard, 'K'),

  


 

您可能想查找下面的文章:

  • 详细解析Python中__init__()方法的高级应用
  • 详细解读Python中的__init__()方法
  • 详细解读Python中的__init__()方法

相关文章

  • Python中logging模块的用法实例
  • python简单的函数定义和用法实例
  • Python制作爬虫采集小说
  • python 合并文件的具体实例
  • python中使用urllib2获取http请求状态码的代码例子
  • Django的session中对于用户验证的支持
  • 将Python的Django框架与认证系统整合的方法
  • win7 下搭建sublime的python开发环境的配置方法
  • 用Python写飞机大战游戏之pygame入门(4):获取鼠标的位置及运动
  • Python爬取qq music中的音乐url及批量下载

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • Python采用Django开发自己的博客系统
    • Python中logging模块的用法实例
    • python实现的简单FTP上传下载文件实例
    • 读写json中文ASCII乱码问题的解决方法
    • python中list列表的高级函数
    • 对于Python中RawString的理解介绍
    • python类和函数中使用静态变量的方法
    • Python中的map()函数和reduce()函数的用法
    • Django框架下在视图中使用模版的方法
    • 用Python的Django框架完成视频处理任务的教程

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有