探索Python,第2部分:探索Python类型的层次结构
PiscesSTAR
|
1#
PiscesSTAR 发表于 2008-06-27 14:46
探索Python,第2部分:探索Python类型的层次结构
转载:http://www.ibm.com/developerworks/cn/opensource/os-python2/
了解对象和容器 级别:初级 Robert J. Brunner ( [email=rb@ncsa.uiuc.edu?subject=%E6%8E%A2%E7%B4%A2%20Python%20%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84&cc=rb@ncsa.uiuc.edu]rb@ncsa.uiuc.edu[/email] ), NCSA 研究专家、天文学助理教授 2005 年 6 月 27 日 Python编程语言是一种简单但功能强大的语言。本文将探索该语言的对象属性,开头部分介绍了一些简单的内置数据类型。此外,本文还介绍了Python元组类,并用它演示容器类型的概念。 在Python语言中,所有事物都是程序可以访问的对象, 其中包括保存整数的简单类型,以及您编写的实际代码和这些代码在Python解释器中的表示。对于熟悉其他编程语言的人来说,此行为可能导致某些混乱。但是,在实践中,不会出现这种情况。Python有一个良好定义的类型(或对象)层次结构,该层次结构在概念上可以划分为四种类别:简单类型、容器类型、代码类型和内部类型。这四种类别和简单类型本身在本系列的第一篇文章“Python入门,第1部分:Python的内置数据类型”中作了介绍。本文将再次查看可以在Python中使用的那些简单的内置数据类型,这次着重介绍这些类型的对象属性。然后,我们将介绍容器类型的概念,并且将Python tuple类作为此类型的第一个示例,进行重点介绍。 简单类型 内置到Python 编程语言中的简单数据类型包括: * bool * int * long * float * complex 支持简单数据类型不是Python独有的功能,因为多数现代编程语言都具有完整类型补充。例如Java™语言甚至有一组更丰富的原始数据类型: * byte * short * int * long * float * double * char * boolean 但是,在Python中,简单数据类型并不是原始数据类型,而是完善的对象,它们有自已的方法和类。另外,这些简单的内置类型是不可改变的,这意味着:创建对象之后,您无法更改对象的值。如果需要新值,则必须创建新的对象。Python简单数据类型的不可改变特性与其他多数流行语言(如Java语言)处理简单原始类型的方式不同。但是,当您对这些简单数据类型的对象属性有了更多的了解之后,就很容易理解这种差异。 所以,整数如何能够拥有一些方法?它仅仅是一个数字吗?不是的,至少在Python中答案是否定的。您自已可以对它进行检验:仅借助内置的help方法,就可以向Python解释器咨询关于int对象的信息(参见清单 1)。 清单1. Python解释器: 用于整数对象的Help rb% python Python 2.4 (#1, Mar 29 2005, 12:05:39) [GCC 3.3 20030304ppp(Apple Computer, Inc. build 1495)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> help(int) Help on class int in module __builtin__: class int(object) | int(x[, base]) -> integer | | Convert a string or number to an integer, if possible. A floating point | argument will be truncated towards zero (this does not include a string | representation of a floating point When converting a string, use | the optional base. It is an error to supply a base when converting a | non-string. If the argument is outside the integer range a long object | will be returned instead. | | Methods defined here: | | __abs__(...) | x.__abs__() abs(x) | | __add__(...) | x.__add__(y) x+y ... 这具体说明了什么?只有一个事情,那就是可以方便地从Python解释器中得到帮助,但是从后面部分可以获得更多帮助。第一行告诉您正在查看int类的帮助页面,它是一个内置的数据类型。如果您对面向对象的编程的概念不太熟悉,那么可以将类想像成只是一个用于构建特殊事物并与之交互的蓝图。好比房子的设计蓝图,不仅显示如何构建房子,还显示房子完工之后,如何更好地使用房子。例如,设计图会显示不同房间的位置、在房间之间的移动方式以及出入房子的通道情况。 第一行的下面是对实际int类的详细说明。在这一点上,您可能不熟悉如何在Python中创建类,因为显示的语法类似于外语。没关系,我将在另一篇文章中对此进行全面介绍。现在,您只需要知道:int对象是从object类中继承而来,它是Python中许多内容的一个基类。 后面的几行介绍int类的构造函数。构造函数只是创建特定类实例(或对象)的特殊方法。构造函数方法好比建筑承包人,它利用房子的设计图建房子。在Python中,构造函数的名称与其创建的类的名称相同。类可以有不同的构造函数方法,这些方法是通过类名称后的圆括号内附带的不同属性进行区分。类可以有不同构造函数方法的较好的一个例子就是int类, 实际上,您可以用多种方法调用它,具体采用哪种方法取决于圆括号中放置的参数(参见清单 2)。 清单2. Python解释器:int类构造函数 >>> int() 0 >>> int(100) # Create an integer with the value of 100 >>> int("100", 10) # Create an integer with the value of 100 in base 10 100 100 >>> int("100", 8) # Create an integer with the value of 100 in base 8 64 这四个构造函数调用创建了四个不同的整数。第一个构造函数创建了一个整数对象,其值为0,在没有值提供给int类构造函数的情况下,该值是所使用的默认值。第二个构造函数根据规定创建了一个值为100的整数。第三个构造函数采用了字符串“100”并创建了以10为基数的整数值(常见的十进制系统)。最后一个构造函数也采用了字符串“100”——但是它使用基数8来创建整数值,通常称为 八进制。不过,该值在输出时会被转换成十进制数值,这就是该数字显示为64的原因。 您可能想知道如果省略了构造函数调用中的圆括号将会发生什么。在这种情况下,您可以向该变量分配一个实际的类名称,有效地为原先的类创建一个别名(参见清单3)。 清单3. Python解释器:int类型 >>> it = int # Create an alias to the integer class >>> it(100) 100 >>> type(it) # We created a new type >>> type(it(100)) # Our new type just makes integers 真是太棒了!您立即可以创建一个由内置int类定义的新数据类型。但请注意不好的一面,不要滥用这一新功能。优秀的程序员除了使代码具有良好性能外,还应努力使代码清淅。这类编码技巧的确有其使用价值,但它们并不常见。 使用Python解释器可以使新的Python程序员简化学习过程,少走弯路。如果您想详细了解 Python内的help工具,只需在Python解释器中的命令提示符下键入help(),就可以访问交互式的帮助工具(参见清单4)。 清单4. Python解释器:帮助解释器 >>> help() Welcome to Python 2. This is the online help utility. If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://www.python.org/doc/tut/. Enter the name of any module, keyword, or topic to get help on writing Python programs and using Python modules. To quit this help utility and return to the interpreter, just type "quit". To get a list of available modules, keywords, or topics, type "modules", "keywords", or "topics". Each module also comes with a one-line summary of what it does; to list the modules whose summaries contain a given word such as "spam", type "modules spam". help> 您可能已经对此有所了解,但在help>提示符处输入int可以显示那些为以前的int类显示的类描述。 容器类型 到目前为止,已经谈论了许多Python语言中使用的简单类型。但是多数程序并不简单,它们涉及通常由简单类型组成的复杂数据。因此,现在的问题就成了“如何在Python中处理复杂数据?” 如果您熟悉面向对象的语言,如Java或C#,那么您可能认为该问题的答案很简单:只需创建一个新类来处理复杂的数据即可。该方法也适用于Python,原因是Python支持通过类创建新类型。但是,在多数情况下,Python还可以提供更为简单的方法。当您的程序需要一次处理多个对象时,就可以利用Python容器类: * tuple * string * unicode * list * set * frozenset * dictionary 这些容器类型提供了两种功能。前六个类型是有序的,最后一个类型dictionary则是一个映射。有序类型与映射类型的区别较为简单。有序类型仅仅是指对象的顺序。所有的有序类型(除set 和frozenset类型外)都支持访问给定顺序的对象。相比之下,映射容器则用于保存那些对顺序不是很敏感的对象;通过提供可以找到关系值的密钥,就可以从容器中提取值。 容器类型间的另一个不同点来自于它们所持有的数据的特性,下面四种容器类型的顺序是不可变的: * tuple * string * unicode * frozenset 这意味着在您创建了这些容器类型之一后,所存储的数据就不可更改。如果出于某种原因需要更改数据,则需要创建一个新容器来保存新的数据。 后三种容器类型(list、set和dictionary)都是可变容器,因此,它们可以根据需要更改保存的任何数据(但在dictionary中所使用的密钥是不可变的,就像您房间的钥匙)。虽然可变容器非常灵活,但它们的动态特性会对性能造成影响。例如,tuple类型,尽管它是不可变的,灵活性较差,但在同一环境中使用时,它们通常比list类型快得多。 这些容器类提供了强大的功能,它们通常是多数Python程序的核心。本文的其余部分讨论了 tuple类型,它用于引入许多与创建和使用Python中的容器类型有关的基本概念。其余的类型将在以后的文章中讨论。 元组 tuple类型像一个口袋,在出门前可以把所需的任何东西一股脑地放在里面。您可以将钥匙、驾驶证、便笺簿和钢笔放在口袋里,您的口袋是存放各种东西的收集箱。Python的tuple类型与口袋类似,它可以存放不同类型的对象。您只需向变量分配一个用逗号分隔的对象序列,就可以创建一个 tuple(参见清单5)。 清单5. Python解释器:创建一个tuple >>> t = (0,1,2,3,4,5,6,7,8,9) >>> type(t) >>> t (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) >>> tt = 0,1,2,3,4,5,6,7,8,9 >>> type(tt) >>> tt (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) >>> tc=tuple((0,1,2,3,4,5,6,7,8,9)) >>> tc (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) >>> et = () # An empty tuple >>> et () >>> st = (1,) # A single item tuple >>> st (1,) 该示例代码显示了如何以多种方式创建tuple。第一种方法是创建一个包含从0到9整数序列的 tuple。第二种方法与第一种相同,但这次省去了括号。在创建一个tuple时,括号通常是可选的,但有时是必需的,这取决于上下文。结果,您会习惯性地使用括号来减少混淆。最后一个tuple tc 使用了一个实际的类构造函数来创建tuple。这里重要的一点是,构造函数构成中仅有一个变量,因此您必须在括号中包括对象序列。最后两个构造函数调用演示了如何通过在括号内不放任何东西来创建空的tuple(et),以及如何通过将一个逗号放在序列中仅有的一个项目后面来创建tuple (st)。 使用口袋装东西的一个主要原因是为了方便生活。但要求在需要这些东西的时候能够迅速地从口袋中取出它们。Python中的多数容器类型(其中包括 uple)允许您使用方括号操作符从集合中方便地访问数据项。但Python比其他语言更具灵活性:您可以使用通常称为分段的方法选择一个项目或多个有序项目(参见清单6)。 清单6. Python解释器:从tuple访问项目 >>> t = (0,1,2,3,4,5,6,7,8,9) >>> t[2] 2 >>> type(t[2]) >>> t[0], t[1], t[9] (0, 1, 9) >>> t[2:7] # Slice out five elements from the tuple (2, 3, 4, 5, 6) >>> type(t[2:7]) >>> t[2:7:2] # Slice out three elements from the tuple (2, 4, 6) 在创建简单的tuple之后,前面的示例显示如何选择一个数据项——在本示例中是整数2。这时,请注意Python使用了零排序,其中集合中的项目从零开始编号。如果您熟悉使用Java语言、C#或其他从C语言派生的语言进行编程,那么您应该非常熟悉此行为。否则,该概念也是非常简单的。用于访问数据项的索引只声明集合中越过第一个数据项有多远,或者称为序列,您需要去获得所需的内容。因此,要获得第三个数据项(在本示例中为整数2),您需要从第一个数据项起越过两个数据项。在访问第三个数据项时,Python知道它是一个整数对象。您还可以方便地从集合中提取多个数据项。在本示例中,您创建了一个新的tuple,其值为从最初的tuple开始第一、第二和第十个值。 其余的示例显示了如何使用Python的分段功能从序列中一次选择多个数据项。术语分段是指从序列中对数据项进行分段的方法。分段的工作方式是声明开始索引、结束索引和一个可选的步骤大小,全部都用冒号分隔。因此,t[2:7]将tuple 中的第三到第七个数据项分段,而t[2:7:2]则对每两个数据项进行分段,从tuple中的第三个数据项开始一直到第七个数据项。 我目前创建的tuple对象是同类的,它们仅包含整数对象。所幸的是,tuple要比显示的示例复杂得多,因为tuple实际上是一个异构容器(参见清单7)。 清单7. Python解释器:异构的tuple >>> t = (0,1,"two",3.0, "four", (5, 6)) >>> t (0, 1, 'two', 3.0, 'four', (5, 6)) >>> t[1:4] (1, 'two', 3.0) >>> type(t[2]) >>> type(t[3]) >>> type(t[5]) >>> t[5] = (0,1) Traceback (most recent call last): File "", line 1, in ? TypeError: object does not support item assignment 您会看到,创建可以拥有各种类型数据项(其中包括另一tuple)的tuple是多么方便。并且可以使用方括号操作符以相同的方式访问所有数据项,它支持将不同类型的有序数据项分段。然而,tuple是不可变的。因此,当我尝试更改第五个元素时,发现不允许对数据项分配。打一个简单的比方,在您将某些东西放入口袋后,改变所取东西的惟一方式是取一个新口袋,并将所有数据项放进去。 如果需要在现有tuple中创建一个包含数据项子集的新tuple,最简单的方法是使用相关的片段,并根据需要同时添加子集(参见清单8)。 清单8. Python解释器:使用tuple >>> tn = t[1:3] + t[3:6] # Add two tuples >>> tn (1, 'two', 3.0, 'four', (5, 6)) >>> tn = t[1:3] + t[3:6] + (7,8,9,"ten") >>> tn (1, 'two', 3.0, 'four', (5, 6), 7, 8, 9, 'ten') >>> t2 = tn[:] # Duplicate an entire tuple, a full slice >>> t2 (1, 'two', 3.0, 'four', (5, 6), 7, 8, 9, 'ten') >>> len(tn) # Find out how many items are in the tuple 9 >>> tn[4][0] # Access a nested tuple 5 您还可以将现有tuple的片段与新tuple的片段合并在一起。使用片段语法,无需指定开始或结束索引,就可以制作现有tuple的副本。最后两个示例也非常有趣。内置的len方法告诉您tuple中数据项的数量。从嵌套的tuple访问数据项也非常简单:选择嵌套的tuple,然后从中访问有趣的数据项。 您还可以从称为打包的过程的一组现有变量中创建一个tuple。反之亦然,其中,tuple中的值被指派给变量。这之后的过程称为解包,它是用于许多情形的功能十分强大的技术,其中包括希望从一个函数中返回多个值。在解包tuple时,仅有的问题是必须为tuple中的每个数据项提供一个变量(参见清单9)。 清单9. Python解释器:打包和解包tuple >>> i = 1 >>> s = "two" >>> f = 3.0 >>> t = (i, s, f) # Pack the variables into a tuple >>> t (1, 'two', 3.0) >>> ii, ss, ff = t # Unpack the tuple into the named variables >>> ii 1 >>> ii, ff = t # Not enough variables to unpack three element tuple Traceback (most recent call last): File "", line 1, in ? ValueError: too many values to unpack 简化概念 尽管看上去十分复杂,但Python的对象属性实际上简化了Python语言新手常常面临的一些更为复杂的概念。在了解如何使用对象之后,所有东西都是对象这一概念意味着您已经进一步理解了一些新概念,如Python的容器类型,使困难的任务变得简单化是使用Python得到的常见好处之一;另一个例子是内置的帮助工具,只需在Python提示符处输入help(),就可以在Python 解释器中看到该工具。由于生活不是用一些简单的概念描述的,所以Python提供了一组丰富的容器(即集合)对象。在本文中,我介绍了其中的最简单的对象——tuple。要正确使用tuple,就需要熟悉它的工作方式。但是,由于许多其他容器类型具有类似的功能,其中包括分段以及打包或解包,了解tuple的工作原理意味着您已经开始完全理解Python中的其他容器类型。 参考资料
关于作者 Robert J. Brunner是国家超级计算应用中心的研究专家,而且是伊利诺伊大学香槟分校的天文学助理教授。他已出版几本书,撰写了许多论文,并对许多主题提供了一些指导,您可以通过 rb@ncsa.uiuc.edu与他联系。 |