前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据分析从入门到“入坑“系列】利用Python学习数据分析-Python函数

【数据分析从入门到“入坑“系列】利用Python学习数据分析-Python函数

作者头像
天道Vax的时间宝藏
发布2021-08-11 15:58:37
3570
发布2021-08-11 15:58:37
举报

函数是Python中最主要也是最重要的代码组织和复用手段。作为最重要的原则,如果你要重复使用相同或非常类似的代码,就需要写一个函数。通过给函数起一个名字,还可以提高代码的可读性。

函数使用def关键字声明,用return关键字返回值:

代码语言:javascript
复制
def my_function(x, y, z=1.5):
    if z > 1:
        return z * (x + y)
    else:
        return z / (x + y)

同时拥有多条return语句也是可以的。如果到达函数末尾时没有遇到任何一条return语句,则返回None。

函数可以有一些位置参数(positional)和一些关键字参数(keyword)。关键字参数通常用于指定默认值或可选参数。在上面的函数中,x和y是位置参数,而z则是关键字参数。也就是说,该函数可以下面这两种方式进行调用:

代码语言:javascript
复制
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) 这种写法可以提高可读性。

命名空间、作用域,和局部函数

函数可以访问两种不同作用域中的变量:全局(global)和局部(local)。Python有一种更科学的用于描述变量作用域的名称,即命名空间(namespace)。任何在函数中赋值的变量默认都是被分配到局部命名空间(local namespace)中的。局部命名空间是在函数被调用时创建的,函数参数会立即填入该命名空间。在函数执行完毕之后,局部命名空间就会被销毁(会有一些例外的情况,具体请参见后面介绍闭包的那一节)。看看下面这个函数:

代码语言:javascript
复制
def func():
    a = []
    for i in range(5):
        a.append(i)

调用func()之后,首先会创建出空列表a,然后添加5个元素,最后a会在该函数退出的时候被销毁。假如我们像下面这样定义a:

代码语言:javascript
复制
a = []
def func():
    for i in range(5):
        a.append(i)

虽然可以在函数中对全局变量进行赋值操作,但是那些变量必须用global关键字声明成全局的才行:

代码语言:javascript
复制
In [168]: a = None
​
In [169]: def bind_a_variable():
   .....:     global a
   .....:     a = []
   .....: bind_a_variable()
   .....:
​
In [170]: print(a)
[]

注意:我常常建议人们不要频繁使用global关键字。因为全局变量一般是用于存放系统的某些状态的。如果你发现自己用了很多,那可能就说明得要来点儿面向对象编程了(即使用类)。

返回多个值

在我第一次用Python编程时(之前已经习惯了Java和C++),最喜欢的一个功能是:函数可以返回多个值。下面是一个简单的例子:

代码语言:javascript
复制
def f():
    a = 5
    b = 6
    c = 7
    return a, b, c
​
a, b, c = f()

在数据分析和其他科学计算应用中,你会发现自己常常这么干。该函数其实只返回了一个对象,也就是一个元组,最后该元组会被拆包到各个结果变量中。在上面的例子中,我们还可以这样写:

代码语言:javascript
复制
return_value = f()

这里的return_value将会是一个含有3个返回值的三元元组。此外,还有一种非常具有吸引力的多值返回方式——返回字典:

代码语言:javascript
复制
def f():
    a = 5
    b = 6
    c = 7
    return {'a' : a, 'b' : b, 'c' : c}

取决于工作内容,第二种方法可能很有用。

函数也是对象

由于Python函数都是对象,因此,在其他语言中较难表达的一些设计思想在Python中就要简单很多了。假设我们有下面这样一个字符串数组,希望对其进行一些数据清理工作并执行一堆转换:

代码语言:javascript
复制
In [171]: states = ['   Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda',
   .....:           'south   carolina##', 'West virginia?']

不管是谁,只要处理过由用户提交的调查数据,就能明白这种乱七八糟的数据是怎么一回事。为了得到一组能用于分析工作的格式统一的字符串,需要做很多事情:去除空白符、删除各种标点符号、正确的大写格式等。做法之一是使用内建的字符串方法和正则表达式re模块:

代码语言:javascript
复制
import re
​
def clean_strings(strings):
    result = []
    for value in strings:
        value = value.strip()
        value = re.sub('[!#?]', '', value)
        value = value.title()
        result.append(value)
    return result

结果如下所示:

代码语言:javascript
复制
In [173]: clean_strings(states)
Out[173]: 
['Alabama',
 'Georgia',
 'Georgia',
 'Georgia',
 'Florida',
 'South   Carolina',
 'West Virginia']

其实还有另外一种不错的办法:将需要在一组给定字符串上执行的所有运算做成一个列表:

代码语言:javascript
复制
def remove_punctuation(value):
    return re.sub('[!#?]', '', value)
​
clean_ops = [str.strip, remove_punctuation, str.title]
​
def clean_strings(strings, ops):
    result = []
    for value in strings:
        for function in ops:
            value = function(value)
        result.append(value)
    return result

然后我们就有了:

代码语言:javascript
复制
In [175]: clean_strings(states, clean_ops)
Out[175]: 
['Alabama',
 'Georgia',
 'Georgia',
 'Georgia',
 'Florida',
 'South   Carolina',
 'West Virginia']

这种多函数模式使你能在很高的层次上轻松修改字符串的转换方式。此时的clean_strings也更具可复用性!

还可以将函数用作其他函数的参数,比如内置的map函数,它用于在一组数据上应用一个函数:

代码语言:javascript
复制
In [176]: for x in map(remove_punctuation, states):
   .....:     print(x)
Alabama 
Georgia
Georgia
georgia
FlOrIda
south   carolina
West virginia

匿名(lambda)函数

Python支持一种被称为匿名的、或lambda函数。它仅由单条语句组成,该语句的结果就是返回值。它是通过lambda关键字定义的,这个关键字没有别的含义,仅仅是说“我们正在声明的是一个匿名函数”。

代码语言:javascript
复制
def short_function(x):
    return x * 2
​
equiv_anon = lambda x: x * 2

本书其余部分一般将其称为lambda函数。它们在数据分析工作中非常方便,因为你会发现很多数据转换函数都以函数作为参数的。直接传入lambda函数比编写完整函数声明要少输入很多字(也更清晰),甚至比将lambda函数赋值给一个变量还要少输入很多字。看看下面这个简单得有些傻的例子:

代码语言:javascript
复制
def apply_to_list(some_list, f):
    return [f(x) for x in some_list]
​
ints = [4, 0, 1, 5, 6]
apply_to_list(ints, lambda x: x * 2)

虽然你可以直接编写[x *2for x in ints],但是这里我们可以非常轻松地传入一个自定义运算给apply_to_list函数。

再来看另外一个例子。假设有一组字符串,你想要根据各字符串不同字母的数量对其进行排序:

代码语言:javascript
复制
In [177]: strings = ['foo', 'card', 'bar', 'aaaa', 'abab']

这里,我们可以传入一个lambda函数到列表的sort方法:

代码语言:javascript
复制
In [178]: strings.sort(key=lambda x: len(set(list(x))))
​
In [179]: strings
Out[179]: ['aaaa', 'foo', 'abab', 'bar', 'card']

笔记:lambda函数之所以会被称为匿名函数,与def声明的函数不同,原因之一就是这种函数对象本身是没有提供名称name属性。

柯里化:部分参数应用

柯里化(currying)是一个有趣的计算机科学术语,它指的是通过“部分参数应用”(partial argument application)从现有函数派生出新函数的技术。例如,假设我们有一个执行两数相加的简单函数:

代码语言:javascript
复制
def add_numbers(x, y):
    return x + y

通过这个函数,我们可以派生出一个新的只有一个参数的函数——add_five,它用于对其参数加5:

代码语言:javascript
复制
add_five = lambda y: add_numbers(5, y)

add_numbers的第二个参数称为“柯里化的”(curried)。这里没什么特别花哨的东西,因为我们其实就只是定义了一个可以调用现有函数的新函数而已。内置的functools模块可以用partial函数将此过程简化:

代码语言:javascript
复制
from functools import partial
add_five = partial(add_numbers, 5)

生成器

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-06-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 命名空间、作用域,和局部函数
  • 返回多个值
  • 函数也是对象
  • 匿名(lambda)函数
  • 柯里化:部分参数应用
  • 生成器
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档