python小小白

Pyhon函数|变量、参数、返回值、匿名、闭包、传递

2019-12-30  本文已影响0人  凡有言说

函数就是把一系列操作打包起来,方便以后遇到相似场景直接调用。函数可以是自己定义的,也可以使用Python的内置函数。

Python内置函数详解

1.自定义函数(基础)

def 函数名称(参数名称):
      返回结果

这样可以使我们的代码看起来很简洁,使代码模块化,然后遇到相似场景时直接调用,使得编程效率大大提升。

2.函数中的变量(基础)

我们在函数中常常会使用到变量,变量所处的位置不同,用起来就不一样。定义在函数里的变量被称为“局部变量”,而函数外的变量则被称为“全局变量”。

province = "浙江省"

#定义函数
def my_place():
    city = "杭州"
    print(city)

#调用函数
my_place()

杭州

上面的例子中province是全局变量,city是局部变量。

如果我们在函数中直接使用全局变量,会直接报错。

province = "浙江省"

#定义函数
def my_place():
    city = "杭州"
    print(city)
    
    province = "中国"+province
    print(province)

#调用函数
my_place()

UnboundLocalError: local variable 'province' referenced before assignment

Python发现,函数中没有事先定义province这个变量,即函数里不能直接使用外部的全局变量。有没有解决办法呢?其实很简单,加上global关键字就可以。

province = "浙江省"

#定义函数
def my_place():
    city = "杭州"
    print(city)
    
    global province
    province = "中国"+province
    print(province)

#调用函数
my_place()

杭州
中国浙江省

下面再举几个复杂点的例子:
函数里变量的使用遵循就近原则,函数会调用自己里面定义的变量。

age = 18

#定义函数
def my_func():
    name = "Van"
    print(name)
    
    age = 24
    age += 1
    print(age)

#调用函数
my_func()

Van
25

函数嵌套时和单个函数规则一样

#定义函数
def my_func():
    age = 24
    def my_inner_func():
        age += 1
        print(age)
    my_inner_func()

#调用函数
my_func()

UnboundLocalError: local variable 'age' referenced before assignment

如何改正呢?

age = 18

#定义函数
def my_func():
    age = 24
    def my_inner_func():
        age = 6
        age += 1
        print(age)
    my_inner_func()

#调用函数
my_func()

7

如果特别想调用my_func中定义的age,此时可以使用另一个关键字nonlocal

age = 18

#定义函数
def my_func():
    age = 24
    def my_inner_func():
        nonlocal age
        age += 1
        print(age)
    my_inner_func()

#调用函数
my_func()

25

对比下global和nonlocal:

age = 18

#定义函数
def my_func():
    age = 24
    def my_inner_func():
        global age
        age += 1
        print(age)
    my_inner_func()

#调用函数
my_func()

19

3.函数中的参数(基础)

所谓参数,就是在定义函数时,可以接受一些“变量”进行操作。

#定义函数
def get_sum(a, b):
    sum = a + b
    return sum

#调用函数
get_sum(1, 2)

如果不传入参数会怎样?

#定义函数
def change_list(list):
    list[1] = "欲买桂花同载酒" 
    list[2] = "终不似"
    list[3] = "少年游"

mylist = [1, 2, 3, 4]
#调用函数
change_list()

print(mylist)

TypeError: change_list() missing 1 required positional argument: 'list'

#定义函数
def change_list(list):
    list[1] = "欲买桂花同载酒" 
    list[2] = "终不似"
    list[3] = "少年游"

mylist = [1, 2, 3, 4]
#调用函数
change_list(mylist)

print(mylist)

[1, '欲买桂花同载酒', '终不似', '少年游']

当然也可以直接指定参数值,然后直接调用,这对于一些参数的顺序传值挺有用。

def book_info(name, author):
    print("名字:", name)
    print("作者:", author)
    
book_info(name="这才是心理学", author="斯坦诺维奇")

名字: 这才是心理学
作者: 斯坦诺维奇

也可以直接指定默认值,当Python发现调用函数时没有传入该参数,就会直接使用指定的默认值。

def book_info(name, author="斯坦诺维奇"):
    print("名字:", name)
    print("作者:", author)
    
book_info(name="这才是心理学")

名字: 这才是心理学
作者: 斯坦诺维奇

如果定义函数时,对于一些参数不确定要定义多个,可以使用可变参数,一般是在参数名称的前面加上*号,表示该位置的参数可以是多个。

def book_info(*name, author):
    print("名字:", name)
    print("作者:", author)

book_info("这才是心理学", "超越智商", "决策与理性", author="斯坦诺维奇")

名字: ('这才是心理学', '超越智商', '决策与理性')
作者: 斯坦诺维奇

可以看到,当函数中的参数前加颗*后,就可以接受多个,并且最后都转化成元组。

还有一种情况是加两颗*,它会将传来的参数转化为字典。此时,调用时要用key-value形式。

def book_info(author, **name):
    print("名字:", name)
    print("作者:", author)

book_info(author="斯坦诺维奇", name1="这才是心理学", name2="超越智商", name3="决策与理性")

名字: {'name1': '这才是心理学', 'name2': '超越智商', 'name3': '决策与理性'}
作者: 斯坦诺维奇

4.返回值(进阶)

函数体中 return 语句的结果就是返回值。如果一个函数没有 reutrn 语句,其实它有一个隐含的 return 语句,返回值是 None,类型也是 'NoneType'。return 语句的作用:结束函数调用、返回值(JetBrains_孙健)。

和print相比,print 仅仅是打印在控制台,而 return 则是将 return 后面的部分作为返回值作为函数的输出,可以用变量接走,继续使用该返回值做其它事(JetBrains_孙健)。

指定返回值:

#指定返回值
def my_func(x):
    return x+1

print(my_func(5))

6

当使用 return 语句返回 "返回值",可以将其赋给其它变量作其它的用处。

隐含返回值:

#隐含返回值
def my_func(x):
    print(x)

print(my_func(5))

5
None

所有函数都有返回值,如果没有 return 语句,会隐式地调用 return None 作为返回值。

return除了返回值,还可以起到结束函数调用的作用。

#结束函数调用
def my_func(x):
    print(x)
    return x+1
    print(x+2)

print(my_func(5))

5
6

需要注意的是,return 只能返回单值,但值可以存在多个元素;

#返回值类型
def my_func():
    return [1, 3, 5]

print(type(my_func()))
print(my_func())

<class 'list'>
[1, 3, 5]


def my_func():
    return 2,4,6

print(type(my_func()))
print(my_func())

<class 'tuple'>
(2, 4, 6)

上面的return 2,4,6看似返回多个值,实际上隐式地被Python封装成了一个元组返回。

有时我们会遇到函数嵌套的情况

#函数嵌套
def outer():
    def inner():  
        print("inner")  
    print("outer")

outer()

outer

这里调用 outer(),只会执行 print("outer"),因为 inner 也是一个函数,函数如果要调用,就必须用函数名()方式,如:

#函数嵌套
def outer():
    def inner():
        print("inner")
    print("outer")
    inner()
 
outer()

outer
inner

5.匿名函数(进阶)

当不需要显式地定义函数,直接传入匿名函数更方便。比如有的函数是一次性的。

def add(x):
    return x+2

add(3)

5

(lambda x : x+2)(3)

5

即匿名函数直接在后面添加括号传参即可。我们也可以传入多个参数:

(lambda x, y : x+y)(1, 2)

3

(lambda *args : print(args))(4, 5)

(4, 5)

上面的例子比较简单,假设需要遍历0-9 10个数字,然后取余数。麻烦的写法是:

def get_list():
    my_list = []
    for x in range(10):
        my_list.append(x%2)
    return my_list

print(get_list())

[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]

使用lambda来写:

print([(lambda x : x%2)(x) for x in range(10)])

[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]

以上两种方法作用是相同的,只是写法不同,可以看到匿名函数的方法很简洁。

匿名函数也可以进行赋值,只是一般不采取这种办法。

add = lambda x,y : x+y
print(add(4, 5))

9

print((lambda x,y : x+y)(4, 5))

9

匿名函数可以结合map()、filter()函数使用。

map()是 Python 内置的高阶函数,它接收一个函数 func 和一个序列,并通过把函数 f 依次作用在序列的每个元素上,得到一个新的 序列并返回。即当执行map 函数时,列表里每个值都会被取出来,然后拿去执行传入的函数。

my_list = [0,1,2,3,4,5]

print(list(map(lambda x : x+5, my_list)))

[5, 6, 7, 8, 9, 10]

my_list = [0,1,2,3,4,5]

def add(x):
    return x+5

print(list(map(add, my_list)))

[5, 6, 7, 8, 9, 10]

6.闭包(进阶)

在学习闭包的知识时,会用到前面提到的作用域,即程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的作用范围只能是函数内部范围内,它不能在函数外引用。还会用到嵌套函数,函数不仅可以定义在模块的最外层,还可以定义在另外一个函数的内部,像这种定义在函数里面的函数称之为嵌套函数。

那么什么是闭包呢?这里举一个例子

#内部inner_func可以直接访问外部变量x
def outer_func(x):
    def inner_func():
        print(x)
    inner_func()

outer_func(9)

9

#在内部无法去修改外部变量x
def outer_func(x):
    def inner_func():
        x += 1 
        print(x)
    inner_func()

outer_func(9)

UnboundLocalError: local variable 'x' referenced before assignment

#若想改变,需要使用nonlocal去声明
def outer_func(x):
    def inner_func():
        nonlocal x
        x += 1 
        print(x)
    inner_func()

outer_func(9)

10

#闭包
def outer_func(x):
    def inner_func():
        print(x+1)
    return inner_func

outer = outer_func(9)
outer()

10

一般情况下,函数中的局部变量仅在函数的执行期间可用,具体到上面的例子,outer=outer_func(9)已经调用了一次函数,按理说“9” 被调用之后就不能再被使用了,但是使用outer()时,竟然会记住这个变量。

再来举个闭包的例子

#闭包
def outer_func(x):
    def inner_func(y):
        print(x+y)
    return inner_func

outer = outer_func(9)
outer(1)
outer(5)
outer(8)

10
14
17

当使用闭包的时候,不需要定义全局变量,也可以访问到函数内部的数据。也就是说可以拿到闭包提供的隐藏数据。

闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量(刘志军)。

如何创建一个闭包?
首先,它得是一个嵌套函数,接着内部函数引用 nonlocal 变量,最后嵌套函数的返回是内部函数。

7.参数传递机制

在讨论参数传递机制前,先看两个例子:
第一段代码,a的值没有变,还是2

def my_func(x):
    x += 1

a = 2
my_func(a)
print(a)

2

第二段代码,a的值变了,从[1,2,3]变成了[1,2,3,9]

def my_func(x):
    x.append(9)

a = [1, 2, 3]
my_func(a)
print(a)

[1, 2, 3, 9]

借助http://pythontutor.com/,可以看到第一段代码:

image.png

对于第二段代码


image.png

背后的原因是整数型和列表的数据类型不同。整数型是不可变类型,而列表是可变类型,当整数型 x+=1时,会开辟一个新空间创造新的内存地址给引用,而列表x.append(9)是可变的,不会创建新的内存地址,因此都指向了同一个内存地址。

以上就是Python中函数的传递:赋值传递,关键点在于数据类型是否可变。整型、元组、字符串等类型是不可变的,因此作为参数进行函数操作时会创建新地址,而像列表、字典是可变的数据类型,因此不会创建新的地址。

值得注意的是,当对可变类型进行+=操作时,会创建新空间:

def my_func(x):
    x + [4]

a = [1,2,3]
my_func(a)
print(a)

[1, 2, 3]

参考资料:
Python内置函数详解
Python 中的函数以及函数中的变量
Python 函数中的那些参数的使用
使用 lambda 一行实现一个函数
匿名函数-廖雪峰
匿名函数-极客学院
Python中map函数使用
一步一步教你认识Python闭包
python之函数的返回值
函数中参数的传递机制

公众号.png
上一篇下一篇

猜你喜欢

热点阅读