Python3_函数:可变长参数、全局变量和局部变量、向前引用、递归函数、匿名函数

一、语法格式:

def test(x):
    "解释函数的作用"
    x += 1
    return x

def:定义函数的关键字

test:函数名

():定义函数的形参

"":文档描述,定义函数的作用

x+=1:函数的代码块

return:定义返回值

当函数遇到一个return时整个函数结束

当指定了默认参数的时候,在调用函数的时候可以不指定参数

二、函数的参数:

1、形参:

在函数中定义的变量,其只有在被调用的时候才会被分配内存单元,在调用结束时释放分配的内存单元

2、实参:

在调研用函数的时候定义的常量、变量、表达式、函数等

3、例子:

def add(x,y):
    res = x * y
    return res
add = add(2,4)

在上面的例子中形参为add后面的x和y,实参为调用函数时的2和4;形参和实参的位置必须要一一对应

三、可变长参数:

可变长参数是指可以向函数的传递不定个数的参数;可变长参数的原则是位置参数必须在关键字参数之前

1、位置参数:

位置参数是和函数中的形参一一对应的,如:

def add(x,y):
    res = x * y
    return res
add(2,4)

在上面的代码中2和4对应函数中x和y,也就是x和y是位置参数

2、关键字参数:

关键字参数是指可以接受0个或多个实参的参数,如:

def test01(x, *args):
    print(x)
    print(args)
test01(1,2,3,4,5)

在上面的代码中x是位置参数,对应实参中的1;

而*args是一个关键字参数,对应实参中的2,3,4,5

3、*:

在函数的形参中*可接受字符串、数字、列表、元组、集合的数据类型;Python最后会将这些数据类型转化为元组。

例子:

def test01(x, *args):
    print(x)
    print(args)
test01(1,2,3,4,5)

运行结果:

1

(2, 3, 4, 5)

若传递的实参是一个可迭代的参数时需要加上一个*号,如:

def test01(x, *args):
    print(x)
    print(args)
test01(1, *[2,3,4])

运行结果:

1

(2, 3, 4)

4、**:

和*类似,不同的是**可接收的是键值对,最后都会转换为字典形式

例子:

def test02(x, **kwargs):
    print(x)
    print(kwargs)
test02(1, y=2, z=3)

运行结果:

1

{'y': 2, 'z': 3}

和*类似**也可以接收字典类型,如:

例子:

def test02(x, **kwargs):
    print(x)
    print(kwargs)
test02(1, **{"y":2, "z":3})

运行结果:

1

{'y': 2, 'z': 3}

5、当 * 和 ** 共用时:

因为可变长参数的原则是位置参数必须在关键字参数之前,所以当*和**共用时为了避免产生歧义,需要将*放在**之前;如:

def test03(x, *args, **kwargs):
    print(x)
    print(args)
    print(kwargs)
test03(1,2,3,4,y=1,z=2)

运行结果:

1

(2, 3, 4)

{'y': 1, 'z': 2}

四、全局变量和局部变量

1、全局变量的定义:

在Python代码中的任意位置都能调用的变量;如:

name = "xiaoming"
def get_name():
    print(name)

get_name()

运行结果:

xiaoming

在上面的例子中在函数外定义了一个变量 “name”,然后在函数内部调用这个变量;最后发现我们可以调用“name”变量;所以变量“name”被称为全局变量

2、局部变量的定义:

在Python代码中只在特定过程或函数中可以访问的变量,当没有在子程序中定义局部变量时会去上一层进行查找是否存在相同的变量名;如:

name = "xiaoming"
def get_name1():
    name = "xiaohong"
    print(name)

def get_name2():
    print(name)

get_name1()
get_name2()

运行结果:

xiaohong

xiaoming

根据上面的例子可以发现先定义的全局变量在函数“get_name1”中并没有被调用,而是调用了函数内部的定义的“name”变量;而函数“get_name2”调用了全局变量“name”,无法调用函数“get_name1”中定义的“name”变量;所以在函数“get_name1”中的变量“name”称之为局部变量。

3、global关键字:

在函数中的局部变量之前使用 global 关键字可以将局部变量定义为全局变量;如:

name = "xiaoming"
def get_name1():
    global name
    name = "xiaohong"
    print(name)

def get_name2():
    print(name)

get_name1()
get_name2()

运行结果:

xiaohong

xiaohong

在上面的例子中,在函数“get_name1”中使用了关键字 global 将函数“get_name1”中的局部变量“name”转化成了全局变量“name”;因为在代码的开头已经定义了全局变量‘name=“xiaoming”’,所以在函数“get_name1”中的变量“name”将代码开头的全局变量“name”的值改成了“xiaohong”,所以在函数“get_name2”中调用“name”的值成了“xiaohong”。

4、函数的嵌套

函数支持多个函数进行嵌套,如:

name = "xiaoming"

def get_name1():
    name = "xiaobai"
    print(name)
    def get_name2():
        global name
        name = "xiaohong"
    get_name2()

get_name1()
print(name)

运行结果:

xiaobai

xiaohong

注:函数在没有被调用的情况下只进行编译,不执行。

当在调用函数“get_name1()”的时候局部变量“name”的值是“xiaobai”,所以在函数“get_name1()”中print的时候输出“xiaobai”;在函数“get_name1()”中调用了函数“get_name2()”,在函数“get_name2()”中使用了global关键字将局部变量name变为了全局变量name并进行了修改,所以在代码最后print的时候输出“xiaohong”

5、nonlocal关键字

nonlocal关键字和global关键字类似,不同的是global关键字是将局部变量转换为全局变量;而nonlocal关键字是将局部变量转为上一级函数的局部变量,当函数只有一级的时候使用nonlocal程序会报错,如:

name = "xiaoming"

def get_name1():
    name = "xiaobai"
    print(name)
    def get_name2():
        nonlocal name
        name = "xiaohong"
    get_name2()
    print(name)

get_name1()
print(name)

运行结果:

xiaobai

xiaohong

xiaoming

6、作用域

函数的作用域和函数的定义有关,和函数的调用位置无关;如:

name = "xiaohong"
def name1():
    name = "xiaoming"
    def name2():
        print(name)
    return name2

name1()()

运行结果:

xiaoming

解释:
当调用函数name1的时候程序会返回函数name2的内存地址,所以name1()()即时调用函数name2,因为name2是在name1下嵌套的一个函数,而name2本身没有局部变量name,所以程序会到name2的上一层函数中寻找是否有变量name,有即输出,若没有找到则去全局变量中找。

五、向前引用(风湿理论)

下面通过四种情况来理解向前引用
在Python中函数即变量!

情况1:

name = "xiaoming"
def get_name1():
    print(name)
    get_name2()

get_name1()

运行结果:

报错

解释:

当创建一个函数的时候Python会在内存中获取一个空间,用该空间来存放函数内的代码块,并将这个内存空间指向函数名,就和变量一样;在情况1中指向的是get_name1。因为在get_name1中的代码块中又调用了一个函数get_name2,但在内存中没有存在指向get_name2的内存空间,所以Python无法执行这行代码,固报错。

情况2:

name1 = "xiaoming"
name2 = "xiaohong"

def get_name1():
    print(name1)
    get_name2()

def get_name2():
    print(name2)

get_name1()

运行结果:

xiaoming

xiaohong

解释:

和情况1类似,不同的是创建了一个函数“get_name2”,即内存中有内存空间指向了get_name2,所以在get_name1中调用get_name2的时候Python能够执行相应的内容

情况3:

name1 = "xiaoming"
name2 = "xiaohong"

def get_name2():
    print(name2)

def get_name1():
    print(name1)
    get_name2()

get_name1()

运行结果:

xiaoming

xiaohong

解释:

和情况2一样,内存中存在执行get_name1和get_name2的内存空间,所以get_name1和get_name2都能被调用并执行。

情况4:

name1 = "xiaoming"
name2 = "xiaohong"

def get_name1():
    print(name1)
    get_name2()

get_name1()

def get_name2():
    print(name2)

运行结果:

报错

解释:

因为Python代码是从上往下执行的,所以当我们在代码中间调用get_name1的时候Python会去内存中找是否存在指向get_name1的内存空间;可以发现能够在内存中找到指向get_name1的内存空间,然后执行其中的get_name1的代码块,发现其中调用了函数get_name2,但是get_name2函数在这时候还没有被创建,内存中不存在指向get_name2的内存空间,所以无法执行。

六、递归函数

1、递归函数的定义

递归函数是指在函数的内部调用自己

2、特性

  • 必须有一个明确的结束条件
  • 每次执行一次递归后问题的规模都应比上一次减少
  • 递归的效率不高,当递归的次数较多的时候会占用内存

3、案例:

1、使用递归函数计算1到100之间的和

def sum(n):
    if int(n) == 1:
        return n
    else:
        return sum(n - 1) + n

print(sum(100))

运行结果:

5050

2、使用递归函数计算10的阶乘

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

print(product(10))

运行结果:

3628800

4、尾递归调用的优化

定义:在函数执行完最后一步的时候在去调用其它函数。(注意:是最后一步而不是最后一行)
在递归函数的特性中已经知道了递归函数会占用内存,原因是在函数中调用函数时若当前函数并没有完全执行完就调用了函数就会造成内存空间的占用,如:

def func1(x):
    return x

def func2(x):
    return func1(x) + 1

print(func2(4))

解释:

函数func2的return值就相当于两步:1、res = func1(x) 2、return res + 1;在执行第一步的时候函数func2会等待func1执行完后返回的值,所以递归函数若用非尾调用的方法写会作用内存空间

将上面的代码改为尾调用

def func1(x):
    return x + 1

def func2(x):
    return func1(x)

print(func2(4))

七、匿名函数

和def定义的函数不同的是匿名函数是一个表达式,定义方法如下:

lambda x:x+1

这相当于def函数中的

def test(x):
    return x+1

例子:
1、生存一个列表,要求元素必须是2的1-10次方,如:[2,4,8,16,...]

list01 = []
func = lambda x:2**x
for i in range(1,11):
    list01.append(func(i))

print(list01)

运行结果:

[2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

2、修改上面的代码将列表内的所有元素都乘以100后输出

list01 = []
func = lambda x:2**x*100
for i in range(1,11):
    list01.append(func(i))

print(list01)

运行结果:

[200, 400, 800, 1600, 3200, 6400, 12800, 25600, 51200, 102400]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335