函数基础与进阶

函数

定义函数的方式

def 函数名():  # 定义阶段(造车轮阶段)
    """函数注释写在这里"""  # 函数相当于工具, 注释相当于工具的说明书
    <代码块>

# 使用  # 调用阶段(开车阶段)
函数名()

def func():
    """func函数的注释"""
    # todo:未来写一个开车函数
    pass
func()

注意:函数在定义阶段的时候不执行函数整体代码,智能检测到语法错误

print(func.__doc__)
可以知道函数中的注释

函数的简单实例

我们不妨尝试用函数来实现登录注册功能

def register():
    """注册函数"""
    username = input('请输入你的用户名:')
    pwd = input('请输入你的密码:')

    with open('user_info.txt', 'a', encoding='utf8') as fa:
        fa.write(f'{username}:{pwd}|')
def login():
    """登录函数"""
    username = input('请输入你的用户名:')
    pwd = input('请输入你的密码:')

    with open('user_info.txt', 'r', encoding='utf8') as fr:
        data = fr.read()
        user_list = data.split('|')
        print(user_list)
        user_info = f'{username}:{pwd}'
        if user_info in user_list:
            print('登录成功')
        else:
            print('傻逼,密码都忘了')

有没有发现,当函数没有参数的时候,里面的代码块和在外面打的没有什么区别。其实,函数更多的是一种思想,而不是一种技术

函数的三种定义方式

三种方式分别为无参函数,有参函数,空函数

无参函数

def add():
    """无参函数"""
    x = input('num1:')
    y = input('num2:')

    print(int(x) + int(y))

无参函数可以进行单独使用,上面的登录注册也是同理

有参函数

有参函数在函数中增加了参数,再往函数中输入参数之后才能使用

def add(x, y): 
    """有参函数"""
    print(int(x) + int(y))


print(1)
x = input('num1:')
y = input('num2:')
add(x, y)

结果为:
num1:1
num2:2
3

空函数

顾名思义,里面什么都没有

当你不知道函数怎么写的时候,可以先放一边,等以后再来写

def func():  # 只知道工具叫什么名字,但不知道如何造出这个工具
    pass

函数的调用

def add(x, y):
    return x+y


res = add(10, 20)  # 函数的调用
print(res*12)	# 360

这就是函数简单的调用,无参函数一般都是直接执行func(),有参函数则要在括号中输入符合要求的参数

函数的返回值

函数的返回值就是return,当函数执行到return时,直接返回return后面的数值,有一点类似于break的用法,可以配合if函数的使用来返回多种情况的值

def add(x, y):
    return (x, y, x + y)  # return可以返回任意数据类型
    return x, y, x + y  # return可以返回任意数据类型,不加括号返回多个值时,默认用元祖的形式返回

到这里函数的基础部分已经结束了。函数并不难,只要多敲几遍熟悉熟悉,相信马上就能熟能生巧

可变长参数

形参

位置形参

默认形参

实参

位置实参

关键字实参

可变长参数之*

如果当你的用户可以输入很多的值,或者输入的值不固定,也就是实参的个数很多或输入的值不固定的时候的时候,我们不可能设定很多的形参,也没法办到一一对应数量不同的实参。这个时候,我们就需要用到可变长参数。

def func(name, pwd, *args):  
    print('name:', name, 'pwd:', pwd)
    print(args)  # args就会接收多余的位置实参
    return 1


res = func('hyc', 123658, 18, 180, 'a', ['reading'])  # 用户使用func函数的时候会传用户的信息进入函数,但是不知道传几个
print(res)

结果为:
name: hyc pwd: 123658
(18, 180, 'a', ['reading'])
1

*args会接收多余的位置实参,args是自己定义的,但是Python都用args来表示。这是是约定俗成的

*args一般用于用户使用func函数传用户的信息进入函数,但是不知道传几个的时候,他会将传出来的值合并成一个元组,其中的值可以为任意数据类型

虽说*args看似完美的解决一些问题,但是却似乎在实际应用中并不是那么的实用。它输出的是元组,别的人看到很难理会这个元组的意思,之后讲的还有一这方法就较为完美的解决了这一个问题

但是在这之前,不妨先看看*的其他用法,他可以将元组打散成位置实参传给形参

def func(name, pwd, x, y, z):
    print('name:', name, 'pwd:', pwd)
    print(x, y, z)
    return 1


tup = (4, 2, 3)
res = func('hyc', 123456, *tup)  # *会把元祖打散成位置实参传给形参
print(res)
结果为:
name: hyc pwd: 123456
4 2 3
1

可变长参数之**

**args的用法和*args较为类似。但是最明显的不同之处在于,**args只能传关键字实参,并且输出的是字典。这样子输出的信息就更加的清楚明了了。

def func(name, pwd, *args, **kwargs):
    print('name:', name, 'pwd:', pwd)
    print(args)
    print(kwargs)  # kwargs会接收多余的关键字实参,以字典形式存储
    return 1


res = func('hyc', 123456, 1, 2, 3, 4, age=21, height=180)
print(res)

结果为:
name: hyc pwd: 123456
(1, 2, 3, 4)
{'age': 18, 'height': 180}
1

结果为:
name: hyc pwd: 123456
(1, 2, 3, 4)
{'age': 18, 'height': 180}
1

当然,**也可以将字典打算成关键字实参传给形参

def func(name, pwd, **kwargs):
    return kwargs


dic = {'a': 5, 'b': 2}  # **dic  --> a=1 , b=2
res = func('nick', 123658, **dic)
# res = func('nick', 123658, a=1, b=2) 
print(res)
结果为
{'a': 5, 'b': 2}

函数对象

函数名等同于变量名,即变量名有的方法,函数名同样也有, 被引用 ; 当做容器元素 ; 当做函数参数 ; 当做函数返回值

def func():
    print('from func')
print(func)		

并没有报错,可以发现它指向一块内存地址

同时它也可以和变量名一样被赋值

def func():
    print('from func')

f = func  # func可以加()调用,那么f也可以加()调用
print(f, func)
f()

变量名还可以放入容器类数据类型

def func():
    print('from func')

a = 1

lt = [1, 2, a, func]
lt[-1]()

函数名还可以被当做参数

def func():
    print('from func')

def f2(name):  # name = func
    name()  # func()

# f2(a)
f2(func)

函数的返回值

def func():
    print('from func')
def f3(name):  # name = func
    return name  # name = func

res = f3(func)  # res = func
print(res)
res()

函数的嵌套

和if判断一样,函数之内也可以进行嵌套

def f1():
    def f2():
        print('from f2')

    return f2


abc = f1()  # f1()拿到函数的返回值,函数的返回值是f2, abc就相当于f2
abc()

它的用法也很简单,但是还是有一些需要让人注意的地方。这些我们会在之后讲

现在,我们可以先用嵌套的方法,来实现一下一个函数求圆的面积和周长

import cmath

def circle(r, action):
    def area():
        return cmath.pi * r ** 2

    def zhouchang():
        return 2 * cmath.pi * r

    if action == 'area':
        area()

    elif action == 'zhouchang':
        zhouchang()


area = circle(3, 'area')
print(area)

zhouchang = circle(3, 'zhouchang')
print(zhouchang)

当然也可以将它和不用函数嵌套的进行比较

import cmath


def circle(r, action):
    if action == 'area':
        return cmath.pi * r ** 2

    elif action == 'zhouchang':
        return 2 * cmath.pi * r


area = circle(3, 'area')
print(area)

zhouchang = circle(3, 'zhouchang')
print(zhouchang)

然后你们就会发现,好像使用嵌套还让代码变得更长了。。

这么看来嵌套除了看起来牛逼好像没有什么实际的用处。。

其实如果代码变得很长时,适当的嵌套还是可以节省代码的长度的

下面这个例子就很好的说明了这一点:在比较四个值中引入比较俩个值的函数:

def self_max(x, y):
    if x > y:
        return x
    return y


res = self_max(10, 20)
print(res)


# 比较四个值
def self_4_max(x, y, z, c):
    res1 = self_max(x, y)
    res2 = self_max(z, c)
    res = self_max(res1, res2)

    return res


res = self_4_max(10, 20, 1, 100)
print(res)

这时你会发现,如果比较四个值又重新写一段代码的话会复杂一些

名称空间和作用域

名称空间

名称空间是用来存放名字的(变量名/函数)

内置名称空间

内置名称空间是Python解释器独有的

函数调用必须得定义, 从来都没定义过. Python解释器启动的时候python自动开辟内置名称空间存放了这些python的内置方法,python解释器停止解释之后才会销毁

全局名称空间

除了内置空间和局部空间,其他的都是全局空间

全局空间需要自己定义,python文件执行之后才可能有全局名称空间,文件结束之后才会销毁

局部名称空间

函数内定义的变量名/函数名都存放在局部名称空间

局部名称空间也需要自己定义, 必须得在函数调用之后才会生成, 调用结束之后就会销毁

三种名称空间的执行顺序

内置 --> 全局 --> 局部
z = 10
def f1():
    x = 10
    def f2():
        y = 10
        print('from f2')

三种名称空间的查找顺序

首先从自己当前位置开始 --> 局部 --> 全局 --> 内置

x = 1
def f1():
    x = 3
print(x)

你会发现,结果为1,这就是因为x=3是函数内部的变量名,所以不会被执行

def f1():
    return x
    # print(x)  # f1中的局部

def f2():
    x = 2  # x=2只能在f2中使用
    f1()


f2()  # f1中的局部和f2中的局部互不干涉

这里也是如此,函数中的变量只能再这个函数中使用

global & nonlocal

如果想要把函数中的变量变为全局,就可以使用global

x = 1


def f1():
    global x  # 声明x为全局的x
    x = 3


f1()
print(x)	# 3

nonlocal则可以针对嵌套函数局部之间的修改

nonlocal

def f1():
    x = 1
    def f2():
        nonlocal x  # 针对嵌套函数局部之间的修改
        x = 3
    f2()
    print(x)


f1()	# 3

作用域的关系仅适用不可变数据类型,不适用于可变数据类型

lt = [1,2,3]  

def f1():
    lt.append(4)

f1()

print(lt)


dic = dict()

def login():
    dic['name'] = 'nick'
login()

def shopping():
    if dic.get('name'):
        print('购物成功')
    else:
        print('请登录')
shopping()


s = '空'

def login():
    s = 'nick'
login()
print(s)


结果为:
[1, 2, 3, 4]
购物成功
空
posted @ 2019-08-09 20:33  黑井白子  阅读(122)  评论(0编辑  收藏  举报
Live2D