函数基础
函数的基本使用
- 函数的定义
格式如下:
说明:
- 函数定义代码块以def关键词开头,后接函数标识符名称和圆括号()
- 任何传入参数都必须放在圆括号中间
- 函数内容以冒号:开始,并且缩进
- return [表达式]结束函数,选择性地返回一个值给调用方,不带表达式的return相当于返回None
- 函数标识符名称采用下划线表示法,且不能以数字开头
- 注意:因为函数体相对比较独立,因此在函数定义的上方,应该和其他代码保留两个空行
- 函数的调用
- 通过 函数名(参数列表) 即可完成对函数的调用
- 注意:函数调用必须放在函数定义的下方
- 函数的文档注释
- 函数的文档注释位于函数体的最前面,即def的下方
- 使用连续的三对引号,在连续的三对三对引号之间编写对函数的说明文字
- 在函数调用位置,使用 Ctrl + 鼠标位置 可以查看函数的说明信息,也可以使用 Ctrl + 点击鼠标 跳转到函数的定义代码处
参数和返回值传递
变量的引用
- 在Python中,变量和数据分开存储,数据保存在内存中的一个位置,变量中保存着数据在内存中的地址,变量中记录数据的地址就叫做引用
- 在Python中,变量引用自对象,对象有不同类型的区分,而变量是没有类型的,变量可以指向任何类型的对象
- 使用id()可以查看变量的引用数据所在的内存地址,即变量中存储的地址值
- 注意:如果变量已经被定义,当给一个变量赋新值的时候,本质上是修改了变量的引用,即变量不再对之前的数据引用,变量改为对新赋值的数据引用
-
可变和不可变对象
不可变对象:内存中的数据不会被修改,即不会在被调用函数内修改调用函数内的变量值
- 数值类型:int、float、bool、complex
- 字符串:str
- 元组:tuple
- 举例:变量赋值 a = 5 后再赋值 a = 10,实际是新生成一个int值对象10,再让a指向它,而5被丢弃,不是改变a的值,相当于新生成了a
可变对象:内存中的数据会被修改,通过接口改变的是数据所在的位置,变量的地址没变
- 列表:list
- 字典:dict
- 集合:set
- 举例:变量赋值 list = [1, 2, 3, 4]后再赋值 list[2] = 5,此时list的第三个元素的值被修改,而list变量的引用没有改变;但是对 list 变量重新赋值为 list = [4, 3, 2, 1],则list变量的引用改变了,原数据[1, 2, 3, 4]的地址和内容都没有改变
- 因此,无论传递的参数是可变对象还是不可变对象,在函数内部对参数使用赋值语句,只会修改局部变量的引用,不会影响到外部变量的引用。但是,如果传递的参数是可变对象,在函数内部使用方法修改了数据的内容,则会影响到外部的数据
- 注意:在函数内部对可变对象使用 += 运算符不会修改变量的引用,即会影响到外部的数据
函数的参数传递
在Python中,函数的参数传递以及返回值都是靠引用传递的
- 对于不可变对象,类似于C++中的值传递,在函数体内修改参数的值,函数体外的值不会受到影响
- 对于可变对象,类似于C++中的引用传递,在函数体内修改参数的值,函数体外的值也会随之改变
- 注意:在函数内对指向可变对象的变量重新赋值是不会改变可变对象的值,这没有修改可变对象
因此,严格意义上不能说Python的参数传递是值传递还是引用传递,而是应该分传不可变对象和传可变对象两种情况讨论
函数参数的说明
必需参数:必需参数须以正确的顺序传入参数,调用时的参数数量和顺序必需和定义函数时一致
- 关键字参数:关键字参数允许函数调用时参数的顺序与定义函数时不一致,因为Python解释器能够用参数名来匹配参数值,但是参数数量仍需一致
举例:
- 缺省参数
- 定义函数时,可以给某个参数指定一个默认值,具有默认值的参数就叫做缺省参数
- 调用函数时,如果没有传入缺省参数的值,则在函数内部使用定义函数时指定的参数默认值
- 注意事项
- 定义函数时必须保证带有默认值的缺省参数在参数列表末尾
- 如果调用函数时有多个缺省参数,需要指定参数名,这样解释器才能够知道参数的对应关系。如果都不指定也是可以的,指定一个以后,后面的就必须指定
- 定义函数时,缺省参数需要使用最常见的值作为默认值
- 如果一个参数的值不能确定,则不应该设置默认值
举例:
- 不定长参数(多值参数)
- 基本语法
- 说明:加了星号*的不定长参数会以元组的形式导入,存放所有未命名的变量参数。如果在函数调用时没有指定参数,它就是一个空元组
举例
- 另外一种就是参数带两个星号**,此时实际参数会以字典的形式导入
- 注意:和普通参数不一样的是,在函数内对传入的字典以调用方法的形式进行修改,函数外不会生效
举例
- 两者可以一起使用,运用场景是:将一个元组变量直接传递给 args,将一个字典变量直接传递给 kwargs,此时需要使用拆包
- 拆包的方式是,在元组变量前增加一个 ,在字典变量前增加两个
- 传递给一个函数的元组和字典也可以继续传递给别的函数,仍然是使用拆包
- 说明:一般在给多值参数命名时,习惯使用 args 存放元组参数,**kwargs 存放字典参数 ```python def demo1(args, **kwargs): print(args) print(kwargs)
def demo(num1, num2, args, **kwargs): print(num1) print(num2) demo1(args[1:], **kwargs)
if name == ‘main‘: tuple1 = (1, 2, 3, 4, 5) dict1 = {‘name’: ‘小明’, ‘age’: 18, ‘gender’: True} demo(123, ‘abc’, tuple1, *dict1)
“”” 输出: 123 abc (2, 3, 4, 5) {‘name’: ‘小明’, ‘age’: 18, ‘gender’: True} “””
<a name="sPEfA"></a>
### 使用模块中的函数
<a name="ZwaoI"></a>
#### 基本说明
- 模块就好比是工具包,要想使用这个工具包中的工具,就需要import 这个模块
- 每一个以扩展名py结尾的Python源代码文件都是一个模块
- 导入模块后,在模块外可以直接使用模块中定义的全局变量、函数,以模块名.变量 / 模块名.函数的方式
- 注意:以数字开头的模块是无法在Pycharm中导入的
<a name="dSDjU"></a>
#### 关于if __name__ == '__main__':
- 每个python模块(文件)都包含内置的变量__name__,当该模块被直接执行的时候,它的__name__等于'__main__';而当该模块被import到其他模块中,则它的__name__等于该模块的文件名(不包含后缀.py)
- 因此,当该模块被直接执行的时候,if __name__ == '__main__': 语句中的代码将被执行;而当该模块被import到其他模块中,if __name__ == '__main__': 语句中的代码不会被执行
- 因此,在被导入模块中,将测试代码写在 if __name__ == '__main__': 语句的内部,防止在导入模块中被执行
<a name="kO9Mr"></a>
### 全局变量和局部变量
<a name="g0wNf"></a>
#### 局部变量
- 局部变量是在函数内部定义的变量,只能在函数内部使用
- 函数执行结束后,函数内部的局部变量会被系统回收
- 不同的函数,可以定义相同名字的局部变量,彼此之间不会产生影响
<a name="Vftk8"></a>
#### 全局变量
- 全局变量是在函数外部定义的变量,所有函数内部都可以使用这个变量
- 在函数内部修改全局变量的值和参数传递的规律是一样的,对于不可变对象是修改不了的,对于可变对象可以通过调用接口进行修改
- 为了保证所有的函数都能够正确使用到全局变量,应该将全局变量定义在所有函数的上方
- 函数执行时,需要处理变量时会:
1. 首先在函数内部查找是否存在指定名称的局部变量,如果有,直接使用
1. 如果没有,在函数外部查找是否存在指定名称的全局变量,如果有,直接使用
1. 如果还没有,程序报错
- 为了避免局部变量和全局变量造成混淆,在定义全局变量时,可以考虑在全局变量名前增加 g_ 或者 gl_ 的前缀
<a name="rN7yS"></a>
#### global的使用
- 如果在函数内部需要修改全局变量,可以使用global进行声明,此时在函数内部对任何全局变量进行的一切修改,在函数外部都将生效
```python
num = 10
list = [1, 2, 3]
def demo():
global num
global list
num = 5
list = [2, 3, 4]
demo()
print(num)
print(list)
"""
输出:
5
[2, 3, 4]
"""