Python Learnning (七)-函数

函数也是一个对象

对象是内存中存储数据的一块区域

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

一 创建函数

def 函数名([形参1,形参1,形参1....]):

​ 代码块

def fn():
    print('this is my first function')
二 调用函数

fn()

fn指的是函数对象,fn()才是调用函数

eg:定义一个函数,求俩个数的和

def sum(a,b):
    print(a+b)
    
sum(1,2)

函数在调用时,解析器不会检查实参的类型,实参可以传递任意类型

eg:123,True,‘HELLO’,None,[1,2,3],fn

所以这里容易报错,这里随便传参

def sum(a,b):
    print(a+b)
sum(123,'456')
TypeError: unsupported operand type(s) for +: 'int' and 'str'

在函数中对形参进行赋值不会影响其它变量,但是如果形参指定的是一个对象当我们通过形参去修改对象,那么所有指向这个对象的变量都会变.

def fn(a):
    a[0]=10
    #a=[4,5,6] 这个时候a变 c不变
    print(a)
c=[1,2,3]
fn(c)
print(c)
# 这个时候a和c同时发生变化

这个时候如果想要不改变c本身可以传入c的浅copy对象或者列表的切片等

def fn(a):
    a[0]=10
    print(a)
c=[1,2,3]
fn(c.copy())
fn(c.[:])
三 不定长参数

需求:要求任意个数字的和

在定义函数的时候,可以在形参的前面加上一个*,这样这个形参将会获取到所有的实参,它将会将所有的实参保存到一个元组中(装包). 注意不定长的参数只能有一个

def fn(*nums):
	result=0
	for i in nums:
		result+=i
	print result
fn(1,2,3,4,5,6,7,8)

可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递

def fn(a,*b,c):
    print(a,b,c)
fn(1,2,3,4,5,c=6)

def fn2(*,a,b,c):
    print(a,b,c)
fn2(1,2,3,a=4,b=5,c=6)

*号形参只能接收位置参数,不能接收关键字参数

def fn(*a):
    print(a)
    
fn(a=1,c=2) error

**形参可以接收位置参数, 它会将这些参数统一保存到一个字典中,字典的key就是参数的名字,字典的value就是参数的值,**形参只能有一个,并且必须写在参数的最后

def fn(b,c,**a):
    print(b)
    print(c)
fn(a=1,c=2)

参数的解包

def fn4(a,b,c)
	print(a,b,c)
t=(10,20,30)
# 传递实参时,也可以在序列类型参数前加*,这样他会自动将序列中的元素依次作为参数传递
# 这里要求序列中元素的个数必须和形参的个数一致
fn4(*t)

通过**来对一个字典进行解包

d={'a':100,'b':200,'c':300}
fn4(**d)
四 返回值

函数可以通过return来指定返回值,通过一个变量来接收返回值

return 后边可以跟任意对象,甚至可以是一个函数,如果仅写一个return或不写return,则相当于return None

return用来结束函数

def fn():
    def fn2():
        print('a')
    return fn2
r=fn()
print(type(r))
# <type 'function'>

fn和fn()的区别

fn是函数对象,实际上是在打印函数对象

fn()实在调用函数,实际上是在打印函数的返回值

五 文档字符串

help()是Python中的内置函数

通过help()函数可以查询Python中函数的用法

help(print)

文档字符串 (doc str)

在定义函数时,可以在函数的内部编写文档字符串,文档字符串就是函数的说明,当我们编写了文档字符串的时候,就可以通过help()函数来查看函数的说明,直接在函数的的第一行写一个字符串就是文档字符串

def fn(a:int,b:bool,c:str='hello')->str:
    '''这个是一个文档字符串
       参数a的默认类型是int
       参数b的默认类型是布尔值
       参数c的默认类型是字符串
       函数的返回值是字符串
    '''
六 作用域和命名空间
作用域(scope):变量生效的区域

在Python一共有俩种作用域:

  1. 全局作用域 在程序执行时创建,在程序执行结束销毁,全局作用域中定义的变量就是全局变量
  2. 函数作用域 在函数调用时创建,调用结束是销毁,调用一次函数产生一个新的函数作用域,在函数作用域定义的变量,它只能在函数内部被访问.
def fn():
	a=20
	def fn2():
        a=40
		print(a)
	fn2()
fn()
# 40

当我们在使用变量时,会优先在当前作用域中寻找该变量,如果有优先使用,没有去上一级作用域中寻找

在函数中为变量赋值时,默认都是为局部变量赋值,如果想要修改全局变量,则需要使用global关键字,来声明变量

a=30
def fn():
	a=10
	print('inside a=',a)
fn()
print('outside a=',a)
# ('inside a=', 10)
# ('outside a=', 30)
a=30
def fn():
	global a
	a=10
	print('inside a=',a)
fn()
print('outside a=',a)
#('inside a=', 10)
#('outside a=', 10)
命名空间(namespace)
  • 命名空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间中

  • 每一个作用域,都会有它对应的命名空间

  • 全局的命名空间用来保存全局变量,函数的命名空间用来保存函数的变量

  • 命名空间实际上就是一个字典,是一个专门用来存储变量的字典(dict)

  • 在外部访问内部的命名空间不行,但是globals()函数可以在任意位置获取全局的命名空间

locals()用来获取当前作用域的命名空间

如果在全局作用域中调用locals()则获取全局的命名空间 ,如果在函数作用域中调用locals()则获取函数的命名空间

scope=locals()
print(scope)
# {'__builtins__': <module '__builtin__' (built-in)>, '__file__': '/home/zax/Documents/pythontest/aa.py', '__package__': None, 'scope': {...}, '__name__': '__main__', '__doc__': None}
def fn():
	a=10
	scope=locals()
	print(scope)
fn()
# {'a': 10}
def fn():
	a=10
	scope=globals()
	print(scope)
fn()
# {'__builtins__': <module '__builtin__' (built-in)>, '__file__': '/home/zax/Documents/pythontest/aa.py', '__package__': None, '__name__': '__main__', '__doc__': None, 'fn': <function fn at 0x7fb24c627668>}
七 递归

尝试求任意数的阶乘(x!)

def factorial(n):
    result=n
    for i in range(1,n):
        result*=i
    return result
print(factorial(10))
# 3628800
递归函数

递归和循环是类似的,可以相互替代.递归的好处是编写起来很难,但是阅读起来简单,用简单的函数表达复杂的功能.

def  fn():
    fn()
# 无穷递归 容易死循环

递归式函数的俩个条件

  1. 基线条件
    • 问题可以被分解为的最小问题,当满足基线条件时,递归就不在执行
  2. 递归条件
    • 将问题继续分解的条件

例如 10!=10*9!

​ 9!=9*8!

​ …

​ 1!=1

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

练习 求n**i 的幂运算(python中有幂运算符号)

def power(n,i):
    '''幂运算
    	参数:
    		n 要做幂运算的数字
    		i 做幂运算的次数
    '''
	if i==1:
		return n
	return n*power(n,i-1)
print(power(10,3))

判断一个字符串是不是回文(从前往后度读和从后往前读是一样的)

def hui_wen(s):
    '''回文字符串检查
    	参数:s代表传入字符串
    '''
	if len(s)<2:
		return True
	elif s[0]!=s[-1]:
		return False
	else :
		return hui_wen(s[1:-1])

print(hui_wen('abcba'))
def hui_wen(s):
	if len(s)<2:
		return True
    # 用一个与运算对上面的代码进行优化
	return s[0]==s[-1] and hui_wen(s[1:-1])

print(hui_wen('abcba'))
八 高阶函数

Python支持函数式编程但不是函数式变成语言 scala是

在Python中函数是一等对象

一等对象一般都会具有如下特点:

1. 对象在运行时创建
2. 能赋值给变量或作为数据结构的元素
3. 能作为参数传递
4. 能作为返回值返回

高阶函数

​ 高阶函数至少符合俩个特点中的一个:

		1. 接收一个或多个函数作为参数
		2. 将函数作为返回值返回
def fn(a):
	if a%2==0:
		return True
	return False

def fn1(a):
	if a%2!=0:
		return True
	return False

def fn2(a):
	if a>=5:
		return True
	else:
		return False

def fn3(func,lst):
    '''
    简单演示一个简单的高阶函数
    参数:
    	func:第一个参数是一个函数 表示匹配规则
    	lst: 第二个参数是序列
    '''
	new_list=[]
	for i in lst:
		if func(i):
			new_list.append(i)
	return new_list

lst=[1,2,3,4,5,6,7,8,9]
print(fn3(fn2,lst))
# [5, 6, 7, 8, 9]

上面这个练习其实也对应着Python自带的一个高阶函数filter()

filter()

可以从序列中过滤出符合条件的元素,保存到一个新的序列中

参数:

1. 函数,根据该函数来过滤序列(可迭代的结构)
2. 需要过滤的序列(可迭代的结构)

返回值:

​ 过滤后新的序列

print(list(filter(fn,lst)))

fn这里就是相当于一个参数调用完成之后就没用了,这个时候我们发现为了一个功能专门定义一个函数很麻烦

引入匿名函数

九 匿名函数

匿名函数lambda 函数表达式 (语法糖,一般都是实现简单功能)

语法:lambda 参数列表:返回值

print(filter(lambda i:i%3==0,lst))
map()

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

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

该方法用来对列表中的元素进行排序,默认是直接比较列表中元素的大小

在我们的sort中可以接收参数key

​ key需要一个函数作为参数,当设置了函数作为参数

​ 这个时候比较的就不是元素,而是函数的返回值

lst=['a','bbb','cc']
lst.sort(key=len)
print(lst)
# ['a', 'cc', 'bbb']
lst=['a','1',2]
lst.sort(key=str)
print(lst)
# ['1', 2, 'a']
sorted()

这个函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序(sort只针对列表)

并且使用sorted排序 不会影响原来的对象,而是返回一个新的对象

lst=['a','bbb','cc']
print(sorted(lst,key=len))
print(lst)
# ['a', 'cc', 'bbb']
# ['a', 'bbb', 'cc']
十 闭包

形成闭包的要求:

1. 函数嵌套
2. 将函数作为返回值返回
3. 内部函数必须要使用到外部函数的变量
def make_averager():
	nums=[]
	def averager(n):
		nums.append(n)
		return sum(nums)/len(nums)
	return averager
averager=make_averager()
print(averager(10))
print(averager(30))
# 这个时候形成一个闭包 就像java中的private函数的私有变量一样 只能通过averager这个函数获取nums这个局部变量
十一 装饰器

希望函数在计算之前打印程序开始,在计算之后打印程序结束

我们可以通过修改函数中的代码来完成这个需求,但是会产生以下问题

1. 每个函数都要修改很麻烦
2. 不方便后期维护
3. 违反对程序扩展打开,修改关闭的开闭原则(ocp)
def add(*nums):
	result=0
	for i in nums:
		result+=i
	return result

def mul(*nums):
	result=0
	for i in nums:
		result*=i
	return result

def begin_end(old_func):
	def new_func(*a,**b):
		print('extend_expression1')
		result=old_func(*a,**b)
		print('extend_expression2')
		return result
	return  new_func
# 调用方法一(不常用)
new_add=begin_end(add)
print(new_add(1,3,1,2))

在定义函数的时候可以通过@装饰器,来使用指定的装饰器,可以同时为一个函数指定多个装饰器,这样函数将会按从内向外的顺序执行

def begin_end(old_func):
	def new_func(*a,**b):
		print('extend_expression1')
		result=old_func(*a,**b)
		print('extend_expression2')
		return result
	return  new_func

def begin_end2(old_func):
	def new_func(*a,**b):
		print('extend_expression3')
		result=old_func(*a,**b)
		print('extend_expression4')
		return result
	return  new_func


def add(*nums):
	result=0
	for i in nums:
		result+=i
	return result


@begin_end
@begin_end2
def mul(*nums):
	result=0
	for i in nums:
		result*=i
	return result



print(mul(1,3,3))
# extend_expression1
# extend_expression3
# extend_expression4
# extend_expression2
# 0

#这个才是常用方法(定义出装饰器,然后以注解的方式加在方法的头上) 注意这种方法你找不到原来的方法了 除非删除装饰器
	
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值