Python语法基础之. Task05 函数与Lambda表达式(6-7/17)

Python基础—— Task05 函数与Lambda表达式

  • 1. 函数
    • 1.1 定义函数
    • 1.2 函数调用
      • 1.2.1 函数调用
      • 1.2.2 函数的返回值
      • 1.2.3 变量作用域
      • 1.2.4 内嵌函数
      • 1.2.5 闭包
      • 1.2.6 循环与递归
    • 1.3 参数传递
      • 1.3.1 可更改(mutable)与不可更改(immutable)对象
    • 1.4 函数参数
      • 1.4.1 必需参数/位置参数
      • 1.4.2 默认参数
      • 1.4.3 可变参数/不定长参数
        • 1.4.3.1 * 参数形式
        • 1.4.3.2 ** 参数形式
      • 1.4.4 关键字参数
      • 1.4.5 命名关键字参数
      • 1.4.6 参数组合
  • 2. Lambda函数
    • 2.1 语法
    • 2.2 匿名函数 VS 正规函数
    • 2.3 Lambda表达式的应用

1. 函数

1.1 定义函数

语法:

def 函数名(参数列表):
    函数体
  • 默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的
  • 如果没有return 语句,会返回None;return None 简写为return

实例

def print_welcome(name):
    print("Welcome", name)

def SuccessEquation(factor1, factor2):
    return str(factor1 + " Plus " + factor2 + " = Success")

print_welcome("IvanZhang")
X = 'Vision'
Y = 'Endeavor'
print(SuccessEquation(X, Y))
# Welcome IvanZhang
# Vision Plus Endeavor = Success

其他:

  • 空函数:用pass语句作为占位符
  • 数据类型检查可以使用内置函数isinstance()实现;
x = 'red'
if not isinstance(x, (int, float)):
    raise TypeError('bad operand type')
# TypeError: bad operand type

1.2 函数调用

1.2.1 函数调用

首先,定义一个函数:给了函数一个名称,指定了函数里包含的参数,和代码块结构。
这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从 Python 命令提示符执行。

其次,python支持非常灵活定义的函数,而且本身内置了很多有用的函数
使用方法:

  • 官方文档 查询
  • help(func)查看对应函数func的帮助文档

“函数名其实就是指向一个函数对象的引用”,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”

f = float
print(f(1.2342342352345235234234233984028953))	    
# 输出 1.2342342352345235

1.2.2 函数的返回值

Sample1:

def add(a, b):
    return a + b

print(add(1, 2))  # 3
print(add([1, 2, 3], [4, 5, 6]))  # [1, 2, 3, 4, 5, 6]

Sample2:

def back():
    return [1, 'python practice', 3.14]


print(back())  # [1, 'python practice', 3.14]

Sample3:

def back():
    return 1, 'python practice', 3.14

print(back())  # (1, 'python practice', 3.14)

Sample4:

def printme(str):
    print(str)

temp = printme('hello') # hello
print(temp) # None
print(type(temp))  # 

1.2.3 变量作用域

  • Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的
  • 定义在函数内部的变量拥有局部作用域,该变量称为局部变量
  • 定义在函数外部的变量拥有全局作用域,该变量称为全局变量
  • 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问
def discounts(price, rate):
    final_price = price * rate
    return final_price

old_price = float(input('请输入原价:'))  # 98
rate = float(input('请输入折扣率:'))  # 0.9
new_price = discounts(old_price, rate)
print('打折后价格是:%.2f' % new_price)  # 88.20
  • 当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字
num = 1

def fun1():
    global num  # 需要使用 global 关键字声明
    print(num)  # 1
    num = 123
    print(num)  # 123

fun1()
print(num)  # 123

1.2.4 内嵌函数

def outer():
    print('outer函数在这被调用')

    def inner():
        print('inner函数在这被调用')

    inner()  # 该函数只能在outer函数内部被调用

outer()
# outer函数在这被调用
# inner函数在这被调用

1.2.5 闭包

  • 是函数式编程的一个重要的语法结构,是一种特殊的内嵌函数。
  • 如果在一个内部函数里对外层非全局作用域的变量进行引用,那么内部函数就被认为是闭包。
  • 通过闭包可以访问外层非全局作用域的变量,这个作用域称为闭包作用域
def funX(x):
    def funY(y):
        return x * y

    return funY

i = funX(8)
print(type(i))  # 
print(i(5))  # 40
  • 闭包的返回值通常是函数。
def make_counter(init):
    counter = [init]

    def inc(): counter[0] += 1

    def dec(): counter[0] -= 1

    def get(): return counter[0]

    def reset(): counter[0] = init

    return inc, dec, get, reset

inc, dec, get, reset = make_counter(0)
inc()
inc()
inc()
print(get())  # 3
dec()
print(get())  # 2
reset()
print(get())  # 0
  • 如果要修改闭包作用域中的变量则需要 nonlocal 关键字
def outer():
    num = 10

    def inner():
        nonlocal num  # nonlocal关键字声明
        num = 100
        print(num)

    inner()
    print(num)

outer()

# 100
# 100

1.2.6 循环与递归

Sample01:n! = 1 x 2 x 3 x ... x n
循环:

n = 5
for k in range(1, 5):
    n = n * k
print(n)  # 120

递归:

def factorial(n):
    if n == 1:
        return 1
    return n * fact(n - 1)

print(factorial(5)) # 120

Samp02:斐波那契数列 f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1

循环:

i = 0
j = 1
lst = list([i, j])
for k in range(2, 11):
    k = i + j
    lst.append(k)
    i = j
    j = k
print(lst)  
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

递归:

def recur_fibo(n):
    if n <= 1:
        return n
    return recur_fibo(n - 1) + recur_fibo(n - 2)

lst = list()
for k in range(11):
    lst.append(recur_fibo(k))
print(lst)  
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  • 设置递归的层数,Python默认递归层数为 100
import sys

sys.setrecursionlimit(1000)

1.3 参数传递

需要注意的是,在 python 中,类型属于对象,变量是没有类型的

1.3.1 可更改(mutable)与不可更改(immutable)对象

在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

  • 不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。
  • 可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python 函数的参数传递:

  • 不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
  • 可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响
'''python 传不可变对象实例'''
def ChangeInt(a):
    a = 10
b = 2
print(ChangeInt(b))     # 返回 None
print(b)                # 结果是 2
'''
解释:实例中有 int 对象 2,指向它的变量是 b,在传递给 ChangeInt 函数时,按传值的方式复制了变量 b,a 和 b 都指向了同一个 Int 对象,在 a=10 时,则新生成一个 int 值对象 10,并让 a 指向它
'''
'''传可变对象实例'''
def changeList(mylist):
    "修改传入的列表"
    mylist.append([1, 2, 3, 4])
    print("函数内取值: ", mylist)
    return

mylist = [10, 20, 30]
changemeList(mylist)
print("函数外取值: ", mylist)
'''
函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
函数外取值:  [10, 20, 30, [1, 2, 3, 4]]
'''

简单来说:

  • 可变对象,经过处理,原对象会发生变化;可变对象在处理前后不会发生变化。

1.4 函数参数

以下是调用函数时可使用的正式参数类型:

  • 必须参数/位置参数 (positional argument)
  • 默认参数 (default argument)
  • 不定长参数/可变参数 (variable argument)
  • 关键字参数 (keyword argument)
  • 命名关键字参数 (name keyword argument)
  • 参数组合

1.4.1 必需参数/位置参数

必需参数须以正确的顺序传入函数,调用时的数量必须和声明时的一样;不然会出现语法错误。

def func(a, b, c):
    pass
# 必须传入三个参数,否则会报错

1.4.2 默认参数

def printinfo(name, age=20):
    print("Name: ", name, " Grade: ", grade)

printinfo(age=21, name="Ivan")	# Name:  Ivan  Grade:  21
printinfo(name="Zhang")			# Name:  Zhang  Grade:  20
  • 调用函数时,如果没有传递参数,则会使用默认参数
  • 默认参数一定要放在位置参数 后面,不然程序会报错
  • Python 允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值

1.4.3 可变参数/不定长参数

顾名思义,可变参数就是传入的参数个数是可变的,可以是 0, 1, 2 到任意个,是不定长的参数。
需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述 2 种参数不同,声明时不会命名

1.4.3.1 * 参数形式

加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数

语法形式:

def functionname([formal_args,] *var_args_tuple ):
   "函数_文档字符串"
   function_suite
   return [expression]

实例:

def printinfo(arg1, *vartuple):
    print(arg1)
    print(vartuple)

printinfo(70, 60, 50, 40, 30)
'''
打印:
70
(60, 50, 40, 30)
'''

特别的:

  • 如果在函数调用时没有指定参数,它就是一个空元组

1.4.3.2 ** 参数形式

还有一种就是参数带两个星号** 用来表示不定长度参数。
加了两个星号 **的参数会以dict字典的形式导入

基本语法:

def functionname([formal_args,] **var_args_dict ):
   "函数_文档字符串"
   function_suite
   return [expression]

实例:

def printinfo(arg1, **vardict):
	print("输出: ")
    print(arg1)
    print(vardict)

printinfo(1024, a_1=2, buffer='full')**
输出: 
1024
{'a_1': 2, 'buffer': 'full'}

特别的:声明函数时,参数中星号*可以单独出现
语法形式:

def f(a,b,*,c):
    return a+b+c

但是,如果单独出现星号*后的参数必须用关键字传入

>>> def f(a,b,*,c):
...     return a+b+c
... 
>>> f(1,2,3)   # 报错
Traceback (most recent call last):
 File "", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given
>>> f(1,2,c=3) # 正常
6

1.4.4 关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
因为有的时候 参数的使用不一定是使用指定顺序部分参数已经输入

def printinfo(name, age, major):
    print("名字: ", name)
    print("年龄: ", age)
    print("专业: ", major)

printinfo(major="CS", age=21, name="Ivan")
'''
名字:  Ivan
年龄:  21
专业:  CS
'''

上面的描述似乎有问题

def functionname(arg1, arg2=v, *args, **kw):
    "函数_文档字符串"
    function_suite
    return [expression]

**kw - 关键字参数,可以是从零个到任意个,自动组装成字典。

def printinfo(arg1, *args, **kwargs):
    print(arg1)
    print(args)
    print(kwargs)

printinfo(70, 60, 50)
# 70
# (60, 50)
# {}
printinfo(70, 60, 50, a=1, b=2)
# 70
# (60, 50)
# {'a': 1, 'b': 2}

「可变参数」和「关键字参数」的同异总结如下:

  • 可变参数允许传入零个到任意个参数,它们在函数调用时自动组装为一个元组 (tuple)。
  • 关键字参数允许传入零个到任意个参数,它们在函数内部自动组装为一个字典 (dict)。

1.4.5 命名关键字参数

def functionname(arg1, arg2=v, *args, *, nkw, **kw):
    "函数_文档字符串"
    function_suite
    return [expression]
  • *,nkw- 命名关键字参数,用户想要输入的关键字参数,定义方式是在nkw 前面加个分隔符 *。
  • 如果要限制关键字参数的名字,就可以用「命名关键字参数」
  • 使用命名关键字参数时,要特别注意不能缺少参数名。
def printinfo(arg1, *, nkw, **kwargs):
    print(arg1)
    print(nkw)
    print(kwargs)

printinfo(70, nkw=10, a=1, b=2)
# 70
# 10
# {'a': 1, 'b': 2}

printinfo(70, 10, a=1, b=2)
# TypeError: printinfo() takes 1 positional argument but 2 were given

没有写参数名nwk,因此 10 被当成「位置参数」,而原函数只有 1 个位置函数,现在调用了 2 个,因此程序会报错。

1.4.6 参数组合

在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:

  • 位置参数、默认参数、可变参数和关键字参数。
  • 位置参数、默认参数、命名关键字参数和关键字参数。

要注意定义可变参数和关键字参数的语法:

  • *args是可变参数,args接收的是一个tuple
  • **kw是关键字参数,kw 接收的是一个 dict
    命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名关键字参数不要忘了写分隔符*,否则定义的是位置参数。

特别的:虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数很难懂

2. Lambda函数

在 Python 里有两种函数:

  • 用 def 关键词的正规函数
  • 用 lambda 关键词的匿名函数

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

  • 所谓匿名,意即不再使用 def语句这样标准的形式定义一个函数:
  • lambda只是一个表达式,函数体比 def简单很多。
  • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  • lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

2.1 语法

lambda 函数的语法只包含一个语句,语法如下:
lambda [arg1 [,arg2,.....argn]]:expression
or
lambda argument_list: expression

  • lambda- 定义匿名函数的关键词。
    -argument_list- 函数参数,它们可以是位置参数、默认参数、关键字参数,和正规函数里的参数类型一样。
  • :-冒号,在函数参数和表达式中间要加个冒号。
  • expression- 函数表达式,输入函数参数,输出一些值

实例

sumary = lambda arg1, arg2: arg1 + arg2
print(sumary(10, 20))  # 30

func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5))  # 15

实例:

psum = lambda arg1, arg2: arg1 + arg2

print("相加后的值为 : ", psum(10, 20))
print("相加后的值为 : ", psum(20, 20))
'''
相加后的值为 :  30
相加后的值为 :  40
'''

实例:

lambda x: 2*x+1
g = lambda x:2*x+1
g(5)
g = lambda x,y:x+y
g(1,2)

但需要注意的是,PEP 8 规范 不建议使用赋值语句定义Lambda函数,而是使用def 进行定义

2.2 匿名函数 VS 正规函数

lbd_sqr = lambda x: x ** 2
print(lbd_sqr)

#  at 0x000000BABB6AC1E0>

def sqr(x):
    return x ** 2

print(sqr)
# 

print(sqr(9))  # 81
print(lbd_sqr(9))  # 81
  • 误用情况:如果用lambda函数只是为了赋值给一个变量,用def的正规函数。
  • lbd_sqr的返回值是以 标识的函数,而 sqr的返回时是以sqr为标识的函数,明显后者一看就知道该函数是「计算平方」用的。

2.3 Lambda表达式的应用

高阶函数 (high-order function) 在函数化编程 (functional programming) 很常见,主要有两种形式:

  • 参数是函数 (filter, map)
  • 返回值是函数 (closure)

filter(function, iterable)过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list()来转换。

a = (list(filter(lambda x: x % 2, range(10))))
print(a)	# [1, 3, 5, 7, 9]

map(function, iterable, …):映射。将iterator中的元素依次作为fun的参数,经过处理的结果加入到结果序列中(根据提供的函数对指定序列做映射)

a = list(map(lambda x: x**2, range(10)))
print(a)	# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

除了 Python 这些内置函数,我们也可以自己定义高阶函数,如下:

def apply_to_list(fun, some_list):
    return fun(some_list)

下面代码分别求出列表中所有元素的和、个数和均值。

lst = [1, 2, 3, 4, 5]
print(apply_to_list(sum, lst))
# 15
print(apply_to_list(len, lst))
# 5
print(apply_to_list(lambda x: sum(x) / len(x), lst))
# 3.0

特别鸣谢

你可能感兴趣的:(Python)