Python基础-函数

函数基本使用

python中定义函数所用的关键字是def, def 函数名() : 冒号代替其他编程语言的大括号,然后换行缩进写的代码就是函数体。

def welcome():
    '''
    一个简单的函数
    :return:
    '''
    print("Hello World !")


welcome()

打印结果:

Hello World !

 

三引号中间的内容属于函数的注释文档,在实际项目中都尽量要把每个函数的文档注释写好,在这里为了节约篇幅,本文下面的代码示例就不再添加这个注释了。

 

函数还可以嵌套,也就是函数里面有函数:

def welcome(name):
    def go(name):
        print('我最棒:' + name)
    go(name)

welcome('小明')

打印结果:

我最棒:小明

 

函数的参数

形参和实参

def welcome(username):
    print("Hello World ! My name is " + str(username))

welcome("小明")
welcome("小花")

打印结果:

Hello World ! My name is 小明
Hello World ! My name is 小花

这是一个带参数的函数,在函数welcome中,username就是一个形参,也就是形式参数,是用来接收调用函数时传入的参数,你传的是啥它就是啥,传人它就是人,传鬼它就是鬼的那种。

实参就是实际参数,在调用函数的时候,传递是小明,那么小明就是实参,传递的是小花,那么小花也是实参,实参传递给函数后,会赋值给函数中的形参,然后我们就可以在函数中使用到外部传入的数据了。

 

参数默认值

写Java的时候最痛恨的就是方法不能设置默认值,使得必须得重载才行。

python允许我们给函数的形参设置一个默认值,不传参数调用的话,就统一默认是这个值。

def welcome(username = '奥特曼'):
    print("Hello World ! My name is " + str(username))

welcome("小明")
welcome()

打印结果:

Hello World ! My name is 小明
Hello World ! My name is 奥特曼

 

修改参数后影响外部

在函数中修改参数内容会不会影响到外部,这个问题取决于实参的类型是不是可变的,可不可变就是可不可以修改。

 

字符串就是一种不可变的类型。

比如:

name = "小明"
name = "小花"

请问,我是把"小明"修改成了"小花"吗? 答案是 非也。

实际上我是把"小花"这个字符串赋值给了name,让name指向了这个新字符串,替换掉了原来的"小明",原来的"小明"仍然是"小明",没有受到一点改变。

在python中,不可变类型有:整数、字符串、元组,可变类型有:列表、字典。如果传递的参数包含可变类型,并且在函数中对参数进行了修改,那么就会影响到外部的值

def change(lis):
    lis[1] = '小明他大爷'

names = ['小明','小花','小红']
change(names)
print(names)

打印结果:

['小明', '小明他大爷', '小红']

 

如果我们不希望出现这种事情,那么就将对象复制一份再传递给函数。

def change(lis):
    lis[1] = '小明他大爷'

names = ['小明','小花','小红']
change(names[:])
print(names)

打印结果:

['小明', '小花', '小红']

我们用切片的方法拷贝了一份names,函数中尽管修改了参数,也不过是修改的是副本,不会影响到原始的names。

 

关键字参数

关键字参数让你可以不用考虑函数的参数位置,你需以键值对的形式指定参数的对应形参。

def welcome(name,address):
    print(f"你好 {name} , 欢迎来到 {address} !")

welcome(address='上海',name='小强')

打印结果:

你好 小强 , 欢迎来到 上海 !

 

收集参数

有时候我们需要允许用户提供任意数量的参数,函数的形参可以带个星号来接收,不管调用函数的时候传递了多少实参,都将被收集到形参这个变量当中,形参的类型是元组。

def welcome(*names):
    print(names)

welcome('小明','小强','小红','小黑')

打印结果:

('小明', '小强', '小红', '小黑')

 

还有一种是带两个星号的形参,用于接收键值对形式的实参,导入到函数中的类型是字典。

def welcome(**names):
    print(names)

welcome(name='小明',age=18,sex='')

打印结果:

{'name': '小明', 'age': 18, 'sex': '男'}

 

分配参数

分配参数是收集参数的相反操作,可使得一个元组或字典变量自动分配给函数中的形参。

def welcome(name,address):
    print(f"你好 {name} , 欢迎来到 {address} !")

a = ('小红','山东')
welcome(*a)

打印结果:

你好 小红 , 欢迎来到 山东 !

 

我们改成字典的方式:

def welcome(name,address):
    print(f"你好 {name} , 欢迎来到 {address} !")

a = {'address':'山东','name':'小红'}
welcome(**a)

打印结果:

你好 小红 , 欢迎来到 山东 !

 

函数的返回值

首先说明,所有的函数都是有返回值的,如果编程人员没有指定返回值,那么默认会返回None,对标其他语言中的null

一个简单的函数返回值的例子:

def get_full_name(first_name,last_name):
    return first_name + last_name

r = get_full_name('','大锤')
print(r)

打印结果:

王大锤

 

然而python中的函数还可以返回多个值,返回出的值被装载到元组中:

def func(num):
    return num**2,num**3,num**4

result = func(2)
print(result)

打印结果:

(4, 8, 16)

 

在python中函数定义的时候没有返回值的签名,导致我们无法提前知道函数的返回值是什么类型,返回的什么完全看函数中的return的是什么,特别是逻辑代码比较多的函数,比如里面有多个if判断,有可能这个判断return出来的是布尔值,另一个判断return出来的是列表,还一个判断啥也不return,你调用的时候你都搞不清楚该怎么处理这个函数的返回值,在这一点来说,Java完胜。

 

所以在无规则限制的情况下,代码写的健不健壮,好不好用,主要取决于编程人员的素质。

 

匿名函数

匿名函数就是不用走正常函数定义的流程,可以直接定义一个简单的函数并把函数本身赋值给一个变量,使得这个变量可以像函数一样被调用,在python中可以用lambda关键字来申明定义一个匿名函数。

我们把王大锤的例子改一下:

get_full_name = lambda first_name,last_name : first_name + last_name
r = get_full_name('','大锤')
print(r)

打印结果:

王大锤

 

函数的作用域

访问全局作用域

python每调用一个函数,都会创建一个新命名空间,也就是局部命名空间,函数中的变量就叫做局部变量,与外部的全局命名空间不会相互干扰。

这是常规状态,当然也会有非常规需求的时候,所以python给我们提供了globals()函数,让我们可以在局部作用域中访问到全局的变量。

def func():
    a = globals()
    print(a['name'])

name = '小明'
func()

打印结果:

小明

 

globals()函数只能让我们访问到全局变量,但是是无法进行修改的,如果我们要修改全局变量,需要用到global关键字将全局变量引入进来。

def func():
    global name
    name = '小花'

name = '小明'
func()
print(name)

打印结果:

小花

 

访问嵌套作用域

对于嵌套函数的情况,如果想要使用嵌套作用域(非全局作用域)的变量,就需要用到nonlocal关键字。

def welcome(name):
    def go():
        nonlocal name
        name = '我变成小花了'
    go()
    print(name)

my_name = '小明'
welcome(my_name)
print(my_name)

打印结果:

我变成小花了
小明

 

函数与模块

导入模块

模块的官方定义是包含函数和变量的python文件就叫做模块,然而你照着这个定义去看,你会发现只要是.py的文件,其实都符合模块的定义。

我新建一个user.py文件 他也叫做user模块 里面写两个函数

def get_name():
    print('我的名字是小明')

def get_age():
    print('我今年11岁了')

 

然后我用import导入这个user模块 并调用模块中的函数

import user
user.get_name()

打印结果:

我的名字是小明

 

这个用处相当于公共函数的封装,当然了,我们面向对象的做法一般都是封装到类里面。

 

为了防止模块名字被当前程序的变量覆盖,你可以选择导入的时候用as指定别名:

import user as model_user
model_user.get_name()

打印结果:

我的名字是小明

 

导入函数

还是拿上面user模块来做例子  我现在只导入其中某个函数

from user import get_name
get_name()

打印结果:

我的名字是小明

 

from ... import ... 的意思就是 从...中获取...,从user模块中获取get_name函数。

 

导入模块中所有函数

通配符星号表示导入所有函数,只是这种做法,有大几率出现函数被覆盖的风险

from user import *
get_name()
get_age()

打印结果:

我的名字是小明
我今年11岁了

 

posted @ 2020-07-08 12:56  不该相遇在秋天  阅读(184)  评论(0编辑  收藏