<p>这里有一些事情要考虑。你知道吗</p>
<p>如果你要写一个游戏,包含一个生物表(比如神奇宝贝)和一堆属性(名字,生命值范围,等等),那么你可能需要围绕这些数据结构来构建你的代码。让数据为您使用的对象提供详细信息。你知道吗</p>
<p>下面是一些简单的代码重组示例:</p>
<pre><code>#!python
# would normally be: #!/usr/bin/env python
from __future__ import print_function
from collections import namedtuple, OrderedDict
if hasattr(__builtins__, 'raw_input'):
input = raw_input
menu_header = '''
********PLAYER 1 POKEMON CHOICE********
'''
menu_item = '''
{p.choice} {p.name}
HP: between {p.low} - {p.high}
ATTACKS:
'''
pokemon = namedtuple('pokemon', ['choice', 'name', 'low', 'high', 'results'])
stats = (
(1, 'Bulbasaur', 500, 1000, 'results for %s'),
(2, 'Charmander', 100, 500, 'different results for %s'),
(3, 'Squirtle', 250, 750, "%s's results"),
(4, 'Pikachu', 100, 600, 'and so on for %s'),
)
army = OrderedDict()
for each in stats:
army[each[0]] = pokemon(*each)
menu = menu_header + ''.join([menu_item.format(p=x) for x in army.values()])
if __name__ == '__main__':
choice = -1
while choice not in (1,2,3,4):
choice = raw_input(menu)
try:
choice = int(choice)
except ValueError as e:
print('Unable to parse choice as an integer: %s' % choice)
print('Please Try again:')
continue
print(army[choice].results % army[choice].name)
</code></pre>
<p>在这个例子中,如果你想掌握Python,我会做很多你想学的事情。首先,我有一个“shebang”行,用于Unix、Linux、macosx和其他类似Unix的系统,将脚本文件与可以执行它的解释器相关联。第一个#!这是StackOverflow渲染引擎的一行。它看到了这一点,并将颜色语法高亮显示添加到我的代码中。下一个注释行显示了一个更典型的“shebang”,这在大多数系统中都可以找到。你知道吗</p>
<p>(Python用户通常使用Posix规范中的<code>env</code>实用程序来查找用户路径上的Python解释器。这允许用户配置他们的帐户和shell会话来选择他们首选的Python版本和安装。。。这对于维护它们对语言环境的所有依赖关系和定制非常有用)。你知道吗</p>
<p>关于<code>from __future__ import</code>的那行。。。在向python3或更高版本(未来)过渡时,是否有处理Python2的方法。在早期版本的Python中,<code>print</code>是一个保留关键字,用于语句中。在Python的现代版本中,<code>print()</code>是一个可以在表达式中使用的函数(并且可以过度使用)。你知道吗</p>
<p>我们将立即讨论我从<a href="https://docs.python.org/2/library/collections.html" rel="nofollow noreferrer">collections</a>导入的这些类。你知道吗</p>
<p>接下来的几行代码还提供了Python2和该语言的现代版本之间的向后兼容性。这有点模糊,因为现实世界的程序很少使用<code>input()</code>或<code>raw_input()</code>(现代编程中的大多数输入是从GUI对话框函数、文件或套接字、web服务器提供的特殊“请求”对象或通过其他api应用程序编程接口读取的)。你知道吗</p>
<p>但这些to行基本上允许您按您可能的意图调用<code>input()</code>。旧的<code>input()</code>函数实际上是一种读取输入并用解释器解析输入的方法。在将数据传递给解释器评估之前,最好使用<code>raw_input()</code>并分别对数据执行健全性检查。(事实上,将用户输入传递给编程解释器进行评估几乎总是一个坏主意。。。但是Python的旧语义是其早期的一个遗留问题(主要用于初级编程教学)。在较新版本的Python中<code>input()</code>的行为与旧的<code>raw_input()</code>类似,如果您打算这样做,模拟旧语义的方法是调用<code>eval(input())</code>)。你知道吗</p>
<p>现在,我导入的<strong>namedtuple</strong>和<strong>OrderedDict</strong>功能将为我们提供围绕一个描述不同类型神奇宝贝的表格构建程序的方法。你知道吗</p>
<p>元组是一种简单的、不可变的数据结构。在Python之外,这个术语最常用于指数据库中的“行”(或SQL查询返回的行)。但是使用Python元组可能有点乏味,因为您必须使用索引(从每个元组开始的数字偏移量)访问各个字段(“数据库表中的列”)。你知道吗</p>
<p>名为tuple的<strong>允许定义一种轻量级对象,其行为类似于tuple(具有tuple的性能和内存消耗优势),但也允许我们按字段的名称访问字段。你知道吗</p>
<p>与许多面向对象编程和脚本语言一样,普通Python对象具有“属性”(如字段),可以使用“点”操作符访问这些属性。所以“矩形”对象的“高度”属性可以作为我的矩形.heig好的。你知道吗</p>
<p>这正是我们访问<code>pokemon</code>namedtuples属性的方法。当我实例化它们时,我必须为每个字段提供所有的值:choice、name、low和high HP limits等等。(事实上,我将结果namedtuple“class”的名称作为第一个参数传递给<code>namedtuple()</code>工厂函数是一个怪癖,它与如何在解释器中、调试期间等表示这种类型的对象有关)。你知道吗</p>
<p>如果你通读剩下的代码,你会看到我有一个“stats”表,这是一个元组的元组。这是一个相当紧凑的方法,可以提供一个充满数据的小表。但它也很好地映射到我们的程序的一些假设的未来版本中,我们在数据库中存储一万种Pokémon类型,并让我们的游戏加载它们作为其初始化的一部分(甚至只是加载给定会话的一部分。。。当它们出现时动态地获取它们)。你知道吗</p>
<p>然后我创建了我的神奇宝贝军队作为一个“字典”(这样我就可以很容易地查找任何成员的菜单选择号码)。但是我把它做成了一个“OrderedDict”——一种字典,它保持了词条的添加顺序。通常我们不需要按特定的顺序保存字典;这些数据结构通常以某种内部排列方式进行维护,这种排列方式经过优化,可以通过字典的“键”进行快速访问(我们使用菜单选项作为键;更常见的排列方式可能使用名称作为键)。你知道吗</p>
<p>我只需要两行代码就可以填充我的军队。如果你在你的“统计”表中增加10000个条目也没关系。。。你可以用同样的两条线来填充整个军队。你知道吗</p>
<p>这就是我所说的围绕数据表动态构建代码的意思。你知道吗</p>
<p>这两行中有一个棘手的部分:我的每个<code>pokemon()</code>轻量级对象都需要用五个参数来调用。但是我正在迭代我的硬编码列表(tuple)中的所有条目。第:<code>army[each[0]] = pokemon(*each)</code>行中的*是Python中的一种特殊语法形式,它将函数“应用”到包含一系列值的变量上(Ruby和JavaScript中也有类似的特性)。你知道吗</p>
<p>基本上,这意味着<code>pokemon(*each)</code>的计算就像我调用了<code>pokemon(each[0], each[1], each[2], ...)</code>(对于namedtuple,这就像我调用了<code>pokemon(each.choice, each.name, each.low, ...)</code>)。你知道吗</p>
<p>我可以用“元组解包”(这是一种“解构”)来写:</p>
<pre><code>for ch, nm, lo, hi, res in stats:
army[ch] = pokemon(ch, nm, lo, hi, res)
</code></pre>
<p>。。。这将完成同样的事情。但它更冗长。只有当我需要添加或删除一些数据“列”或在实例化我的每个部队成员之前对这些字段(列)做其他事情时,这才有意义。你知道吗</p>
<p>一旦我有了我的军队,我就简单地用它来制作菜单。我取菜单标题并添加一个通过连接菜单项创建的大字符串。每个菜单项都是通过使用string<a href="https://pyformat.info/" rel="nofollow noreferrer">.format()</a>方法“呈现”(填充)一个小模板来创建的。你知道吗</p>
<p>在Python中还有许多其他方法可以完成这类工作。Python中有许多非常复杂的模板呈现引擎,它们被广泛使用。。。尤其是在web应用程序开发中。你知道吗</p>
<p>但是这个例子只是创建了一个相当简单的模板(<code>menu_item</code>),它引用了我们感兴趣的来自<code>pokemon</code>对象的字段。请注意,我可以按自己喜欢的顺序使用任意多或任意少的这些字段/属性,如果愿意,可以重复使用。<code>p.choice</code>和<code>p.name</code>充当模板的“宏”名称。对于更复杂的情况,我可以将多个对象传递给方法,例如:<code>.format(p=this, m=something, o=other)</code>。你知道吗</p>
<p>在所有这些设置之后,我的“程序”只有9行长。在这个示例中,我展示了可重用模块代码(函数的定义、硬编码数据结构的初始化等)与该文件作为程序执行时应该运行的代码之间的传统分隔符。(这允许要用作模块的同一文件。。。用<code>import</code>语句。。。以及一个独立的程序。你知道吗</p>
<p>注意,我在这段代码中没有任何<code>if ... elif ... elif ...</code>。我们不需要他们。我认为这些行为(在本例中只是静态“结果”字符串模板)嵌入到我们的数据表中。因此,每个<code>pokemon</code>对象都包含我们处理该选择所需的数据。你知道吗</p>
<p>在真实的游戏中,我们很可能会定义一个<code>class Pokemon(object):</code>。。。并定义方法(函数是“绑定到”类的属性),在我们的菜单(或“事件循环”)中,我们可以执行以下操作:</p>
<pre><code>while playing:
choice = input(menu)
if choice == 'q':
playing = False
continue
monster = army[choice]
results = monster.fight()
print(results)
</code></pre>
<p>。。。或者类似的。你知道吗</p>
<p>面向对象编程的要点是可以创建和组合数据(对象),以保持它们自己的状态并提供它们自己的行为。因此,包装在我们对象周围的代码只需要知道如何作为一个整体访问类的行为,而不是与针对不同实例的特殊处理进行耦合(纠结)。你知道吗</p>
<p>在我的“results”中,我使用了一种更简单、更原始的字符串模板呈现形式(在字符串上使用%操作符)。“per”(percent)操作符将一个或多个值插入字符串中,以替换某些类似“printf”的模式。这与%作为“module”的数字用法非常类似,因为我们可以说操作符左边的字符串是结果“module”(不包括右边的因子)。你知道吗</p>
<p>此代码的另一个特性是<code>try: ... except ValueError</code>。。。我本可以直接将<code>choice</code>字段设置为字符串并避免这一步。你知道吗</p>
<p>但是我还想包括一个异常处理的例子。你知道吗</p>
<p>输入验证和异常/错误处理是实际编程的大部分工作所在。当你在一个有可靠网络的完美操作系统上提供清晰的输入时,大多数编程都是相对简单的。但是健壮的程序需要在不理想的条件下处理退化的,甚至是敌对的输入。你知道吗</p>