Python之函数

函数

什么是函数

函数是可以重复执行的语句块,可以重复使用

作用

  • 用于封装语句块,提高代码的重用性
  • 定义用户级别的函数

函数定义(创建)

def 函数名(形参列表):
  语句块

说明

  • 函数的名字就是语句块的名称
  • 函数名的命名规则与变量名相同(函数名必须为标识符)
  • 函数有自己的名字空间,在函数外部不可以访问函数内部的变量,在函数内部可以访问函数外部的变量,通常让函数处理外部数据需要用参数给函数传入一些数据
  • 函数的参数列表可以为空
  • 语句部分不能为空,如果为空需要填充 pass 语句

函数调用

函数名(实际调用传递参数列表)    # 实际调用传递参数以后称为实参

函数调用说明

  • 函数调用是一个表达式
  • 如果没有 return 语句,此函数执行完毕后返回 None 对象
  • 如果函数需要返回其它的对象需要用到 return 语句

return 语句

语法

return [表达式]

作用

用于函数中,结束当前函数的执行,返回到调用该函数的地方,同时返回一个对象的引用关系

说明

  • return 语句后跟的表达式可以省略,省略后相当于 return None
  • 如果函数没有 return 语句,则函数执行完最后一条语句后返回 None (相当于在最后加了一条 return None 语句)
  • 函数调用一定会返回一个对象的引用

函数的参数传递

位置传递

  • 实际参数和形式参数通过位置进行传递的匹配
  • 实参个数必须与形参个数相同
def myfun(a, b, c):
    print(a, b, c)

myfun(1, 2, 3)            # 1, 2, 3
myfun(100, 200, 300)      # 100, 200, 300

序列传参

序列传参是在函数调用过程中,用 * 将序列拆解后按位置传参的方式进行参数传递

def myfun(a, b, c):
    print(a, b, c)

s = [1, 2, 3]
myfun(*s)          # * 表示把s拆成 1,2,3,打印结果为 1,2,3

s2 = "ABC"
myfun(*s2)         # * 表示把s2拆成 'A','B','C' 打印结果为 A,B,C

关键字传参

  • 关键字传参是指传参时,按着形参的名称给形参赋值
  • 实参和形参按名称进行匹配
  • 实参和形参可以不按位置匹配
def myfun(a, b, c):
    print(a, b, c)

myfun(a=99, b=88, c=188)    # 99, 88, 188
myfun(b=22, c=33, a=11)     # 等同于myfun(11,22,33)
myfun(c=3, b=2, a=1)        # 1, 2, 3

字典关键字传参

  • 指实参为字典,将字典用 ** 拆解后进行关键字传参
  • 字典的键名和形参名必须一致
  • 字典键名必须为字符串
  • 字典的键名要在形参中存在
def myfun(a, b, c):
    print(a, b, c)

myfun(**{'c': 3, 'b': 2,'a':1)    # 1 2 3
myfun(**{'d': 3, 'b': 2,'a':1})   # 报错,字典的键名和形参名必须一致
myfun(**{'c': 3, 'b': 2,'a':1,'d':4})  # 报错,字典的键名都要在形参中存在

综合传参

  • 函数传参方式,在能确定形参能唯一匹配到相应实参的情况下可以任意组合
  • 传参时先位置传参,后关键字传参
def myfun(a, b, c):
    pass

myfun(100, *[200, 300])                # 正确
myfun(*'AB', 300)                      # 正确
myfun(100, c=300, b=200)               # 正确 
myfun(1, **{'c': 3, 'b': 2})           # 正确
myfun(**{'c': 3, 'b': 2}, a=1)         # 正确
myfun(b=2, c=3, 1)                     # 错误,不能确定1给谁?

函数的缺省参数

语法

def 函数名(形参名1=默认实参1, 形参名2=默认实参2, ...):
  • 缺省参数必须自右至左依次存在,如果一个参数有缺省参数,则其右侧的所有参数都必须有缺省参数
  • 缺省参数可以有0个或多个,甚至全部都有缺省参数
def info(name, age=1, address="未填写"):
    print(name, "今年",age, '岁, 家庭地址是:', address)

info('tarena', 15)
info('小魏', 20, '北京市朝阳区')
info('小李')

def test(a, b=10, c):  # 错误
    pass

函数的形参定义方式

位置形参

def 函数名(形参名1, 形参名2, ...):
    语句块

星号元组形参

  • 收集多余的位置传参
  • 元组形参名通常用:args
def 函数名(*元组形参名):
    语句

def func(*args):
    print(len(args), args)

func(1, 2, 3, 4)   # 4, 1, 2, 3, 4

命名关键字形参

  • 所有的参数都必须用关键字传参或字典关键字传参传递
  • * 为语法标识符,遇到 *,右边所有的参数必须以关键字传参
def 函数名(*, 命名关键字形参):
    语句
或
def 函数名(*args, 命名关键字形参):
    语句

def fn(*, d, e):
    print("d=", d)
    print("e=", e)

fn(d=100, e=200)         # 合法调用
fn(1, 2)                 # 不合法,不能用位置传参
fn(1, 2, d=100, e=200)   # 不合法,不能用位置传参

def fm(*args, d, e):
    print(args)
    print('d=', d)
    print('e=', e)

fm(1, 2, d=100, e=200)  
# (1, 2)
# d= 100
# e= 200

fm(*"AB", **{'e': 20, 'd':10})
# ('A', 'B')
# d= 10
# e= 20

fm(*[5,6], **{'e': 20, 'd':10})
# (5, 6)
# d= 10
# e= 20

双星号字典形参

  • 收集多余的关键字传参
  • 通常字典形参名定为: kwargs
def 函数名(**字典形参名):
    语句

def func(**kwargs):
    print("关键字参数个数是:", len(kwargs))
    print("kwargs =", kwargs)

func(name='Jason', age=15)
# 关键字参数个数是: 2
# kwargs = {'name': 'Jason', 'age': 15}

func(name='Jason', age=15,score = 90)
# 关键字参数个数是: 5
# kwargs = {'name': 'Jason', 'age': 15,'score':90}

func()
# 关键字参数个数是: 0
# kwargs = {}

函数的参数说明

  • 缺省参数,位置形参,星号元组形参,命名关键字形参和双号星字典形参可以混合使用
  • 函数参数自左至右的顺序为:位置形参,星号元组形参,命名关键字形参,双星号字典形参
def f1(a, b, *args, c, **kwargs):
    print(a)  # 1
    print(b)  # 2
    print(args) # 3,4
    print(c)  # 5
    print(kwargs)  # {'d':7,'e':7}

f1(1, 2, 3, 4, d=6, c=5, e=7)
# a = 1
# b = 2
# args = (3,4)
# c = 5
# kwargs = {'d':7,'e':7}

f1(*"hello",d=6,**{'c':5,'e':7})  # 相当于 f1('h','e','l','l','o',d=5,c=5,e=7)
# a = 'h'
# b = 'e'
# args = ('l','l','o')
# c = 5
# kwargs = {'d':6,'e':7}

函数的不定长参数

可以接收任意的位置传参和关键字传参

def fn(*args, **kwargs):
    print(args)
    print(kwargs)

fn()
# args = (), kwargs = {}

fn(1,2,3,4)
# args = (1, 2, 3, 4), kwargs = {}

fn(1,2,3,'a','b',c=4,d=5,**{'e':7,'f':8})
# args = (1, 2, 3, 'a', 'b'), kwargs = {'c': 4, 'd': 5, 'e': 7, 'f': 8}

函数变量

函数名是变量,它在创建函数时绑定一个函数

def f1():
    print("f1被调用")

fx = f1
fx()     # 等同于 f1()
del f1   # 对fx没影响
# ***************************************************
def f1():
    print("hello")

def f2():
    print("world")

f1, f2 = f2, f1  # 交换两个变量的绑定关系
f1()             # world

函数作为参数和返回值

  • 一个函数可以作为另一个函数的参数传递
def f1():
    print("hello")

def f2():
    print('world')

def fx(fn):
    print(fn)
    fn()  # 

fx(f1) 
# <function f1 at 0x000001F2F48CC1E0> 函数在内存中的地址
# hello

fx(f2) 
# <function f2 at 0x000001F2F6598A60> 函数在内存中的地址
# world

# 示例
def fx(a, fn):
    return fn(a)

L = [1,2,3,4]
print('最大值是:', fx(L, max))  # 4
print('最小值是:', fx(L, min))  # 1
print('和是:', fx(L, sum))     # 10
  • 函数可以返回另一个函数,即函数为作函数的返回值
def get_fx():
    s = input('请输入您要做的操作: ')
    if s == '求最大':
        return max
    elif s == '求最小':
        return min
    elif s == '求和':
        return sum

L = [2,4,6,8,10]
print(L)
f1 = get_fx()
print(f1(L))

函数嵌套定义

函数的嵌套定义是指一个函数里用 def 语句来创建其它的函数

def fn_outer():
    print("fn_outer被调用!")
    def fn_inner():
       print("fn_inner被调用")
    fn_inner()
    print('fn_outter调用结束')
    retuen fn_inner

fn_outer()
fn_inner()  # 报错
fx = fn_outer()
fx()        # 调用fn_outer内部的fn_inner()函数

函数的文档字符串

函数内部,第一个没有赋值给任何变量的字符串为文档字符串

def 函数名(形参列表):
    '''函数的文档字符串'''
    函数语句块

函数的属性

  • __doc__ 属性用于绑定该函数的文档字符串
  • __name__ 属性用于绑定函数名的字符串

函数定义语句

# def语句的语法
# 完整的函数定义语句

[@装饰器名1]      # 详见系列文章Python之装饰器
[@装饰器名2]      # 详见系列文章Python之装饰器
...
def 函数名([位置形参],[*[元组形参名]], [命名关键字形参], [**字典形参]):
    '''文档字符串'''
    语句块

全局变量和局部变量

局部变量

  • 定义在函数内部的变量称为局部变量(函数的形参也是局部变量)
  • 局部变量只能在函数内部使用
  • 局部变量在函数调用时才能够被创建,在函数调用之后会自动销毁
  • 在函数内首次对变量赋值是创建局部变量,再次为变量赋值是修改局部变量的绑定关系
  • 在函数内部的赋值语句不会对全局变量造成影响
  • 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个模块范围内访问

全局变量

  • 定义在函数外部,模块内部的变量称为全局变量
  • 全局变量,所有的函数都可以直接访问(但函数内部不能将其直接赋值)
a = 100
b = 200
def fx(c):
    d = 400
    print(a, b, c, d)

fx(300)
print('a =', a)    # a = 100
print('b =', b)    # b = 200
print('c =', c)    # 出错,不能访问局部变量
print('d =', d)    # 出错,不能访问局部变量
 
def fy(c):
    d = 40
    a = 10         # 此时会创建局部变量a,不会修改全局变量a
    print(a, b, c, d)   

fy(30)             # 10, 200, 30, 40  

globals 函数和 locals 函数

  • globals() 返回当前全局作用域内变量的字典
  • locals() 返回当前局部作用域内为量的字典
a = 1
b = 2
c = 3

def f1(c, d):
    e = 300
    print(locals())        # {'c': 100, 'd': 200, 'e': 300}
    print(globals())       # 返回结果中包含a,b,c,f1等全局变量
    print(c)               # 100
    print(globals()['c'])  # 返回全局变量的c变量的值 

f1(100, 200)

Python作用域

作用域也叫名字空间,是访问变量时,查找变量名的范围空间

Python的四个作用域LEGB

  • 局部作用域 Local function 简称 L
  • 外部嵌套函数作用域 Enclosing Function Locals 简称 E
  • 函数定义所在模块(文件)的作用域 Global(Mudule) 简称 G
  • Python 内置模块的作用域 Builtin(python) 简称 B

变量名的查找规则

  • L --> E --> G --> B
  • 在默认情况下,对变量名赋值会创建或改变本作用域内的变量
v = 100  # 全局作用域
def fun1():
    v = 200  # 外部嵌套函数的作用域
    print('fun1内的v=', v)
    def fun2():
        v = 300  # 局部作用域
        print("fun2内的v=", v)
    fun2()

fun1()
# fun1内的v= 200
# fun2内的v= 300

print("v =", v)  # v = 100

# ****************************************************** #

v = 100  # 全局作用域
def fun1():
    v = 200  # 外部嵌套函数的作用域
    print('fun1内的v=', v)
    def fun2():
        print("fun2内的v=", v)
    fun2()

fun1()
# fun1内的v= 200
# fun2内的v= 200

print("v =", v)  # v = 100

global 语句

作用

  • 告诉解释器,global 语句声明的一个或多个变量,这些变量的作用域为模块级的作用域,也称作全局变量
  • 全局声明 global 将赋值变量映身到模块文内部的作用域

语法

global 变量1, 变量2, ...

v = 100

def fn():
   global v
   v = 200

fn()
print(v)  # 200 

说明

  • 全局变量如果要在函数内部被赋值,则必须经过全局声明(否则会被认为是局部变量)
  • 全局变量在函数内部不经过声明就可以直接访问
  • 不能先声明局部的变量,再用 global 声明为全局变量,此做法不附合规则
  • global 变量列表里的变量不能出现在此作用域内的形参列表里
# 不能先声明局部的变量,再用 global 声明为全局变量,此做法不附合规则
def fx():
  x = 100   # 此处会出现语法警告
  global x
  x = 200

# global 变量列表里的变量不能出现在此作用域内的形参列表里
def fx(v):
  global v   # 报错
  v = 200

nonlocal 语句

作用

*告诉解释器,nonlocal 声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量

语法

nonlocal 变量名1, 变量名2, ...

def f1():
    var = 200
    print("f1里的var=", var)    # 200

    def f2():
        nonlocal var
        var = 300  
        print("f2里的var=", var)   # 300
    f2()

    print("f2调用结束后f1里的var值为", var)  # 300

f1()
print('全局的var =', var)  # 100

说明

  • nonlocal 语句只能在被嵌套函数内部进行使用
  • 访问 nonlocal 变量将对外部嵌套函数的作用域的变量进行操作
  • 当有两层或两层以上的函数嵌套时,访问 nonlocal 变量只对最近一层的变量进行操作
  • nonlocal 语句的变量列表里的变量名,不能出现在此函数的参数列表中
# 1. nonlocal语句只能在被嵌套函数内部进行使用
def f1():
    # nonlocal x  # 这是错的,因为没有外部嵌套函数
    x = 100
f1() 

# 3. 当有两层或两层以上的函数嵌套时,访问nonlocal变量只对最近一层的变量进行操作
def f1():
    v = 100
    def f2():
        v = 200
        def f3():
            nonlocal v  # 只对f2里的v进行操作
            v += 1
        f3()
        print("f2最后的v=", v)
    f2()
    print('f1最后的v=', v)

f1()

# 4. nonlocal语句的变量列表里的变量名,不能出现在此函数的参数列表中
def f1():
    v = 100
    def f2(v):
        # nonlocal v  # 出错,v已在形参列表中...
        v += 1
    f2(20)
f1()

lambda 表达式(匿名函数)

作用

创建一个匿名函数对象,同 def 类似,但不提供函数名

语法

lambda [参数1, 参数2, ...]: 表达式

  • lambda 只是一个表达式,它用来创建一个函数对象
  • lambda 表达式调用时,先执行冒号后 : 的表达式,并返回表达式的结果的引用
  • lambda 表达式创建的函数只能包含一条 表达式
  • lambda 比函数简单,且可以随时创建和销毁,有利于减少程序的偶合度
def myadd(x, y):
        return x + y
# 以上函数可以改写为:
myadd = lambda x, y: x + y
print('2 + 3 =', myadd(2, 3))

# ******************************************* #

res = lambda x:(x**2+1) % 5 == 0
print(res(3))   # True
print(res(4))   # False

# ****************************************** #

res = lambda x,y:x if x > y else y
print(res(3,4))   # 4
print(res(2,1))   # 2

# ******************************************* #

def fx(f, x, y):
    print(f(x, y))

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

推荐阅读更多精彩内容