讲函数之前,先了解一下变量的作用域
变量作用域
2种变量作用域
- 局部变量
- 全局变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域,而局部变量只能在其被声明的函数内部访问,全局变量则可以在整个程序范围内访问
变量的作用域如下:
● L(Local):局部作用域
● E(Enclosing):闭包函数外的函数中
● G(Global):全局作用域
● B(Built-in):内建作用域
变量的属性与执行依据:
● 变量的先后顺序是:L –> E –> G –>B 的规则查找
● 在子程序中定义的变量称为局部变量
● 在程序的一开始定义的变量称为全局变量
● 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序
● 当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用,在其它地方全局变量起作用
● 当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了
● 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问
局部转全局: 将一个局部变量通过global
关键字,转换为全局变量.
import os
import sys
def print_num():
global num
num = 1000
print("函数内调用: ", num)
print_num()
print("函数外调用: ", num)
无参和有参函数
demo
#无参函数
def print_num():
print("hello world!")
print_num()
#有参函数
def add(x,y):
z=x+y
print(z)
add(1,2)
函数参数的传递
Python中所支持的参数传递形式:
- 普通参数:普通参数传递,在定义函数时就指定了规律是从左至右传递
- 默认参数:定义函数时是使用"name=value"的语法直接给变量一个值,从而传入的值可以少于参数个数
- 指定参数:调用函数时指定"name形式参数=value实际参数"的语法通过参数名进行匹配
- 动态参数:在我们定义函数时,形式参数中收集任意多基于普通参数【定义函数时使用* :收集普通参数,返回元组,*args】【定义函数时使用**:收集指定参数,返回列表,**kwargs】
- 动态参数解包:在调用函数时,使用**开头的参数,从而传递任意多基于普通或指定参数
demo
#普通传参
def stu(name,age,country):
print("姓名:",name)
print("年龄:",age)
print("国际:",country)
stu("cc",18,"CN")
#带默认参数传递: 同样的,我们可以给指定的字段添加默认参数,如果用户不输入则默认使用指定参数
#这里需要注意的是:如果您要使用带默认参数的函数,则需要把带参数的字段,放在函数最后一项.
def stu2(age,country,name="aa"):
print("姓名:",name)
print("年龄:",age)
print("国籍:",country)
stu2(23,"CN","aa") #此时我们给予全部的参数则无默认值
stu2("cc",20,"AM") #形参如何排列,实参就得如何排列 姓名: AM 年龄: cc 国籍: 20
stu2(18,"LOS") #传递输入是忽略带有默认值的字段
#动态参数传递(传递列表): 若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数,传递一个列表.
def stu3(name,age,*args):
print(name,age,args)
stu3("aa",22)
stu3("aa",22,"a","b","c")
ls=[1,2,3]
stu3("aa",22,ls) #传入列表
#动态参数传递(万能参数): 我们使用*与**通常情况下可以传递任何值,所以称作万能参数.
def fun(*args,**kwargs):
print(args,type(args))
print(kwargs,type(kwargs))
ls2=[1,2,3,4,5]
dic={"a":1,"b":2,"c":3}
fun(*ls2,**dic) #(1, 2, 3, 4, 5) <class 'tuple'> {'a': 1, 'b': 2, 'c': 3} <class 'dict'>
函数的返回值
demo
#函数的返回值
def add(x,y):
z=x+y
print("函数内返回:",z)
z+=10
return z
z=add(1,2)
print("函数外返回:",z)
函数闭包:
闭包是由函数及其相关的引用环境组合而成的实体(闭包=函数+引用环境)这个从字面上很难理解。Python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).这个定义是相对直白的,好理解的,下面举一个简单的例子来说明.
def adds(x):
def adder(y):return x+y
return adder
c=adds(10)
print(type(c))
print(c.__name__)
print(c(10))
如上代码,在一个内部函数里:adder(y)就是这个内部函数,对在外部作用域(但不是在全局作用域)的变量进行引用:x就是被引用的变量,x在外部作用域adds里面,但不在全局作用域里,则这个内部函数adder就是一个闭包.闭包=函数块+定义函数时的环境,adder就是函数块,x就是环境,当然这个环境可以有很多,不止一个简单的x.
函数嵌套与递归
除了函数的闭包以外,函数还支持两种调用方式,一种是嵌套函数,另一种是递归函数,这里需要注意的是,最好在开发中尽量少用这样的结构,这种结构一旦层数变多将很难后期进行维护,所以你懂的
demo:嵌套函数
import os
name = "cc"
def chage_name():
name = "cc blog"
def chage_name_new():
name = "xiao blog"
print("第3层循环打印: ", name)
chage_name_new() # 在函数内部调用内部的函数
print("第2层循环打印: ", name) # 第二层函数执行结果
chage_name() # 调用最外层函数
print("查看最外层变量: ", name) # 查看外层变量
运行结果:
第3层循环打印: xiao blog
第2层循环打印: cc blog
查看最外层变量: cc
demo2:递归函数
import os
def fun(n):
if 0==n: # n=0 的话直接返回空,对用户输入的零进行判断
return None
elif 1==n: # n=1 的话就不再递归
return n
else:
return n*fun(n-1) # 递归在执行f(n-1),直到f(1)
print(fun(5)) # 120
'''
f(5)的执行过程如下
===> f(5)
===> 5 * f(4)
===> 5 * (4 * f(3))
===> 5 * (4 * (3 * f(2)))
===> 5 * (4 * (3 * (2 * f(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
'''
用递归实现二分法:
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
def binary_search(dataset,find_num):
print(dataset)
if len(dataset) >1:
mid = int(len(dataset)/2)
if dataset[mid] == find_num: #find it
print("找到数字",dataset[mid])
elif dataset[mid] > find_num : # 找的数在mid左面
print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
return binary_search(dataset[0:mid], find_num)
else: # 找的数在mid右面
print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
return binary_search(dataset[mid+1:],find_num)
else:
if dataset[0] == find_num: #find it
print("找到数字啦",dataset[0])
else:
print("没的分了,要找的数字[%s]不在列表里" % find_num)
binary_search(data,66)
匿名函数
demo
#匿名函数的使用
# 定义匿名函数方式1(普通方式)
def func(arg):
return arg + 1
result = func(123)
print(result)
# 定义匿名函数方式2(lambda表达式)
my_lambda = lambda arg: arg + 1
# 执行函数
result = my_lambda(123)
print(result)