Python基础7-函数
函数也是一个对象
对象是内存中存储数据的一块区域
函数可以用来保存一些可执行的代码.并且可以在需要时,对这些语句进行多次的调用
一 创建函数
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一共有俩种作用域:
- 全局作用域 在程序执行时创建,在程序执行结束销毁,全局作用域中定义的变量就是全局变量
- 函数作用域 在函数调用时创建,调用结束是销毁,调用一次函数产生一个新的函数作用域,在函数作用域定义的变量,它只能在函数内部被访问.
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()
# 无穷递归 容易死循环
递归式函数的俩个条件
- 基线条件
- 问题可以被分解为的最小问题,当满足基线条件时,递归就不在执行
- 递归条件
- 将问题继续分解的条件
例如 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
#这个才是常用方法(定义出装饰器,然后以注解的方式加在方法的头上) 注意这种方法你找不到原来的方法了 除非删除装饰器