1.2函数
函数是Python中最重要、最基础的代码组织和代码复用方式。
函数声明时使用def关键字,返回时使用return关键字
def my_function(x, y, z = 1.5): if z > 1: return z*(x + y) else: return z/(x + y)
有多条返回语句是没有问题的,如果Python达到函数尾部还没有遇到return语句,就会自动返回None。
每个函数都可以有位置参数和关键字参数。关键字参数最常用于指定默认值或可选参数。在前面的函数中x和y是位置参数,z是关键字参数。这意味着函数可以通过以下任意一种方式进行调用:
my_function(5, 6, z = 0.7) my_function(3.14, 7, 3.5) my_function(10, 20)
函数参数的主要限制是关键字参数必须跟在位置参数后(如果有的话)。可以按照任意的顺序给函数传参,这意味这我们不需要强行记住参数顺序,只需记住参数名即可。
my_function(x = 5, y = 6, z = 7)
my_function(y = 6, x = 5, z = 7)
在部分场景中,这种传参方式有助于代码可读性。
1.2.1 命名空间、作用域和本地函数
函数有两种连接变量的方式:全局、本地。在Python中另一种更贴切的描述变量作用域的名称是命名空间。在函数内部,任意变量都是默认分配到本地命名空间的。本地命名空间是在函数被调用时生成的,并立即由函数的参数填充。当函数执行结束后,本地命名空间就会销毁,考虑以下函数:
def func(): a = [] for i in range(5): a.append(i)
当调用func( )时,空的列表a会被创建,5个元素被添加到列表,之后a会在函数退出时被销毁。假设像下面这样声明a:
a = [] def func(): for i in range(5): a.append(i)
在函数外部给变量赋值是可以的,但是那些变量必须使用global关键字声明为全局变量。
In [1]: a = None In [2]: def bind_a_variable(): ...: global a ...: a = [] ...: bind_a_variable() In [3]: print(a) []
global关键字:通常全局变量用来存储系统中的某些状态。如果发现用了大量的全局变量,则应该考虑使用类。
1.2.2 返回多个值
def f(): a = 5 b = 6 c = 7 return a, b, c
a, b, c = f()
上面的代码可以用下面的代码来代替
return_value = f()
这里return_value是一个包含3个元素的元组。想之前那样一次返回多个值的还有一种潜在的实现
def f(): a = 5 b = 6 c = 7 return{'a' : a, 'b' : b, 'c' : c}
1.2.3 匿名(Lambda)函数
Python支持所谓的匿名或者lambda函数。匿名函数是一种通过单个语句生成函数的方式,其结果是返回值。匿名函数使用lambda关键字定义,该关键字仅表达“我们声明一个匿名函数”的意思
def short_function(x): return x * 2 equiv_anon = lambda x : x * 2
匿名函数在数据分析中非常方便,因为在很多案例中数据变形函数都可以作为函数的参数。匿名函数代码量小,将它作为参数进行传值,比写一个完整的函数或者将匿名函数赋值给局部变量更好
1.2.4 柯里化:部分参数应用
柯里化表示通过部分参数的应用方式从已有的函数中衍生出新的函数。例如我闷油一个不重要的函数,其功能是将两个数加在一起:
def add_numbers(x, y): return x + y
使用这个函数我们可以衍生出一个只有一个变量的新函数,add_five,可以给参数加上5
add_five = lambda y : add_numbens(5, y)
第二个参数对于函数add_numbens就是柯里化了。这里并没有什么神奇的地方,我们只是定义了一个新函数。这个新函数调用了已经存在的函数。内建的functools模块可以使用pratial函数简化这种处理
from functools import partial add_five = partial(add_numbers, 5)
1.2.5 生成器
通过一致的方式遍历序列,例如列表中的对象或者文件中的一行行内容是Python的一个重要特性。这个特性是通过迭代器协议来实现的,迭代器协议是一种令对象可遍历的通用方式。例如遍历一个字典,获得字典的键:
In [1]: some_dict = {'a' : 1, 'b' : 2, 'c' : 3} In [2]: for key in some_dict: ...: print(key) ...: a b c
当你写下for key in some_dict的语句时,Python解释器首先尝试根据some_dict生成一个迭代器:
In [3]: dict_iterator = iter(some_dict) In [4]: dict_iterator Out[4]: <dict_keyiterator at 0x215855e84a8
迭代器就是一种用于上下文中(比如for循环)向Python解释器生成对象的对象。大部分以列表或列表型对象为参数的方法都可以接受任意的迭代器对象。包括内建方法比如,min、max和sum以及类型构造函数比如list和tuple。
In [5]: list(dict_iterator) Out[5]: ['a', 'b', 'c']
生成器是构造新的可便利对象的一种非常简洁的方式。普通函数执行并一次返回单个结果,生成器则是一次返回一个多结果序列,在每一个元素产生之后暂停,直到下一个请求。如需创建一个生成器,只需要在函数中将返回关键字return替换为yield关键字
def squares(n = 10): print('Generating squares from 1 to {0}'.format(n ** 2)) for i in range(1, n+1): yield i ** 2
当实际调用生成器时,代码并不会立即执行:
In [8]: gen = squares() In [9]: gen Out[9]: <generator object squares at 0x0000021585BE2138>
直到请求生成器中的元素时,它才会执行它的代码
In [17]: for x in gen: ...: print(x, end = ' ') ...: Generating squares from 1 to 100 1 4 9 16 25 36 49 64 81 100
1.2.5.1生成器表达式
用生成器表达式来创建生成器更为简单。生成器表达式与列表、字典、集合的推导式很类似,创建一个生成器表达式,只需要将刘表推导是的中的小括号替换为大括号。
In [4]: gen = (x ** 2 for x in range(100)) In [5]: gen Out[5]: <generator object <genexpr> at 0x000002153AE87228>
上面的代码等价于下面更为复杂的生成器
def _make_gen(): for i in range(100): yield x ** 2 gen = _make_gen()
很多情况下,生成器表达式可以作为函数用于替代列表推导式:
In [8]: sum(x ** 2 for x in range(100)) Out[8]: 328350 In [9]: dict((i, i +2) for i in range(5)) Out[9]: {0: 2, 1: 3, 2: 4, 3: 5, 4: 6}