1.2.4. 定义函数

1.2.4.1. 函数定义

In [56]: def test():
....: print('in test function')
....:
....:
In [57]: test()
in test function

警告

函数代码块必须和其他控制流代码块一样缩进。

1.2.4.2. 返回语句

函数可以可选地返回值。

In [6]: def disk_area(radius):
...: return 3.14 * radius * radius
...:
In [8]: disk_area(1.5)
Out[8]: 7.0649999999999995

注意

默认情况下,函数返回None

注意

注意定义函数的语法:

  • def关键字;
  • 后跟函数的名称,然后
  • 函数的参数在括号中,括号后面有一个冒号。
  • 函数体;
  • return object用于可选择地返回值。

1.2.4.3. 参数

强制参数(位置参数)

In [81]: def double_it(x):
....: return x * 2
....:
In [82]: double_it(3)
Out[82]: 6
In [83]: double_it()
---------------------------------------------------------------------------
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: double_it() takes exactly 1 argument (0 given)

可选参数(关键字或命名参数)

In [84]: def double_it(x=2):
....: return x * 2
....:
In [85]: double_it()
Out[85]: 4
In [86]: double_it(3)
Out[86]: 6

关键字参数允许你指定默认值

警告

默认值在定义函数时求值,而不是在调用时求值。当使用可变类型(例如字典或列表)并在函数体中修改它们时,这可能是有问题的,因为修改将在函数的每次调用之间持续存在。

在关键字参数中使用不可变类型:

In [124]: bigx = 10
In [125]: def double_it(x=bigx):
.....: return x * 2
.....:
In [126]: bigx = 1e9 # Now really big
In [128]: double_it()
Out[128]: 20

在关键字参数中使用可变类型(并在函数体中修改它):

In [2]: def add_to_dict(args={'a': 1, 'b': 2}):
...: for i in args.keys():
...: args[i] += 1
...: print args
...:
In [3]: 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 [98]: def slicer(seq, start=None, stop=None, step=None):
....: """Implement basic python slicing."""
....: return seq[start:stop:step]
....:
In [101]: rhyme = 'one fish, two fish, red fish, blue fish'.split()
In [102]: rhyme
Out[102]: ['one', 'fish,', 'two', 'fish,', 'red', 'fish,', 'blue', 'fish']
In [103]: slicer(rhyme)
Out[103]: ['one', 'fish,', 'two', 'fish,', 'red', 'fish,', 'blue', 'fish']
In [104]: slicer(rhyme, step=2)
Out[104]: ['one', 'two', 'red', 'blue']
In [105]: slicer(rhyme, 1, step=2)
Out[105]: ['fish,', 'fish,', 'fish,', 'fish']
In [106]: slicer(rhyme, start=1, stop=4, step=2)
Out[106]: ['fish,', 'fish,']

关键字参数的顺序无关紧要:

In [107]: slicer(rhyme, step=2, start=1, stop=4)
Out[107]: ['fish,', 'fish,']

但是最好使用与函数定义相同的顺序。

关键字参数是定义具有可变数量参数的函数的非常方便的功能,特别是在大多数函数调用中使用默认值时。

1.2.4.4. 按值传递

你可以修改函数内部变量的值吗?大多数语言(C、Java,...)区分“传递值”和“通过引用传递”。在Python中,这样的区别是有点人为的,你的变量是否将被修改有点微妙。幸运的是,存在明确的规则。

函数的参数是对象的引用,它们通过值传递。当你将一个变量传递给一个函数时,python将引用传递给变量引用的对象(value)。不是变量本身。

如果在函数中传递的是不可变的,该函数不会修改调用者的变量。如果是可变的,则函数可以就地修改调用者的变量:

>>> def try_to_modify(x, y, z):
... x = 23
... y.append(42)
... z = [99] # new reference
... print(x)
... print(y)
... print(z)
...
>>> a = 77 # immutable variable
>>> b = [99] # mutable variable
>>> c = [28]
>>> try_to_modify(a, b, c)
23
[99, 42]
[99]
>>> print(a)
77
>>> print(b)
[99, 42]
>>> print(c)
[28]

函数具有称为本地命名空间的局部变量表。

变量x只存在于函数try_to_modify内。

1.2.4.5. 全局变量

在函数外声明的变量可以在函数内引用:

In [114]: x = 5
In [115]: def addx(y):
.....: return x + y
.....:
In [116]: addx(10)
Out[116]: 15

但是这些“全局”变量不能在函数中修改,除非在函数中声明为全局

这不会改变X:

In [117]: def setx(y):
.....: x = y
.....: print('x is %d' % x)
.....:
.....:
In [118]: setx(10)
x is 10
In [120]: x
Out[120]: 5

这将改变X:

In [121]: def setx(y):
.....: global x
.....: x = y
.....: print('x is %d' % x)
.....:
.....:
In [122]: setx(10)
x is 10
In [123]: x
Out[123]: 10

1.2.4.6. 可变数量的参数

特殊形式的参数:
  • *args:任意数量的位置参数,封装到一个元组中
  • **kwargs:任意数量的关键字参数,封装在一个字典中
In [35]: def variable_args(*args, **kwargs):
....: print 'args is', args
....: print 'kwargs is', kwargs
....:
In [36]: 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 [67]: def funcname(params):
....: """Concise one-line sentence describing the function.
....:
....: Extended summary which can contain multiple paragraphs.
....: """
....: # function body
....: pass
....:
In [68]: 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文档字符串相关联的语义和约定。

此外,Numpy和Scipy模块定义了用于文档化科学计算函数的精确标准,你可能想要让你自己的函数遵从它们,例如使用Parameters部分、Examples部分,等等。请参阅http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standardhttp://projects.scipy.org/numpy/browser/trunk/doc/example .py#L37

1.2.4.8. 函数是对象

函数是第一等对象,这意味着它们可以:
  • 赋值给一个变量
  • 列表(或任何集合)中的元素
  • 作为参数传递给另一个函数。
In [38]: va = variable_args
In [39]: va('three', x=1, y=2)
args is ('three',)
kwargs is {'y': 2, 'x': 1}

1.2.4.9. 方法

方法是附属在对象上的函数。你已在我们的列表字典字符串...等示例中看到这些内容。

1.2.4.10. 练习

练习:斐波那契序列

编写一个函数,显示Fibonacci序列的前n项,定义如下:

  • u_0 = 1; u_1 = t6>
  • u_(n + 2) = u_(n + 1) + / t5>

练习:Quicksort

实现快速排序算法,由维基百科定义

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))