Python学习笔记:函数和变量传递

函数和变量传递

函数

函数是一个可重复使用的代码块。函数能提高应用的模块性和代码的重复利用率。

Python提供了许多内建函数供用户直接调用,比如print()。同时也可以自己创建函数,称之为用户自定义函数。

函数定义

使用def关键字来定义函数。语法格式为:

def 函数名 (参数):
    """
    说明文档
    """
    执行语句
    (return 返回值)

即使函数没有参数值,参数的括号也不能省略。

如:

def add(a, b):
    """
    输入两个值,返回他们的和
    """
    return a + b

上述代码段中定义了一个函数add(),括号内包含两个参数ab,函数的返回值为a+b

返回值return是指退出函数并返回结果,一个函数可以没有返回值,也可以有多个返回值;有多个返回值时,若只有一个变量来接受,则返回值为元组类型;或者必须有对应的变量数量来接收,否则会报错。

函数调用

  • 一个简单的两个参数输入返回一个值的函数:
def add(a, b):
    return a + b

print(add(1, 2))
# 输出:3

​ 调用函数时,需严格按照函数的参数格式输入,包括参数的数量和顺序。

​ 上述代码中,add()函数指定了两个输入参数,用户在调用函数时也需要输入两个参数来获得函数的返回值。

  • 多个参数输入多个参数返回的函数:
def my_print(a, b, c):
    return a, b, c

a = my_print(1, 2, 3)
print(a)
# 输出:(1, 2, 3)
a, b = my_print(1, 2, 3)
print(a, b)
# 错误
a, b, c = my_print(1, 2, 3)
print(a, b, c)
# 输出:1 2 3

调用函数时请注意参数的对应关系。

参数传递

参数分类

函数的参数可以分为形式参数和实际参数。

  • 形式参数(形参):在定义函数时,函数名后面括号中的参数就是形式参数。
  • 实际参数(实参):在调用函数时,函数名后面括号中的参数称为实际参数。
def add(a, b):
    return a + b

print(add(1, 2))

上述代码中,定义函数add(a,b)时,其中的"a"和"b"就是形式参数;调用函数时add(1,2)中的"1"和"2"就是实际参数。

形式参数

形式参数可以分为必选参数,默认参数,可变长位置参数,可变长关键字参数。

  • 必选参数和默认参数

    如上门所述,调用函数时,需严格按照函数的参数格式输入,指的就是必备参数。

    而默认参数则是调用函数时可选择是否传入,若不传入则由默认值替代。

    如:

    def stu_info(name, age, sex="male"):  # 若不传入sex,则默认值为"male"
        print(f"name is {name},age is {age},sex is {sex}")
    
    stu_info("user1", "20", "female")
    stu_info("user2", "20")
    # 输出:
    # name is user1,age is 20,sex is female
    # name is user2,age is 20,sex is male
    
  • 可变长位置参数

    可变长位置参数是指参数输入的数量不确定,使用星号*来定义可变长位置参数。

    加了星号的变量名会存放所有未命名的变量参数,打包成元组形式存储元素。

    如:

    def my_args(*nums):
        print(nums)
    
    my_args()
    my_args(1)
    my_args(1, 2)
    # 输出:
    # ()
    # (1,)
    # (1, 2)
    

    若要传入元组、列表或者集合中的元素(可以简单理解为解包),也要使用*来传入。传入的元组会被解包成单个元素再进行处理。

    如:

    def myprint(a, b, *args):
        print(a, b, args)
    
    myprint(1, *(2, 3), 4, 5)
    # 输出:1 2 (3, 4, 5)
    

    在上述代码中,元组(1, 2)被解包成两个元素;然后再进行位置参数的处理,即1,2对应形参a和b,3,4,5被打包成args元组输出。

  • 可变长关键字参数

    与可变长位置参数类似,使用两个星号**来定义可变长关键字参数。读入的参数会以字典形式来存储。

    def my_kwargs(**nums):
        print(nums)
    
    my_kwargs()
    my_kwargs(a=1)
    my_kwargs(b=2, c=3)
    
    # 输出
    # {}
    # {'a': 1}
    # {'b': 2, 'c': 3}
    

    同样的,如果要在参数内传入解包的字典,需要使用**

    def myprint(x, y, **kwargs):
        print(x, y, kwargs)
    
    myprint(1, 2, **{'a': 1, 'b': 2})
    # 输出:1 2 {'a': 1, 'b': 2}
    

实际参数

实际参数可以分为位置参数,关键字参数。

  • 位置参数:按照函数的参数位置依次输入参数。

    如:

    def stu_info(name, age, sex):print(f"name is {name},age is {age},sex is {sex}")
        
    stu_info("user1", "20", "female")
    
  • 关键字参数:使用关键字给定参数值;和位置参数一起使用时,放在位置参数后。

    如:

    def stu_info(name, age, sex):
        print(f"name is {name},age is {age},sex is {sex}")
        
    stu_info("user1", sex="male", age="18")
    

值传递和引用传递

根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:

  1. 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
  2. 引用(地址)传递:适用于实参类型为可变类型(列表,字典);

值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。

  • 可更改对象与不可更改对象

可更改对象与不可更改对象的区别在于对象本身是否可变。字符串、元组、数值是不可更改对象,而列表、字典等则是可更改对象。

在更改不可更改对象的值时,实际上是生成了一个新的对象再指向原变量。

而更改可更改对象的值时,只是更改了对象内部的数据,对象本身没有发生变化。

如:

num = 10
print(f"第一个num的地址为{id(num)}")
num = 20
print(f"第二个num的地址为{id(num)}")
# 第一个num的地址为2610845516304
# 第二个num的地址为2610845516624

my_list = [1, 2, 3]
print(f"第一个list的地址为{id(my_list)}")
my_list[0] = 4
print(f"第二个list的地址为{id(my_list)}")
# 第一个list的地址为2023694724288
# 第二个list的地址为2023694724288

在python函数中,参数传递分为不可变类型传参和可变类型传参:

  • 不可变类型:如果传入一个不可更改对象参数,传递的只是该参数的值,不会影响参数本身。

    如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。

  • 可变类型:如果传入一个可更改对象参数,则传递的参数本身,修改函数的值会修改该变量的值。

    如 fun(my_list),则是将 my_list真正地传过去,修改后 fun 外部的 my_list 也会受影响。

python 中一切都是对象,严格意义上来说不能说值传递还是引用传递,应该说传不可变对象和传可变对象。

变量作用域

LEGB

在python访问一个变量时,其查找顺序遵循LEGB机制:

  • L:local,局部作用域,即函数中定义的变量;
  • E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域;
  • G:global,全局变量,即模块级别定义的变量;
  • B:built-in,系统固定模块里面的变量,即python的内置作用域。
  • 搜索变量的优先级顺序依次是:局部作用域>外层作用域>当前模块中的全局变量>python内置作用域

下面是一个简单的示例:

s = 'aaa'

def f1():
    print(s)
    
def f2():
    s = 'bbb'
    f1()
    
f1()
# 输出:aaa

在函数 f1() 中,变量 s 是在全局作用域中定义的,它的值是 'aaa'

在函数 f2() 中,另一个名为 s 的变量被定义为 'bbb',但它只存在于 f2() 的本地作用域中。

f2() 调用 f1() 时,它会调用全局变量 s,因此输出的值为 'aaa'

global关键字

如果想在函数中声明全局变量或者改变临时变量,可以使用global关键字。

如:

def f1():
    global s
    s = "aaa"

def f2():
    print(s)

f1()
f2()
# 输出:aaa

在上述代码中,函数f1()定义了一个全局变量s = "aaa",首先调用函数f1()来声明全局变量,再调用f2()1时,变量s被识别为全局变量并输出。

如果全局变量是不可变类型,想要在函数中对函数变量进行修改,则需要先在函数内声明其为global,再进行修改;如果是可变数据类型则可以直接修改。

s = "aaa"

def f1():
    print(s)

def f2():
    global s
    s = "bbb"

def f3():
    print(s)

f1()
f2()
f3()
# 输出:
# aaa
# bbb

特殊函数

匿名函数

匿名函数是指没有名字的函数。匿名函数使用关键字lambda来定义,函数体只有一个表达式。

匿名函数可以作为参数传递给其他函数,可以使代码更简洁、更易于阅读。

语法表达式为lambda 参数:返回值

如:

func1 = lambda a, b: a + b
print(func1(1, 2))
# 输出:3

递归函数

如果一个函数在内部调用函数本身,这个函数就是递归函数。

下面是一个递归函数,用于计算输入值到1的和:

def calculate(n):
    if n > 0:
        return n + calculate(n - 1)
    else:
        return 0

计算输入值到1的和用数学表达式表达为n + (n-1) + (n-2) + ... + 1

当给定一个变量 n 时,函数会返回 n + n-1 ,并将 n-1 递归给函数本身进行下一次计算,以此循环,直至返回到 n-1=0 时结束递归。

使用递归函数需要考虑好每一层递归的执行代码,即问题的共同规矩;要考虑好函数的出口。

注意,函数的递归次数不要超过1000次,否则会造成堆栈溢出报错等问题。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值