1.2. 声明函数
J
象大多数其它语言,Python拥有函数。但是不象C++或
ava,它没有独立的接口声明和实现声明。一旦需要一个函数,声明它,编码就行了。
根据上个例子:
dive into python有写道:
def buildConnectionString(param
s
):
有几件事情需要注意的。首先,关键字 def 为函数声明的开始。不象VB,Python并不区分有返回值的函数与无返回值的函数。它没有子程序。全部都是函数,所有的函数都以 def 开始。
其次,函数没有定义返回的数据类型。实际上,甚至不需要指定是否会返回一个值。函数并不关心第一种情况或第二种情况。如果函数执行了一个 return 语句,它将返回一个值,否则会返回 None (Python的空值)。
第三,参数 params 并不需要指明数据类型。在Python中,变量不需要被明确类型。Python会指出一个变量是什么类型,并在内部保持记录。
1.3. 文档化函数
可以通过给出一个文档字符串文档化一个Python函数。
例 1.4. 定义 buildConnectionString 函数的文档字符串
def buildConnectionString(param
s
): """Build a connection string from a dictionary of parameters. Returns string."""
三重双引号的引用表示一个多行字符串。在定义一个文档字符串的时候,你会看到它们经常被使用。
任何在三重双引号中的东西都是函数的文档字符串,它们用来说明函数可以做什么。如果存在文档字符串,它必须要在函数中的被首先定义(也就是说在冒号后面被定义)。在技术上不需要给函数定义文档字符串,但是你应该这样做。我相信在你所参加过的每一个编程课上已经听到过这一点,但是Python会给你一些其它的机制:文档字符串在运行时可作为函数的属性。
许多Python IDE使用文档字符串来提供上下文相关提示,这样当你敲入一个函数名,它的文档字符串将显示为提示框。这一点相当的有用,但是完全要看你写的文档字符串的好坏了。 1.4. 每个都是对象万一你没听到,我刚才说了Python函数有属性,那些属性在运行时可用。 函数,如同在Python中的每个东西,是一个对象。 例 1.5. 存取 buildConnectionString 函数的文档字符串 >>> import odbchelper >>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> print odbchelper.buildConnectionString(params) server=mpilgrim;uid=sa;database=master;pwd=secret >>> print odbchelper.buildConnectionString.__doc__ Build a connection string from a dictionary Returns string. 在Python中每个东西都是对象,并且几乎每个东西都有属性和方法。[1] 所有的函数都有一个内置的属性 __doc__,它会返回在函数源代码中定义的文档字符串。 这一点如此重要,所以我会在前几讲中重复它,以防你忘记了:在Python中的每个东西都是对象。字符串是对象。列表是对象。函数是对象。甚至模块是对象,这一点我们很快会看到。
|
1.5. 缩排代码
Python函数没有明显的 begin 或 end,或任何括号或大括号,可以标识函数从哪里开始,又在哪里结束。唯一的分隔是通过一个冒号(:)和代码本身的缩排来表示。
例 1.6. 缩排 buildConnectionString 函数
def buildConnectionString(param
s
): """Build a connection string from a dictionary of parameters. Returns string.""" return ";".join
(["%s=%s" % (k, params
[k]) for k in params
.keys
()])
代码块(函数,if 语句,for 循环,等等)通过它们的缩排来定义。缩排表示块的开始,非缩排表示结束,不存在明显的括号,大括号,或关键字。这就意味着空白是有意义的,并且要一致。在这个例子中,函数代码(包括文档字符串)缩近了4个空格。不一定非要是4个,只要一致就可以了。第一行没有缩近,则不算在函数内。
在经过开始的一些反对和同Fortran相比后的挖苦之后,你将会心平气和地对待它,开始理解它的好处。一个主要的好处是,所有的Python程序看上去差不多,因为缩排是语言的要求而不是风格问题。这样就更容易阅读和理解他人的Python代码。
1.6. 测试模块
Python模块是对象,并且有几个有用的属性。在编写模块时,你可以利用这一点更容易地测试模块。
if __name__ == "__main__":
在开始学习好东西之前,有两点要说明。第一,在 if 表达式周围不需要小括号。第二,象C语言一样,Python使用 == 进行比较,使用 = 进行赋值。不象C语言,Python不支持行内赋值,所以不存在把赋值当成比较的偶然情况。
那么为什么说这个特殊的 if 语句是一个技巧呢?模块是对象,并且所有的模块都有一个内置属性 __name__。一个模块的 __name__ 的值要看你如何使用它。如果 import 模块,那么 __name__ 的值通常为模块的文件名,不带路径或者文件扩展名。但是你也可以象一个标准的程序一样直接运行模块,在这种情况下 __name__ 的值将是一个特别的缺省值, __main__。
>>> import odbchelper
>>>
odbchelper.__name__ 'odbchelper'
一旦了解这一点,你可以在模块内部为你的模块设计一个测试套件,通过在其中加入这个 if 语句。当你直接运行模块, __name__ 的值是 __main__,所以测试套件执行。当你导入模块, __name__ 的值就是别的东西了,所以测试套件被忽略。这样使得在将新的模块集成到一个大程序之前开发和调试容易多了。
在MacPython上,需要一个额外的步聚来使得 if __name__ 技巧有效。点击窗口右上角的黑色三角,弹出模块的属性菜单,确认Run as __main__被选中。 |
1.7. 字典 101
有必要先讲些别的,因为你需要知道字典,序列(tuples),和列表。如果你一个Perl高手,你大概可以忽略关于字典和列表那一块,但是你仍然应该对序列进行关心。
Python的内置数据类型之一是字典,它在关键字与值之间定义了一对一的关系。这一点就象Perl中的关联数组,Java中的 Map ,或VBScipt中的 Scripting.Dictionary 对象。
>>>d = {"server":"mpilgrim", "database":"master"}
>>>d {'server': 'mpilgrim', 'database': 'master'} >>> d["server"]
'mpilgrim' >>> d["database"]
'master' >>> d["mpilgrim"]
Traceback (innermost last):
File "<interactive input>", line 1, in ?
KeyError: mpilgrim
>>>d {'server': 'mpilgrim', 'database': 'master'} >>> d["database"] = "pubs"
>>>d {'server': 'mpilgrim', 'database': 'pubs'} >>> d["uid"] = "sa"
>>>d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'}
不能在一个字典中有重复的键字。给一个存在的键字赋值会抹掉原来的值。 | |
可以在任何时候加入新的键-值对。这种语法同修改存在的值一样。(是的,它可能某天会给你带来麻烦,你可能认为增加了新值,但实际上只是反复地修改了同样的值,因为你的键字没有按照你的想象改变。) |
注意新的元素(键字为 uid,值为 sa)出现在字典中间。实际上,它只不过是一种巧合,在第一个例子中的元素看上去是有序的。现在它们看上去无序了则更是一种巧合。
字典没有元素顺序的概念。说元素顺序乱了是不正确的,它们只是简单的无序。这是一个重要的特性,它会在你想要以一种特定的,可重复的顺序(象以键字的字母表顺序)存取字典元素的时候骚扰你。有一些实现的方法,它们只是没有加到字典中去。 |
>>>d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'} >>> d["retrycount"] = 3
>>>d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d[42] = "douglas"
>>>d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3}
字典不是只用于字符串。字典的值可以是任意数据类型,包括字符串,整数,对象,或者甚至其它的字典。在一个单个字典里,字典的值并不需要全都是同一数据类型,可以根据需要混用和配匹。 |
|
字典的关键字要严格一些,但是它们可以是字符串,整数和几种其它的类型(后面还会谈到这一点)。也可以在一个字典中混用和配匹关键字。 |
>>>d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3} >>> del d[42]
>>>d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d.clear()
>>>d {}
del 允许你根据键字将单个元素从字典中删除。 | |
clear 会删除一个字典中所有元素。注意空的大括号所表示的集合说明一个字典没有元素。 |
自己运行的情况
1.8. 列表 101
列表是Python中使用最频繁的数据类型。如果你对列表仅有的经验是在VB中的数组或Java中的 Lists ,那么振作起来,面对Python列表吧。
>>>li = ["a", "b", "mpilgrim", "z", "example"]
>>>li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[0]
'a' >>> li[4]
'example'
首先我们定义了一个有5个元素的列表。注意它们保持着它们初始的顺序。这不是一个偶然。一个列表是一个用方括号包括的有序元素集。 |
|
一个列表可以象一个以0开始的数组一样使用。任何一个非空列表的第一个元素总是 li[0]。 | |
这个5元素列表的最后一个元素是 li[4],因为列表总是从0开始。 |
>>>li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[-1]
'example' >>> li[-3]
'mpilgrim'
负数索引从列表的尾部开始向后计数存取元素。任何一个非空的列表最后一个元素总是 li[-1]。 | |
如果负数索引使你感到糊涂,象这样理解它:li[n] == li[n - len(li)]。所以在这个列表里,li[2] == li[2 - 5] == li[-3]. |
>>>li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[1:3]
['b', 'mpilgrim'] >>> li[1:-1]
['b', 'mpilgrim', 'z'] >>> li[0:3]
['a', 'b', 'mpilgrim']
>>>li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[:3]
['a', 'b', 'mpilgrim'] >>> li[3:]
['z', 'example'] >>> li[:]
['a', 'b', 'mpilgrim', 'z', 'example']
>>>li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li.append("new")
>>>li ['a', 'b', 'mpilgrim', 'z', 'example', 'new'] >>> li.insert(2, "new")
>>>li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new'] >>> li.extend(["two", "elements"])
>>>li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
>>>li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.index("example")
5 >>> li.index("new")
2 >>> li.index("c")
Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.index(x): x not in list >>> "c" in li
0
在Python中不存在布尔类型。在一个布尔上下文中(象 if 语句),0 是假,所有其它的数值为真。这一点也可以扩展到其它类型。一个空串(""),一个空列表([]),和一个空字典({})都是假,所有其它的字符串,列表,和字典是真。 |
>>>li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.remove("z")
>>>li ['a', 'b', 'new', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("new")
>>>li ['a', 'b', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("c")
Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.remove(x): x not in list >>> li.pop()
'elements' >>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two']
>>>li = ['a', 'b', 'mpilgrim'] >>> li = li + ['example', 'new']
>>>li ['a', 'b', 'mpilgrim', 'example', 'new'] >>> li += ['two']
>>>li ['a', 'b', 'mpilgrim', 'example', 'new', 'two'] >>> li = [1, 2] * 3
>>>li [1, 2, 1, 2, 1, 2]
列表也可以用 + 操作符连接起来。list = list + otherlist 相当于 list.extend(otherlist)。但是 + 操作符将连接后的列表作为一个值返回,而 extend 仅修改存在的列表。 | ||||||||||||||||||||||||||||||||||||
Python支持 += 操作符。li += ['two'] 相当于 li = li + ['two']。+= 操作符可用于列表,字符串,和整数,并且它也可以在用户定义类中被重载。(第三章会有更多的类) | ||||||||||||||||||||||||||||||||||||
* 操作符作为一个重复符可用在列表上。li = [1, 2] * 3 相当于 li = [1, 2] + [1, 2] + [1, 2], 将三个列表连成一个。 1.9. 序列 101序列是不可变列表。一旦创建了一个序列就不能以任何方式改变它。 >>>t = ("a", "b", "mpilgrim", "z", "example") >>>t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t[0] 'a' >>> t[-1] 'example' >>> t[1:3] ('b', 'mpilgrim')
>>>t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t.append("new") Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'append' >>> t.remove("z") Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'remove' >>> t.index("example") Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'index' >>> "z" in t 1
那么序列有什么好处呢?
1.10. 定义变量即然你认为已经了解了字典,序列和列表的所有知识,就让我们回到我们的例子程序 odbchelper.py。 Python象大多数其它语言一样有局部和全局变量,但是它没有明显的变量声明。变量通过赋值产生,当超出作用范围时自动消灭。 if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret" \ } 这里有几个有趣的地方。首先,注意一下缩排。if 语句是代码块,需要象函数一样缩排。 其次,变量的赋值是一条命令被分成了几行,用反斜线(“\”)作为续行符。
第三,你从未声明过变量 myParams,你只是给它赋了一个值。这点就象是VBScript没有设置 option explicit 选项。幸运的是,不象VBScript,Python不允许你引用一个未被赋值的变量,试图这样做会引发一个异常。 >>>x Traceback (innermost last): File "<interactive input>", line 1, in ? NameError: There is no variable named 'x' >>> x = 1 >>> x 1 早晚有一天你会为此而感谢Python。
1.11. 格式化字符串odbchelper.py 所有实际的工作是用一行代码完成的,下面就是。 return ";".join (["%s=%s" % (k, params [k]) for k in params .keys ()]) 不要恐慌。忽略别的东西,集中在中间部分,它是字符串格式化表达式。(如果你是一个C高手,也许可以忽略这部分。) "%s=%s" % (k, params [k]) Python支持将值的格式化输出到字符串中,象C语言中的 sprintf 函数。最基本的用法是简单地在 %s 占位符的地方插入一个值。 >>>params = {"uid":"sa", "pwd":"secret"} >>> k = "uid" >>> "%s=%s" % (k, params[k]) 'uid=sa' >>> k = "pwd" >>> "%s=%s" % (k, params[k]) 'pwd=secret'
注意(k,params[k])是一个序列。我说过它们对某些东西有用。 你可能一直在想做了这么多工作只是为了做简单的字符串连接,你想的不错;只不过字符串格式化不只是连接。它甚至不仅仅是格式化。它也是强制类型转换。 >>>uid = "sa" >>> pwd = "secret" >>> print pwd + " is not a good password for " + uid secret is not a good password for sa >>> print "%s is not a good password for %s" % (pwd, uid) secret is not a good password for sa >>> userCount = 6 >>> print "Users connected: %d" % (userCount, ) Users connected: 6 >>> print "Users connected: " + userCount Traceback (innermost last):
File "<interactive input>", line 1, in ?
TypeError: cannot add type "int" to string
1.13. 连接列表和分割字符串你有了一个形如 key=value 的键-值对列表,并且想将它们合成为单个字符串。为了将任意字符串列表连接成单个字符串,使用一个字符串对象的 join 方法。 例 1.32. 在 buildConnectionString 中连接字符串 return ";".join (["%s=%s" % (k, params [k]) for k in params .keys ()]) 在我们继续之前有一个有趣的地方。我一直在重复函数是对象,字符串是对象,每个东西都是对象。你也许认为我的意思是说字符串 变量 是对象。但是不,仔细地看一下这个例子,你将会看到字符串 ";" 本身是一个对象,你在调用它的 join 方法。 总之, join 方法将列表元素连接成单个字符串,每个元素用一个分号隔开。分隔符不需要是一个分号;它甚至不必是单个字符。它可以是任何字符串。
>>>params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> ["%s=%s" % (k, params[k]) for k in params.keys()] ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> ";".join(["%s=%s" % (k, params[k]) for k in params.keys()]) server=mpilgrim;uid=sa;database=master;pwd=secret 接着,上面的字符串从 help 函数返回,被调用块打印出来,这样就给出在你开始阅读本章时令你感到吃惊的输出。 历史记录 当我开始学Python时,我以为 join 应该是列表的方法,它会使用分隔符作为一个参数。看上去有些矛盾的原因纯粹由于历史造成的。在Python 1.6之前,字符串完全没有这些有用的方法。有一个独立的 string 模块拥有所有的字符串函数,每个函数使用一个字符串作为它的第一个参数。这些函数被认为足够重要,所以它们移到字符串中去了,这当然是件好事情。但是,无论好坏, join 跟它们一起,这就是我们看到的。 你可能在想是否存在一个适当的方法来将字符串分割成一个列表。当然有,它叫做 split。 >>>li = ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s = ";".join(li) >>> s 'server=mpilgrim;uid=sa;database=master;pwd=secret' >>> s.split(";") ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s.split(";", 1) ['server=mpilgrim', 'uid=sa;database=master;pwd=secret']
1.14. 小结odbchelper.py 程序和它的输出现在应该非常清楚了。 def buildConnectionString(param
s
): """Build a connection string from a dictionary of parameters. Returns string.""" return ";".join (["%s=%s" % (k, params [k]) for k in params .keys ()]) if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret" \ } print buildConnectionS tring(myParams ) server=mpilgrim;uid=sa;database=master;pwd=secret
在深入下一章之前,确保你可以无困难地完成下面的事情:
下面第一章已经完毕,大体上懂得python是什么玩意 |