【AICAMP —— Python】入门系列!(3. Python函数编程)

2021-05-28 11:16:53 浏览数 (1)

Python函数编程

1. 介绍

Python与其它编程语言一样,可以直接调用我们写好的函数,或者已经实现的函数。如我们可以使用系统自带的函数,求一个数的绝对值,使用abs函数,对其传入参数,得到输出。

代码语言:javascript复制
>>> abs(-100)
100
>>> abs(100)
100

对于函数而言,传入的参数也是有限制的,如果传入的参数不对,那么就会输出错误信息。如下:

代码语言:javascript复制
>>> abs(-1, 100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)

abs需要传入一个参数,若我们传入2个参数,就会报错。 如果参数的参数个数是正常的,但是传入的类型不对,也是会报错的,如下:

代码语言:javascript复制
>>> abs('kobe')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'

如max函数,可以接入多个参数:

代码语言:javascript复制
>>> max(1,2,3)
3

同样,python中的类型可以相互之间进行转换。如int()函数可以将其它的数据类型转换成整型。

代码语言:javascript复制
>>> max(1,2,3)
3
>>> int('123')
123
>>> int(12.34)
12
>>> str(123)
'123'
>>> bool(1)
True
>>> bool(0)
False

其中在python中0为False,非0为True。 同时,函数名其实就是指向一个函数的引用,完全可以把一个函数名赋给一个变量,然后在使用这个变量来调用这个函数。

代码语言:javascript复制
>>> a = abs
>>> a(-1)
1

2. 自定义函数:

python中,我们定义函数需要使用def语句,依次写出函数名,括号,括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

代码语言:javascript复制
def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

空函数,如果暂时还不确定在某一张判断条件需要做什么,这时候可以使用pass来占位。先使用一个pass,使代码可以跑起来。

代码语言:javascript复制
age = 3
if age > 0:
    pass
else:
    age  = 1

同样,函数返回值,可以返回多个,如下图:

代码语言:javascript复制
def move(a, b):
    x = a   b
    y = a - b
    return x, y

函数的参数: 定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。

Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。 默认参数:

如果我们需要经常计算

x^2

,那么我们可能定义如下一个函数:

代码语言:javascript复制
# -*- coding:utf-8 -*-
def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * n
    return s
value1 = power(3, 2) #把power(3,2)的返回值赋给value1
#or
value2 = power(3)

在我们调用的时候,如果我们不显示传入n的值,那么默认n为2,若我们需要计算

x^3

,我们可以传入参数,修改这个n的值,这个n的值也叫默认参数

同样默认参数可以设置多个,如果不按照顺序进行输入的时候,需要将变量名一起写到调用函数的那一行。

代码语言:javascript复制
def enroll(name, gender, age=6, city="beijing"):
    print('name', name)
    print('gender', gender)
    print('age', age)
    print('city', city)
enroll('kobe', 'M', city='losangles', age=42)

默认参数很有用,但使用不当,也会掉坑里。默认参数有个最大的坑,演示如下: 先定义一个函数,传入一个list,添加一个'END'再返回:

代码语言:javascript复制
>>> def add_end(l=[]):
...    l.append('END')
...    return l
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

定义默认参数要牢记一点:默认参数必须指向不变对象! 要修改上面的例子,我们可以用None这个不变对象来实现:

代码语言:javascript复制
>>> def add_end(l=None):
...    if L is None:
...        L = []
...    L.append('END')
...    return L
>>> add_end()
['END']
>>> add_end()
['END']
>>> add_end()
['END']

3. 可变参数

Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。 我们以数学题为例子,给定一组数字a,b,c……,请计算

a^2 b^2 c^2 ……

。 要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个listtuple传进来,这样,函数可以定义如下:

代码语言:javascript复制
def calc(numbers):
    sum = 0
    for n in numbers:
        sum = sum   n * n
    return sum

在我们调用的时候,我们传入的是可以遍历的list或者tuple实例。 如果利用可变参数,可以简化函数的调用。

定义可变参数和定义一个listtuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

代码语言:javascript复制
>>> def calc(*numbers):
...     sum = 0
...     for n in numbers:
...         sum = sum   n * n
...     return sum
>>> calc(1, 2, 3)
14
>>> calc()
0

如果有一个list或者tuple,要调用一个可变参数,可以通过:

代码语言:javascript复制
>>> numbers = [1, 2, 3]
>>> calc(*numbers)
14

4. 关键字参数:

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例: 如下:

代码语言:javascript复制
def test(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

在使用该函数时,可以只传入必选参数:

代码语言:javascript复制
>>> test('Michael', 20)
name: Michael age: 20 other: {}

你也可以传入任意关键字参数:

代码语言:javascript复制
>>> test('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> test('curry', 34, gender='M', job='Player')
name: curry age: 34 other: {'gender': 'M', 'job': 'Player'}

关键字参数可以扩展函数的功能。比如,在test函数中,我们保证能接收到name和age这两个参数,同时也保留了开放的关键字接口,可以 扩展性的输入其它字段的信息。

同时,我们也可以先使用一个dict来装一些关键字的参数,再把这个dict给喂到这个函数的关键字参数中去:

代码语言:javascript复制
>>> other_keys = {'city': 'shanghai', 'job':'coder'}
>>> test('zone', 24, city=other_keys['city'], job=other_keys['job'])
name: zone age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

当然,我们可以使用更为简洁的写法:

代码语言:javascript复制
>>> other_keys = {'city': 'shanghai', 'job':'coder'}
>>> test('zone', 24, **other_keys)
name: zone age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

关键字参数的限制:

如果想要限制关键字参数的名字,那么可以命名关键字参数,如我们在test函数中只接收cityjob来作为 关键字,以这种方式定义的函数为:

代码语言:javascript复制
def test(name, age, *, city, job):
    print(name, age, city, job)

调用方式如下:

代码语言:javascript复制
>>> test('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

命名关键字参数的时候可以有缺失值,从而简化调用:

代码语言:javascript复制
def test(name, age, *, city='Beijing', job):
    print(name, age, city, job)

调用的方法可以为:

代码语言:javascript复制
>>> test('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer

5. 匿名函数

python使用lambda来创建匿名函数。

  • lambda知识一个表达式,但是它的函数体比def简单得多。
  • lambda主体是一个表达式,因为一般这里面的内容较少,所以都只能封装有限的逻辑代码进去。
  • lambda函数里面的变量同样具有自己的命名空间,不能访问自己函数列表之外或者全局命名空间里的参数。
  • lambda函数在C 等函数中也有,但是与之不同的是,C 中的lambda函数是为了调用小函数不占用栈内存而提高运行效率的。

看一个代码的实例:

代码语言:javascript复制
sum = lambda x1, x2 : x1   x2

则我们在调用的时候可以:

代码语言:javascript复制
ret1 = sum(10, 20)

6. 变量作用域

变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称。两种最基本的变量作用域如下:

  • 全局变量
  • 局部变量

定义在函数内部的变量拥有一个局部作用域(只在函数里里面有效),定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。如下实例:

代码语言:javascript复制
#首先定义一个全局的变量,保存函数之和
total_sum = 0
def sum(x1, x2):
  total_sum = x1   x2
  print("函数内是局部变量", total_sum)
  return total_sum
#调用sum函数
ret_sum = sum(10, 20)
print("函数外是全局变量", total_sum)

则函数输出为:

代码语言:javascript复制
函数内是局部变量 :  30
函数外是全局变量 :  0

7. 迭代器与生成器

迭代器

迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。同时,迭代器只能往前不会后退。迭代器有两个基本的方法:iter()next()。对于字符串,列表或元组对象都可用于创建迭代器。

代码语言:javascript复制
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print (x, end=" ")
代码语言:javascript复制
1 2 3 4
代码语言:javascript复制
import sys         # 引入 sys 模块
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
 
while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()

执行以上程序,输出结果如下:

代码语言:javascript复制
1
2
3
4

生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。生成器是一个返回迭代器的函数,只能用于迭代操作。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。因此,当我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!

创建生成器的方法有两种:

  • 把一个列表生成式的[]改成(),就创建了一个generator g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x104f1fc10>

8.map与reduce

map() 会根据提供的函数对指定序列做映射。

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

代码示例如下:

代码语言:javascript复制
items = [1, 2, 3, 4, 5]
def f(x):
    return x**2
#使用map函数,返回的是一个迭代器,再使用list转成列表形式
squared = list(map(f, items))
print(squared)

输出结果为:

代码语言:javascript复制
[1, 4, 9, 16, 25]

Reduce()

reduce的工作过程是 :在迭代序列的过程中,首先把 前两个元素(只能两个)传给 函数,函数加工后,然后把 得到的结果和第三个元素 作为两个参数传给函数参数, 函数加工后得到的结果又和第四个元素 作为两个参数传给函数参数,依次类推。

reduce函数不能直接用,需要使用from functools import reduce进行导入!

比如说我想求10的阶乘,那么我们可以用reduce来做:

代码语言:javascript复制
from functools import reduce 
# 定义函数
def f(x,y):
    return x*y
# 定义序列,含1~10的元素
items = range(1,11)
# 使用reduce方法
result = reduce(f,items)
print(result)

关于我

欢迎加我微信,每天16个小时在线

0 人点赞