前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 基础系列--函数

Python 基础系列--函数

作者头像
somenzz
发布2020-12-10 11:26:01
4900
发布2020-12-10 11:26:01
举报
文章被收录于专栏:Python七号Python七号

在中学数学中我们知道 y=f(x) 代表着函数,x 是自变量,y 是函数 f(x) 的值,给定 x 可以计算出对应的 y。在程序设计中,函数的功能是一样的,给定输入,返回对应的输结果,变量 x 不在限制为数字,可以为任意的数据类型,比如字符串,列表,字典,对象,或者自定义的对象等,同样地返回值也可以任意的数据类型。函数的作用是对加工细节的一种封装,对外提供统一的接口,使用者无需关心函数对内的细节,是最基本的一种代码抽象方式。

函数不仅减少代码行数,而且能节省内存,提高程序运行速度:当一个函数调用完毕时,退出程序堆栈,内存空间被回收,当新的函数被调用时,局部变量又可以重新使用相同的地址。当一块数据被反复读写,其数据会留在 CPU 的一级缓存中,访问速度非常快,从而加快程序执行速度。

下面来说一说 Python 中的函数。

定义一个函数

Python 定义函数的规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

使用 def 关键字,一般格式如下:

代码语言:javascript
复制
def 函数名(参数列表):
    函数体

以简单的数据计算函数为例,定义函数 fun(a,b,h) 来计算上底为 a,下底为 b,高为 h 的梯形的面积:

代码语言:javascript
复制
>>> def fun(a,b,h):      #def 定义函数fun,参数为a,b,h
...     s=(a+b)*h/2     #使用梯形的面积计算公式,注意此行前有4个空格
...     return s         #返回面积,注意此行前有4个空格
...
>>> fun(3,4,5)         #计算上底为3,下底为4,高为5的梯形的面积
17.5

将常用的处理过程封装成函数,在需要的时候调用它,可以屏蔽实现细节,减少代码数量,增强程序可读性。假如有许多个梯形的面积需要计算,实例如下:

代码语言:javascript
复制
>>> for a,b,h in [(3,4,5),(7,5,9),(12,45,20),(12,14,8),(12,5,8)]:  #计算5个梯形面积
...     print("上底{},下底{},高{}的梯形,面积为{}".format(a,b,h,fun(a,b,h))) #字符串格式化函数format
...
上底3,下底4,高5的梯形,面积为17.5
上底7,下底5,高9的梯形,面积为54.0
上底12,下底45,高20的梯形,面积为570.0
上底12,下底14,高8的梯形,面积为104.0
上底12,下底5,高8的梯形,面积为68.0

普通函数

上例中的调用方法fun(3,4,5)并不直观,为了增加可读性,我们稍做调整,并增加函数的文档说明,如下:

代码语言:javascript
复制
>>> def trapezoidal_area(upperLength,bottom,height):
...     """函数说明:输入:长、宽、高
... 返回该梯形的面积"""
...     return (upperLength+bottom)*height/2
...
>>> trapezoidal_area(3,4,5)   # 按定义的顺序对应 upperLength=3,bottom=4,height=5
17.5
>>> trapezoidal_area(upperLength=3,bottom=4,height=5)  #显式的指定参数的值,位置可以变化
17.5
>>> trapezoidal_area(bottom=4,height=5,upperLength=3) #显式的指定参数的值,位置可以变化
17.5
>>>

可以使用 help 函数查看该函数的文档说明:

代码语言:javascript
复制
>>> help(trapezoidal_area)
Help on function trapezoidal_area in module __main__:

trapezoidal_area(upperLength, bottom, height)
    函数说明:输入:长、宽、高
    返回该梯形的面积

参数带默认值的函数

在调用此函数传递参数的时候使用参数关键字,这样参数的位置可以任意放置而不影响运算结果,增加程序可读性。假如待计算的梯形默认高度都为 5,可以定义带默认值参数的函数

代码语言:javascript
复制
>>> def trapezoidal_area(upperLength,bottom,height=5):#定义默认值参数
...     return (upperLength+bottom)*height/2
...
>>> trapezoidal_area(upperLength=3,bottom=4)
17.5
>>> trapezoidal_area(3,4)
17.5
>>> trapezoidal_area(3,4,5)
17.5
>>> trapezoidal_area(3,4,10)
35.0

注意:带有默认值的参数必须位于不含默认值参数的后面

参数个数不固定的函数

你可能需要一个函数能处理比当初声明时更多的参数,此时你可以定义不定长参数,语法如下:

代码语言:javascript
复制
def 函数名([固定参数列表,] *不固定参数名 ):
   "函数_文档字符串"
   函数体
   return [expression]

加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。 举个例子:

代码语言:javascript
复制
#!/usr/bin/python3

# 可写函数说明
def printinfo( arg1, *vartuple ):
   "打印任何传入的参数"
   print ("输出: ")
   print (arg1)
   for var in vartuple:
      print (var)

# 调用printinfo 函数
printinfo( 10 ) #不向函数传递未命名的变量
printinfo( 70, 60, 50 ) #向函数传递未命名的变量

输出结果为:

代码语言:javascript
复制
输出: 
10
输出: 
70
60
50

还有一种就是参数带两个星号 **的参数会以字典的形式传入:

代码语言:javascript
复制
#!/usr/bin/python3

# 可写函数说明
def printinfo( arg1, **vardict ):
   "打印任何传入的参数"
   print ("输出: ")
   print (arg1)
   print (vardict)

# 调用printinfo 函数
printinfo(1, a=2,b=3)

输出结果为:

代码语言:javascript
复制
输出: 
1
{'a': 2, 'b': 3}

声明函数时,参数中星号 * 可以单独出现,例如:

代码语言:javascript
复制
def f(a,b,*,c):
    return a+b+c

如果单独出现星号 * 后的参数必须用关键字传入。

代码语言:javascript
复制
>>> def f(a,b,*,c):
...     return a+b+c
... 
>>> f(1,2,3)   # 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given
>>> f(1,2,c=3) # 正常
6
>>>

是值传递还是引用传递?

关于函数是否会改变传入变量的值分两种情况: (1)对不可变数据类型的参数,函数无法改变其值,如字符串,数字,元组等。 (2)对可变数据类型的参数,函数可以改变其值,如列表,字典,集合等。 这里什么是可变数据类型,什么是不可变数据类型,请参考上一篇文章 Python 的可变/不可变数据类型

请尝试说出下面程序的输出结果:

代码语言:javascript
复制
# !/usr/local/bin/python3
# -*- coding: utf-8 -*-
# Time: 2018/10/6 7:36:38
# Description:
# File Name: lx_fun_params.py

def change_nothing(var):
    var = "new value"

def try_change(var):
    if type(var) is list:
        var.append("new value")
    elif type(var) is str:
        var = var + " new value"
    else:
        pass

def try_change1(var):
    var = var+"a"

str1 = "old value"
list1 = ["old value"]

change_nothing(str1)
change_nothing(list1)
print("after call change_nothing:")
print(str1)
print(list1)

#恢复原值
str1 = "old value" 
list1 = ["old value"] 

try_change(str1) 
try_change(list1)

print("after call try_change:")
print(str1)
print(list1)

按照 C/C++ 的思维会产生函数参数是值传递,还是引用传递。有些同学可会潜移默化的认为列表是属于引用传递, change_nothing 调用之后 str1 未被改变,list1 变成字符串 “new value", try_change 调用之后 str1 未被改变,list1 会新加入元素 “new value"。 真正的结果是:

image.png

Python 函数参数的传递既不是所谓的传值也不是传引用。如果你理解发什么是可变数据类型 ,什么是不可变数据类型,这就很好理解。请牢记,在 Python 世界里,万物皆对象,变量是对象的引用,代表着对象在内存中的地址。Python 中函数参数传递的是变量的值,即就是变量所指向的对象的地址。 对上例中的字符串 str1 ,如下图所示:在调用 change_nothing 传入参数时前,str1 与 var 均指向 "old value" 的地址,调用 change_nothing 后,var 指向了新的对象 "new value",因此 str1 未发生任何变化,对字符串 str1 调用 try_change 的本质与 change_nothing 是一样的,同样都是赋值操作,因此 str1 均不发生变化。

image.png list1 也是同样的道理,因此在调用 change_nothing 之后,list1 的值仍然是 ["old value"]

但是在调用 try_change 函数时,发生了变化。如下图所示

image.png 开始传参时 list1 和 var 均指向 ["old value"],由于列表是可变数据类型,增加、删除、修改元素时不产生新的对象,对象在内存中的地址不发生变化,var 仍指向原来的 list1 的地址,因此在调用 try_change 函数后,list1 被改变。

涉及到的其他小知识:

(1)isinstance 和 type 的用法: python 判断一个变量属于什么对象可以使用 isinstance 和 type,二者的区别在于判断有继承关系的类时 isinstance 认为子类是父类,type 则认为子类不是父类,如下所示:

代码语言:javascript
复制
class A:
    pass

class B(A): # B 是 A 的子类
    pass

isinstance(A(), A)  # returns True
type(A()) == A      # returns True
isinstance(B(), A)    # returns True
type(B()) == A        # returns False

(2)匿名函数: python 使用 lambda 来创建匿名函数。 所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。 语法 lambda 函数的语法只包含一个语句,如下:

代码语言:javascript
复制
lambda [arg1 [,arg2,.....argn]]:expression

例子:

代码语言:javascript
复制
#!/usr/bin/python3

# 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2

# 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 ))
print ("相加后的值为 : ", sum( 20, 20 ))

以上实例输出结果:

代码语言:javascript
复制
相加后的值为 :  30
相加后的值为 :  40

(完)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义一个函数
  • 普通函数
  • 参数带默认值的函数
  • 参数个数不固定的函数
  • 是值传递还是引用传递?
  • 涉及到的其他小知识:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档