1.2 Python语言
最后更新于:2022-04-01 11:21:26
# 1.2 Python语言
> 作者 Chris Burns, Christophe Combelles, Emmanuelle Gouillart, Gaël Varoquaux
> **Python中的科学计算** 这里我们介绍Python语言。这里只会仅仅解决可以用于Numpy和Scipy的最低要求。想要更多的了解这门语言,请参考[http://docs.python.org/tutorial](http://docs.python.org/tutorial) 这个非常好的教程。也可以借助专门的图书,比如:[http://diveintopython.org/](http://diveintopython.org/).
Python是一门**编程语言**,与C、Fortran、BASIC和PHP等等类似。Python的一些特性如下:
* 一种_解释性_(不是编译)语言。与C或者Fortran等不同,Python代码在执行前不会编译。另外,Python可以**交互**使用:有许多的Python解释器,命令和脚本可以在其中执行。
* 在**开源**证书下发布的免费软件:Python可以免费使用和分发,即使用于商用。
* **多平台**:Python可以用于所有的主流操作系统,Windows、Linux/Unix、MacOS X, 甚至可能是你有手机操作系统等等。
* 可读性很强的语言,有清晰不罗嗦的语法
* 拥有大量高质量的包,可以应用于多种多样的应用,从网站框架到科学计算。
* 非常简单的接口与其他语言交互,特别是C和C++
* 稍后会介绍一些语言的其他特性。例如Python是面向对象的语言,包含动态类型(一个变量可以在程序过程中,可以包含不同的对象类型)。
Python的特有特性的更多信息,请见:[http://www.python.org/about/](http://www.python.org/about/)
## 1.2.1 第一步
启动**IPython** Shell(一个增强的Python交互Shell):
* 在Linux/Mac终端中输入“ipython”,或者在Windows cmd sheell,
* 或者从菜单启动程序,即在[Python(x,y)](http://www.pythonxy.com/)或[EPD](http://www.enthought.com/products/epd.php),如果你已经安装这些Python科学套装之一。
如果你的电脑上还没有安装IPython,也可以选择其他Python shells,比如在终端中输入“Python”启动纯Python shell,或者Idle解释器。但是,我们建议使用IPython Shell,因为它增强特性,特别是对于科学计算。
如果你已经启动了解释器,输入
In [2]:
```
print "Hello, world!"
```
```
Hello, world!
```
接下来就会显示信息"Hello, world!"。你已经执行了你的第一条Python命令,恭喜!
你自己开始吧,输入下列命令
In [1]:
```
a = 3
b = 2*a
type(b)
```
Out[1]:
```
int
```
In [2]:
```
print b
```
```
6
```
In [3]:
```
a*b
```
Out[3]:
```
18
```
In [4]:
```
b = 'hello'
type(b)
```
Out[4]:
```
str
```
In [5]:
```
b + b
```
Out[5]:
```
'hellohello'
```
In [6]:
```
2*b
```
Out[6]:
```
'hellohello'
```
上面定义了_a_和_b_两个变量。注意这里在赋值前没有声明变量类型。相反,在C中,应该写为:
```
int a=3;
```
另外,变量的类型可以改变,在一个时间点它可以等于一个特定类型,在接下来的时间里,他可以等于另外的类型。_b_首先等于整数,但是当它被赋值为_"hello"_时他变成等于字符。在Python中,整数的运算符(b=2*a)原生支持的,一些字符上的操作符例如相加和相乘也是支持的,相当于串联和重复。
## 1.2.2 基础类型
### 1.2.2.1 数值类型
Python支持如下的数值、标量类型:
**整型:**
In [8]:
```
1 + 1
```
Out[8]:
```
2
```
In [11]:
```
a = 4
type(a)
```
Out[11]:
```
int
```
**浮点型:**
In [12]:
```
c = 2.1
type(c)
```
Out[12]:
```
float
```
**复数:**
In [13]:
```
a = 1.5 + 0.5j
a.real
```
Out[13]:
```
1.5
```
In [14]:
```
a.imag
```
Out[14]:
```
0.5
```
In [15]:
```
type(1. + 0j )
```
Out[15]:
```
complex
```
**布尔:**
In [16]:
```
3 > 4
```
Out[16]:
```
False
```
In [17]:
```
test = (3 > 4)
test
```
Out[17]:
```
False
```
In [18]:
```
type(test)
```
Out[18]:
```
bool
```
因此,Python shell可以代替你的口袋计算器,因为基本的代数操作符 +、-、*、/、%(模)都已经原生实现了。
In [19]:
```
7 * 3.
```
Out[19]:
```
21.0
```
In [20]:
```
2**10
```
Out[20]:
```
1024
```
In [21]:
```
8 % 3
```
Out[21]:
```
2
```
类型转化(投射):
In [22]:
```
float(1)
```
Out[22]:
```
1.0
```
**注意**:整数相除
In [23]:
```
3 / 2
```
Out[23]:
```
1
```
**技巧**:使用浮点:
In [24]:
```
3 / 2.
```
Out[24]:
```
1.5
```
In [25]:
```
a = 3
b = 2
a / b
```
Out[25]:
```
1
```
In [26]:
```
a / float(b)
```
Out[26]:
```
1.5
```
如果你明确想要整除,请使用//:
In [27]:
```
3.0 // 2
```
Out[27]:
```
1.0
```
Python3改变了除运算符行为。细节请看[python3porting](http://python3porting.com/preparing.html#use-instead-of-when-dividing-integers)网站.
### 1.2.2.2 容器
Python提供了许多有效的容器类型,其中存储了对象集合。
#### 1.2.2.2.1 列表
列表是一个有序的对象集合,对象可以有多种类型。例如:
In [28]:
```
L = ['red', 'blue', 'green', 'black', 'white']
type(L)
```
Out[28]:
```
list
```
索引:访问包含在列表中的单个对象:
In [29]:
```
L[2]
```
Out[29]:
```
'green'
```
使用负索引,从结尾开始计数:
In [30]:
```
L[-1]
```
Out[30]:
```
'white'
```
In [31]:
```
L[-2]
```
Out[31]:
```
'black'
```
**注意:索引从0开始**(和C中一样),而不是1(像在Fortran或Matlab)!
切片:获得规律分布元素的子列表:
In [32]:
```
L
```
Out[32]:
```
['red', 'blue', 'green', 'black', 'white']
```
In [33]:
```
L[2:4]
```
Out[33]:
```
['green', 'black']
```
**注意**:L[start:stop]包含索引start<= i < stop的元素(i的范围从start到stop-1)。因此,L[start:stop]包含(stop-start)个元素。
**切片语法**:`L[start:stop:stride]`
所有切片参数都是可选的:
In [34]:
```
L
```
Out[34]:
```
['red', 'blue', 'green', 'black', 'white']
```
In [35]:
```
L[3:]
```
Out[35]:
```
['black', 'white']
```
In [36]:
```
L[:3]
```
Out[36]:
```
['red', 'blue', 'green']
```
列表是可变对象,可以被改变:
In [38]:
```
L[0] = 'yellow'
L
```
Out[38]:
```
['yellow', 'blue', 'green', 'black', 'white']
```
In [39]:
```
L[2:4] = ['gray', 'purple']
L
```
Out[39]:
```
['yellow', 'blue', 'gray', 'purple', 'white']
```
**注:**一个列表的元素可以有不同的类型:
In [40]:
```
L = [3, -200, 'hello']
L
```
Out[40]:
```
[3, -200, 'hello']
```
In [41]:
```
L[1], L[2]
```
Out[41]:
```
(-200, 'hello')
```
对于一个所有类型都相同的数值数据集合,使用Numpy模块提供的数组类型通常更有效。Numpy数组是包含固定大小项目的内存组块。使用Numpy数组,元素上的操作可以非常快速,因为元素均匀分布在内存上并且更多的操作是通过特殊的C函数而不是Python循环。
Python提供了一大组函数来修改或查询列表。这里是一些例子,更多内容,请见:[http://docs.python.org/tutorial/datastructures.html#more-on-lists](http://docs.python.org/tutorial/datastructures.html#more-on-lists)
添加和删除元素:
In [42]:
```
L = ['red', 'blue', 'green', 'black', 'white']
L.append('pink')
L
```
Out[42]:
```
['red', 'blue', 'green', 'black', 'white', 'pink']
```
In [43]:
```
L.pop() # 删除并返回最后一个项目
```
Out[43]:
```
'pink'
```
In [44]:
```
L
```
Out[44]:
```
['red', 'blue', 'green', 'black', 'white']
```
In [45]:
```
L.extend(['pink', 'purple']) # 扩展列表L,原地
L
```
In [46]:
```
L = L[:-2]
L
```
Out[46]:
```
['red', 'blue', 'green', 'black', 'white']
```
反转:
In [47]:
```
r = L[::-1]
r
```
Out[47]:
```
['white', 'black', 'green', 'blue', 'red']
```
In [48]:
```
r2 = list(L)
r2
```
Out[48]:
```
['red', 'blue', 'green', 'black', 'white']
```
In [49]:
```
r2.reverse() # 原对象
r2
```
Out[49]:
```
['white', 'black', 'green', 'blue', 'red']
```
串联和重复列表:
In [50]:
```
r + L
```
Out[50]:
```
['white',
'black',
'green',
'blue',
'red',
'red',
'blue',
'green',
'black',
'white']
```
In [51]:
```
r * 2
```
Out[51]:
```
['white',
'black',
'green',
'blue',
'red',
'white',
'black',
'green',
'blue',
'red']
```
排序:
In [52]:
```
sorted(r) # 新对象
```
Out[52]:
```
['black', 'blue', 'green', 'red', 'white']
```
In [53]:
```
r
```
Out[53]:
```
['white', 'black', 'green', 'blue', 'red']
```
In [55]:
```
r.sort() # 原对象
r
```
Out[55]:
```
['black', 'blue', 'green', 'red', 'white']
```
**方法和面向对象编程**
符号r.method() (即 r.append(3) and L.pop()) 是我们第一个关于面向对象编程的例子(OOP)。作为列表,对象r有可以以这种方式调用的方法函数。对于这篇教程不需要关于面向对象编程的更多知识,只需要理解这种符号。
**发现方法**:
提醒:在IPython中:tab完成 (按tab)
```
In [28]: r.<TAB>
r.__add__ r.__iadd__ r.__setattr__
r.__class__ r.__imul__ r.__setitem__
r.__contains__ r.__init__ r.__setslice__
r.__delattr__ r.__iter__ r.__sizeof__
r.__delitem__ r.__le__ r.__str__
r.__delslice__ r.__len__ r.__subclasshook__
r.__doc__ r.__lt__ r.append
r.__eq__ r.__mul__ r.count
r.__format__ r.__ne__ r.extend
r.__ge__ r.__new__ r.index
r.__getattribute__ r.__reduce__ r.insert
r.__getitem__ r.__reduce_ex__ r.pop
r.__getslice__ r.__repr__ r.remove
r.__gt__ r.__reversed__ r.reverse
r.__hash__ r.__rmul__ r.sort
```
#### 1.2.2.2.2 字符
不同的字符语法(单引号、双引号或三个引号):
In [58]:
```
s = 'Hello, how are you?'
s = "Hi, what's up"
s = '''Hello,
how are you''' # 三个引号可以允许字符跨行
s = """Hi,
what's up?"""
'Hi, what's up?'
```
```
File "<ipython-input-58-dfe00f996c26>", line 7
'Hi, what's up?'
^
SyntaxError: invalid syntax
```
如果在字符中要是使用引号,那么应该嵌套使用,或者使用"\"进行转义,否则会报错。
换行的符号为 \n,tab符号是\t。
字符也是类似与列表的结合。因此,也可以使用相同的语法和规则索引和切片。
索引:
In [59]:
```
a = "hello"
a[0]
```
Out[59]:
```
'h'
```
In [60]:
```
a[1]
```
Out[60]:
```
'e'
```
In [61]:
```
a[-1]
```
Out[61]:
```
'o'
```
(记住负索引从右侧开始计数。)
切片:
In [64]:
```
a = "hello, world!"
a[3:6] # 第三到第六个(不包含)元素:元素3、4、5
```
Out[64]:
```
'lo,'
```
In [65]:
```
a[2:10:2] # 语法:a[开始:结束:步幅]
```
Out[65]:
```
'lo o'
```
In [66]:
```
a[::3] # 从开始到结尾,每隔3个字母
```
Out[66]:
```
'hl r!'
```
重音符号和特殊字符也可以被处理为Unicode字符(请见 [http://docs.python.org/tutorial/introduction.html#unicode-strings)。](http://docs.python.org/tutorial/introduction.html#unicode-strings)。)
字符是**不可变**对象,不可能修改内容。不过可以从原始的字符中创建一个新的字符。
In [68]:
```
a = "hello, world!"
a[2] = 'z'
```
```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-68-8f124c87c8cf> in <module>()
1 a = "hello, world!"
----> 2 a[2] = 'z'
TypeError: 'str' object does not support item assignment
```
In [69]:
```
a.replace('l', 'z', 1)
```
Out[69]:
```
'hezlo, world!'
```
In [70]:
```
a.replace('l', 'z')
```
Out[70]:
```
'hezzo, worzd!'
```
字符有许多有用的方法,比如上面的a.replace。回忆一下a.面向对象的符号,并且使用tab完成或者help(str)来搜索新的方法。and use tab completion or
**更多内容** Python提供了操作的字符的高级可能性,看一下模式或格式。感兴趣的读者请参考:[http://docs.python.org/library/stdtypes.html#string-methods](http://docs.python.org/library/stdtypes.html#string-methods) 和 [http://docs.python.org/library/string.html#new-string-formatting。](http://docs.python.org/library/string.html#new-string-formatting。)
字符格式:
In [71]:
```
'An integer: %i; a float: %f; another string: %s' % (1, 0.1, 'string')
```
Out[71]:
```
'An integer: 1; a float: 0.100000; another string: string'
```
In [72]:
```
i = 102
filename = 'processing_of_dataset_%d.txt' % i
filename
```
Out[72]:
```
'processing_of_dataset_102.txt'
```
#### 1.2.2.2.3\. Dictionaries
字典本质上是一个**映射键值**的高效表格。它是一个**无序**的容器
In [74]:
```
tel = {'emmanuelle': 5752, 'sebastian': 5578}
tel['francis'] = 5915
tel
```
Out[74]:
```
{'emmanuelle': 5752, 'francis': 5915, 'sebastian': 5578}
```
In [75]:
```
tel['sebastian']
```
Out[75]:
```
5578
```
In [76]:
```
tel.keys()
```
Out[76]:
```
['sebastian', 'francis', 'emmanuelle']
```
In [77]:
```
tel.values()
```
Out[77]:
```
[5578, 5915, 5752]
```
它可以方便的以名字(日期的字符和名称等)存储和获取值。更多信息见 [http://docs.python.org/tutorial/datastructures.html#dictionaries。](http://docs.python.org/tutorial/datastructures.html#dictionaries。)
一个字典的键(代表值)可以有不同的类型:
In [78]:
```
d = {'a':1, 'b':2, 3:'hello'}
d
```
Out[78]:
```
{3: 'hello', 'a': 1, 'b': 2}
```
#### 1.2.2.2.4\. More container types
**元组**
元组本质上是不可变列表。元组的元素用括号包起来,或者只是用逗号分割:
In [79]:
```
t = 12345, 54321, 'hello!'
t[0]
```
Out[79]:
```
12345
```
In [80]:
```
t
```
Out[80]:
```
(12345, 54321, 'hello!')
```
In [81]:
```
u = (0, 2)
```
**集合**:无序,惟一项目:
In [82]:
```
s = set(('a', 'b', 'c', 'a'))
s
```
Out[82]:
```
{'a', 'b', 'c'}
```
In [83]:
```
s.difference(('a', 'b'))
```
Out[83]:
```
{'c'}
```
#### 1.2.2.3\. 赋值运算
[Python类库参考:](http://docs.python.org/reference/simple_stmts.html#assignment-statements)
> 赋值语句被用于(重)绑定名称与值,以及修改可变对象的项目或属性。
简单来说,它这样工作(简单赋值):
1. 右侧表达式被评估,创建或获得产生的对象
2. 左侧的名字被赋值或绑定到右侧的对象
需要注意的事情:
* 单个对象可以有多个绑定的名称:
In [84]:
```
a = [1, 2, 3]
b = a
a
```
Out[84]:
```
[1, 2, 3]
```
In [85]:
```
b
```
Out[85]:
```
[1, 2, 3]
```
In [86]:
```
a is b
```
Out[86]:
```
True
```
In [87]:
```
b[1] = 'hi!'
a
```
Out[87]:
```
[1, 'hi!', 3]
```
* 要在**原地**改变列表,请使用索引或切片:
In [88]:
```
a = [1, 2, 3]
a
```
Out[88]:
```
[1, 2, 3]
```
In [89]:
```
a = ['a', 'b', 'c'] # 创建另一个对象
a
```
Out[89]:
```
['a', 'b', 'c']
```
In [90]:
```
id(a)
```
Out[90]:
```
4394695640
```
In [91]:
```
a[:] = [1, 2, 3] # 在原地修改对象
a
```
Out[91]:
```
[1, 2, 3]
```
In [92]:
```
id(a)
```
Out[92]:
```
4394695640
```
与上一个id相同,你的可能有所不同...
* 这里的关键观点是可变 vs. 不可变
* 可变对象可以在原地修改
* 不可变对象一旦被创建就不可修改
**更多内容**在David M. Beazley的文章[Python中的类型和对象](http://www.informit.com/articles/article.aspx?p=453682)中也可以找到关于以上问题非常不错的详尽解释。
## 1.2.3 流程控制
控制代码执行顺序。
### 1.2.3.1 if/elif/else
In [93]:
```
if 2**2 == 4:
print 'Obvious!'
```
```
Obvious!
```
**代码块用缩进限定**
**小技巧**:在你的Python解释器内输入下列行,并且注意保持缩进深度。IPython shell会在一行的 : 符号后自动增加缩进,如果要减少缩进,向左侧移动4个空格使用后退键。按两次回车键离开逻辑块。
In [96]:
```
a = 10
if a == 1:
print(1)
elif a == 2:
print(2)
else:
print('A lot')
```
```
A lot
```
在脚本中也是强制缩进的。作为练习,在condition.py脚本中以相同的缩进重新输入之前几行,并在IPython中用`run condition.py`执行脚本。
### 1.2.3.2 for/range
在索引上迭代:
In [97]:
```
for i in range(4):
print(i)
```
```
0
1
2
3
```
但是最经常使用,也更易读的是在值上迭代:
In [98]:
```
for word in ('cool', 'powerful', 'readable'):
print('Python is %s' % word)
```
```
Python is cool
Python is powerful
Python is readable
```
### 1.2.3.3 while/break/continue
典型的C式While循环(Mandelbrot问题):
In [13]:
```
z = 1 + 1j
while abs(z) < 100:
z = z**2 + 1
z
```
Out[13]:
```
(-134+352j)
```
**更高级的功能**
bread 跳出for/while循环:
In [103]:
```
z = 1 + 1j
while abs(z) < 100:
if z.imag == 0:
break
z = z**2 + 1
print z
```
```
(1+2j)
(-2+4j)
(-11-16j)
(-134+352j)
```
continue 继续下一个循环迭代:
In [101]:
```
a = [1, 0, 2, 4]
for element in a:
if element == 0:
continue
print 1. / element
```
```
1.0
0.5
0.25
```
### 1.2.3.4 条件表达式
**if [OBJECT]:**
评估为False:
```
- 任何等于0的数字 (0、0.0、0+0j)
- 空容器(列表、元组、集合、字典, ...)
- False,None
```
评估为True:
```
- 任何其他的东西
```
**a == b:**
判断逻辑是否相等:
In [1]:
```
1 == 1
```
Out[1]:
```
True
```
**a is b:**
测试同一性:两边是相同的对象:
In [2]:
```
1 is 1
```
Out[2]:
```
True
```
In [3]:
```
a = 1
b = 1
a is b
```
Out[3]:
```
True
```
**a in b:**
对于任何集合b:b包含a
In [11]:
```
b = [1, 2, 3]
2 in b
```
Out[11]:
```
True
```
In [12]:
```
5 in b
```
Out[12]:
```
False
```
如果b是字典,这个语法测试a是否是b的一个键。
### 1.2.3.5\. 高级循环
#### 1.2.3.5.1 序列循环
你可以在任何序列上进行循环(字符、列表、字典的键,文件的行...):
In [14]:
```
vowels = 'aeiouy'
for i in 'powerful':
if i in vowels:
print(i),
```
```
o e u
```
In [15]:
```
message = "Hello how are you?"
message.split() # 返回一个列表
```
Out[15]:
```
['Hello', 'how', 'are', 'you?']
```
In [16]:
```
for word in message.split():
print word
```
```
Hello
how
are
you?
```
很少有语言(特别是科学计算语言)允许在整数或索引之外的循环。在Python中,可以在感兴趣的对象上循环,而不用担心你通常不关心的索引。这个功能通常用来让代码更易读。
**警告**:改变正在循环的序列是不安全的。
#### 1.2.3.5.2 跟踪列举数
通常任务是在一个序列上循环,同时跟踪项目数。
```
- 可以像上面,使用带有计数器的while循环。或者一个for循环:
```
In [17]:
```
words = ('cool', 'powerful', 'readable')
for i in range(0, len(words)):
print i, words[i]
```
```
0 cool
1 powerful
2 readable
```
但是,Python为这种情况提供了enumerate关键词:
In [18]:
```
for index, item in enumerate(words):
print index, item
```
```
0 cool
1 powerful
2 readable
```
#### 1.2.3.5.3 字典循环
使用**iteritems**:
In [19]:
```
d = {'a': 1, 'b':1.2, 'c':1j}
for key, val in d.iteritems():
print('Key: %s has value: %s' % (key, val))
```
```
Key: a has value: 1
Key: c has value: 1j
Key: b has value: 1.2
```
#### 1.2.3.5.4 列表理解
In [20]:
```
[i**2 for i in range(4)]
```
Out[20]:
```
[0, 1, 4, 9]
```
**练习**
用Wallis公式,计算π的小数
![Wallis公式](http://scipy-lectures.github.io/_images/math/31913b3982be13ed2063b0ffccbcab9cf4931fdb.png)
## 1.2.4\. 定义函数
### 1.2.4.1 函数的定义
In [21]:
```
def test():
print('in test function')
test()
```
```
in test function
```
**注意**:函数块必须像其他流程控制块一样缩进
### 1.2.4.2 返回语句
函数**可以选择**返回值。
In [22]:
```
def disk_area(radius):
return 3.14 * radius * radius
disk_area(1.5)
```
Out[22]:
```
7.0649999999999995
```
**注意**:默认函数返回`None`。
**注意**:注意定义函数的语法:
* def关键字:
* 接下来是函数的名称,然后
* 在冒号后是在括号中的函数的参数。
* 函数体;
* 以及可选返回值的返回对象
### 1.2.4.3 参数
必选参数(位置参数)
In [24]:
```
def double_it(x):
return x * 2
double_it(3)
```
Out[24]:
```
6
```
In [25]:
```
double_it()
```
```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-25-51cdedbb81b0> in <module>()
----> 1 double_it()
TypeError: double_it() takes exactly 1 argument (0 given)
```
可选参数(关键词和命名参数)
In [26]:
```
def double_it(x=2):
return x * 2
double_it()
```
Out[26]:
```
4
```
In [27]:
```
double_it(3)
```
Out[27]:
```
6
```
关键词参数允许你设置特定默认值。
**警告:**默认值在函数定义时被评估,而不是在调用时。如果使用可变类型(即字典或列表)并在函数体内修改他们,这可能会产生问题,因为这个修改会在函数被引用的时候一直持续存在。
在关键词参数中使用不可变类型:
In [2]:
```
bigx = 10
def double_it(x=bigx):
return x * 2
bigx = 1e9 # 现在真的非常大
double_it()
```
Out[2]:
```
20
```
在关键词参数中使用可变类型(并且在函数体内修改它):
In [3]:
```
def add_to_dict(args={'a': 1, 'b': 2}):
for i in args.keys():
args[i] += 1
print args
add_to_dict
```
Out[3]:
```
<function __main__.add_to_dict>
```
In [4]:
```
add_to_dict()
```
```
{'a': 2, 'b': 3}
```
In [5]:
```
add_to_dict()
```
```
{'a': 3, 'b': 4}
```
In [6]:
```
add_to_dict()
```
```
{'a': 4, 'b': 5}
```
更复杂的例子,实现Python的切片:
In [7]:
```
def slicer(seq, start=None, stop=None, step=None):
"""Implement basic python slicing."""
return seq[start:stop:step]
rhyme = 'one fish, two fish, red fish, blue fish'.split()
rhyme
```
Out[7]:
```
['one', 'fish,', 'two', 'fish,', 'red', 'fish,', 'blue', 'fish']
```
In [8]:
```
slicer(rhyme)
```
Out[8]:
```
['one', 'fish,', 'two', 'fish,', 'red', 'fish,', 'blue', 'fish']
```
In [9]:
```
slicer(rhyme, step=2)
```
Out[9]:
```
['one', 'two', 'red', 'blue']
```
In [10]:
```
slicer(rhyme, 1, step=2)
```
Out[10]:
```
['fish,', 'fish,', 'fish,', 'fish']
```
In [11]:
```
slicer(rhyme, start=1, stop=4, step=2)
```
Out[11]:
```
['fish,', 'fish,']
```
关键词参数的顺序**不**重要:
In [12]:
```
slicer(rhyme, step=2, start=1, stop=4)
```
Out[12]:
```
['fish,', 'fish,']
```
但是,最好是使用与函数定义相同的顺序。
_关键词参数_是特别方便的功能,可以用可变数量的参数来定义一个函数,特别是当函数据绝大多数调用都会使用默认值时。
### 1.2.4.4 值传递
可以在一个函数内部改变变量的值吗?大多数语言(C、Java...)区分了“值传递“和”引用传递“。在Python中,没有严格的这种区分,并且视你的变量是否会修改而有一些不同。幸运的是,这些情况存在明确的规则。
函数的参数是对象的引用,传递的是值。当你像一个函数传递了一个变量,Python传递的是对象的引用,这个对象引用的变量(值)。而不是变量本身。
如果**值**传递给函数的值是不可变的,那么这个函数并不会改变调用者的变量。如果**值**是可变的,那么函数将可能在原地修改调用者的变量。
In [13]:
```
def try_to_modify(x, y, z):
x = 23
y.append(42)
z = [99] # 新引用
print(x)
print(y)
print(z)
a = 77 # 不可变变量
b = [99] # 可变变量
c = [28]
try_to_modify(a, b, c)
```
```
23
[99, 42]
[99]
```
In [14]:
```
print(a)
```
```
77
```
In [15]:
```
print(b)
```
```
[99, 42]
```
In [16]:
```
print(c)
```
```
[28]
```
函数有名为_local namespace_的本地变量表。
变量X只存在于函数try_to_modify内部。
### 1.2.4.5 全局变量
在函数外定义的变量可以在函数内引用:
In [18]:
```
x = 5
def addx(y):
return x + y
addx(10)
```
Out[18]:
```
15
```
但是,这些全局变量不能在函数内修改,除非在函数内声明**global**。
这样没用:
In [19]:
```
def setx(y):
x = y
print('x is %d' % x)
setx(10)
```
```
x is 10
```
In [20]:
```
x
```
Out[20]:
```
5
```
这样可以:
In [21]:
```
def setx(y):
global x
x = y
print('x is %d' % x)
setx(10)
```
```
x is 10
```
In [22]:
```
x
```
Out[22]:
```
10
```
### 1.2.4.6 可变数量参数
函数的特殊形式:
```
- *args:封装成元组的任意数量的位置参数
- **kwargs:封装成字典的任意数量的关键词参数
```
In [23]:
```
def variable_args(*args, **kwargs):
print 'args is', args
print 'kwargs is', kwargs
variable_args('one', 'two', x=1, y=2, z=3)
```
```
args is ('one', 'two')
kwargs is {'y': 2, 'x': 1, 'z': 3}
```
### 1.2.4.7 Docstrings
关于函数作用及参数的文档。通常惯例:
In [24]:
```
def funcname(params):
"""Concise one-line sentence describing the function.
Extended summary which can contain multiple paragraphs.
"""
# 函数体
pass
funcname?
```
```
Type: function
Base Class: type 'function'>
String Form: <function funcname at 0xeaa0f0>
Namespace: Interactive
File: <ipython console>
Definition: funcname(params)
Docstring:
Concise one-line sentence describing the function.
Extended summary which can contain multiple paragraphs.
```
**注 Docstring 指导**
为了标准化,Docstring 惯例页面为Python Docstring相关的含义及惯例提供了文档。
Numpy和Scipy模块也为科学计算函数定义了清晰的标准,你可能想要在自己的函数中去遵循,这个标准有参数部分,例子部分等。见[http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard](http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard) 及 [http://projects.scipy.org/numpy/browser/trunk/doc/example.py#L37](http://projects.scipy.org/numpy/browser/trunk/doc/example.py#L37)
### 1.2.4.8 函数作为对象
函数是一级对象,这意味着他们可以是:
```
- 可以被赋值给变量
- 列表的一个项目(或任何集合)
- 作为参数传递给另一个函数
```
In [26]:
```
va = variable_args
va('three', x=1, y=2)
```
```
args is ('three',)
kwargs is {'y': 2, 'x': 1}
```
### 1.2.4.9 方法
方法是对象的函数。你已经在我们关于列表、字典和字符等...的例子上看到了。
### 1.2.4.10\. 练习
**练习:斐波那契数列**
写一个函数来展示斐波那契数列的前n个项目,定义如下:
```
- u_0 = 1; u_1 = 1
- u_(n+2) = u_(n+1) + u_n
```
**练习:快速排序**
实现快速排序算法,定义来自wikipedia:
function quicksort(array)
```
var list less, greater if length(array) < 2
return array
select and remove a pivot value pivot from array for each x in array
if x < pivot + 1 then append x to less else append x to greater
return concatenate(quicksort(less), pivot, quicksort(greater))
```