函数基本使用
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岁了