五、函数

函数

函数定义:

  • 函数也是一个对象
  • 函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次的调用

创建函数:

def 函数名([形参1,形参2,...形参n]):
	代码块

调用函数:

def fn():
    print("这是我的第一个函数")

fn()
print(type(fn))

输出:
在这里插入图片描述

函数的参数

定义一个函数,用来求任意两个数的和:

def add(a, b, c=20):
	return a+b+c

1)参数传递的方式:

1、位置参数–将对应位置的实参赋值给对应位置的形参

print(add(10, 20, 30))

2、关键字参数

print(add(b=20, a=10, c=30))

注意:位置参数和关键字参数可以混合使用(位置参数要写在前面!!)

print(add(10, c=30, b=20))

2)实参的类型

  • 函数在调用时,解析器不会检查实参的类型
  • 实参可以传递任意类型的对象
  • 如果实参传递的是可变对象,那么形参对对象的改变会导致实参的改变

3)不定长参数

1、带一个星号的形参

在定义函数时,可以在形参前面加上一个*,这样这个参数将会获取到所有的实参,它会将所有的实参保存到一个元组中(这个步骤可以称为装包)。

def fn(*a):
	print("a = ", a, type(a))

fn(1, 2, 3, 4, 5)

在这里插入图片描述
求任意个参数的和:

def add(*a):
	return sum(a)

add(1, 2, 3, 4, 5)

注意:带*号的参数只能有一个。而且带星号参数后面的参数,必须以关键字的形式传递

如果第一个形参为*,则所有参数都必须以关键字形式传递

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

fn(a=10, b=20, c=30)

2、带两个星号的形参

可以接受其它关键字的参数,然后将这些参数统一保存到字典中。

def fn(**a):
	print("a=", a, type(a))

fn(b=1, c=2, d=3)

输出:
在这里插入图片描述
注意:带两个星号的形参必须写在参数列表的最后

4)参数解包

1、对元组解包

传递实参时,也可以在序列类型的参数前添加星号,这样他会自动将序列中的元素依次作为参数传递

def fn(a, b, c)
	print("a = ", a)
	print("b = ", b)
	print("c = ", c)
s = (1, 2, 3)
def(*s)

2、对字典解包

def fn(a, b, c):
    print("a = ", a)
    print("b = ", b)
    print("c = ", c)

d = {"b":100, "a":200, "c":300}
fn(**d)

输出:
在这里插入图片描述

返回值

返回值就是函数执行以后返回的值

def sum(a, b):
	return a + b
print(sum(1, 2))
  • return后面可以跟任意对象(所以返回一个函数也是可以的哦~~)
  • 如果return后面没有也对象,或者不写return,则相当于 return None
  • 在函数中,return后的代码都不会执行

文档字符串

help() 是 python 中的内置函数,通过这个函数可以查询 python 中的函数的用法

help(print)

输出:
在这里插入图片描述
文档字符串(doc str):

  • 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明。
  • 当我们编写了文档字符串后,就可以通过 help() 来查看函数的说明。
  • 在函数的第一行写一个字符串即可
def fn(a, b, c):
	'''
	这是一个文档字符串示例
	函数的参数:
		a,作用,类型,默认值,...
		b,作用,类型,默认值,...
		c,作用,类型,默认值,...
	'''
	print("a = ", a)
	print("b = ", b)
	print("c = ", c)

help(fn)

输出:
在这里插入图片描述

作用域(scope)

作用域指的是变量生效的区域。python中一共有两种作用域:全局作用域和函数作用域。

全局作用域:

  • 全局作用域在程序执行时创建,在程序执行结束时销毁
  • 所有函数以外的区域都是全局作用域
  • 在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问。

函数作用域:

  • 函数作用域在函数调用时创建,在调用结束时销毁
  • 函数每调用一次就会产生一个新的函数作用域
  • 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问

变量查找:

  • 当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有则继续去上一级作用域中寻找,依次类推。
  • 如果找到全局作用域依然没有找到,则会抛出异常。
  • 如果在函数内部想要访问全局全量,则需要使用 global 关键字
    a = 3
    def fn3():
    	global a
    	a = 10
    	print(a)
    

命名空间(namespace)

  • 命名空间就是变量存储的位置,每一个变量都需要存储到指定的命名空间当中。
  • 每一个作用域都会有一个它对应的命名空间。
  • 全局命名空间,用来保存全局变量。函数命名空间用来保存函数中的变量。
  • 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
  • locals() 用来获取当前作用域的命名空间。如果在全局作用域中调用locals(),则获取全局命名空间。如果在函数作用域中调用locals(),则获取函数命名空间。返回的是一个字典。
  • global() 可以在任意位置(包括函数内)获取全局的命名空间。
    a = 3
    def fn3():
    a = 10
    print(a)
    print(locals())
    
    fn3()
    print(locals())
    
    输出:
    在这里插入图片描述

递归

递归简单来理解,就是自己调用自己

尝试求10的阶乘:

def factorial(n):
	result = n
	for i in range(1, n):
		result *= i
	return result

print(factorial(10))

递归函数的两个要件:

  1. 结束条件。即问题可以被分解为更小的问题,直到满足结束条件就不再执行了。
  2. 递归条件。将问题继续分解的条件。

将以上条件改为递归的形式:

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

print(factorial(10))

PS:递归和循环类似,基本是可以互相代替的。循环编写起来比较容易,阅读起来难。递归编写起来难,但是方便阅读。

函数式编程

在python中,函数是一等对象。一等对象具有以下的特点:

  • 对象是在运行时创建的
  • 能赋值给变量或作为数据结构中的元素
  • 能作为参数传递
  • 能作为返回值返回

高阶函数:接收函数作为参数,或者将函数作为返回值的函数都是高阶函数

1、将函数作为参数传递

def fn1(n):
    if n % 2 == 0:
        return True
    else:
        return False

def fn2(n):
    if n % 2 == 0:
        return False
    else:
        return True

def fn3(n):
    if n > 5:
        return True
    else:
        return False
    
def fn(func, lis):
    new_lis = []
    for n in lis:
        if func(n):
            new_lis.append(n)

    return new_lis

lis = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(fn(fn1, lis))

这样写的好处是:在传入参数时就可以决定是取偶数、奇数、还是大于5的数。

匿名函数:

在前面的例子中我们看到,fn1、fn2、fn3都只会被调用一次,而为了这一次调用需要在代码里写上完整的这个函数,感觉有点不值。下面用匿名函数来解决:

filter()函数可以用来过滤可迭代对象:

lis = [1, 2, 3, 4, 5, 6, 7, 8, ,9 ,10]
s = filter(lambda i : return i % 2, lis)
print(s)

lambda函数表达式专门用来创建一些简单的函数,它是函数创建的又一种方式。

fn6 = lambda a, b : a + b
print(fn6(3, 4))

map() 函数可以对可迭代对象中所有元素做指定的操作,然后将其添加到一个新的对象中返回。

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
r = map(lambda i : i + 1, l)
print(r)

在这里插入图片描述
sort() 方法可以用来对列表中的元素排序:

l = ["aaa", "bbb", "ccc", "ddd"]
l.sort()
print(l)
# 也可以接收一个函数作为参数,来确定排序规则
# 以长度来作为排序规则
l.sort(key=len)
print(l)
l = ['2', 5, 6, '8', '3']
l.sort(key=int)
print(l)

sorted() 函数的使用与sort() 使用方法类似,只是sorted() 函数不会改变原来的列表,而是返回一个新的排完序的列表

以上代码的输出:
在这里插入图片描述

2、将函数作为返回值–闭包

def fn():
	def fn2():
		print("inner")
	return fn2

fn()
  • 这个函数是在 fn() 内部定义的,所以总是能访问到 fn() 函数内的变量。
  • 通过闭包,可以创建一些只有当前函数能访问的变量,即可以讲一些私有的数据藏到闭包中。
def make_averager():
	# 创建一个列表,用来保存数值
	nums = []
	# 创建一个函数,用来计算平均值
	def averager(n):
		nums.append(n)
		return(sum(nums) / len(sums))
avg = make_averager()
print(avg(10))
print(avg(10))
print(avg(5))

输出:
在这里插入图片描述
这样做的好处是,nums列表在函数内,其它人不好改!!

形成闭包的要件:

  • 函数嵌套
  • 将内部函数作为返回值返回
  • 内部函数必须要使用外部函数的变量

装饰器

希望在不修改原函数的情况下,来对函数进行扩展

def fn():
	print("我是fn函数...")

def new_fn():
   print("函数开始执行")
   fn()
   print("方式执行结束")

但是每扩展一个函数就手动创建一个新函数,实在太麻烦。

def begin_end(old):
	'''
	用来对其它函数进行扩展,使其它函数可以在执行前打印开始执行, 执行后打印执行结束
	参数:
		old:被扩展的函数
	'''
	def new_function(*args, **kwargs):
		print("开始执行~~")
		#调用被扩展的函数
		old(*args, **kwargs)
		print("执行结束")
		
	return new_function

类似于begin_end() 这样的函数,我们就称它为装饰器。通过装饰器可以在不修改原来函数的情况下,对函数进行扩展。

在定义函数时,可以通过@装饰器,来使用指定的装饰器。并且可以同时使用多个,那样就是装饰一个装饰器

@begin_end
def say_hello():
	print("大家好!!")
	
say_hello()

输出:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值