07 python基础六--函数

1 调用函数

所谓调用函数,即指调用python提供的内置函数,需要知道的是,要调用一个函数,必须知道它的名称和参数
Python内置函数列表:https://docs.python.org/3/library/functions.html

#当然除了查看文档外,python还支持help()查看函数信息的方式
#eg
>>>help(abs)
Help on built-in function abs in module __builtin__:

abs(...)
    abs(number) -> number
    
    Return the absolute value of the argument. #返回一个数字的绝对值

>>> abs(100)
100
>>> abs(-100)
100

# 最大值
>>> max(1,2)
2
>>> max(1,2,3,4,5)
5

# 调用内置函数进行数据类型的转换
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(123)
'123'
>>> bool(1)
True
>>> bool('')
False
>>> bool(0)
False

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

>>> a = abs
>>> a(-123)
123

2 定义函数

  • 当内置函数无法满足我们的需求时,可以自定义一个函数
  • 在Python中,定义一个函数使用def语句,依次写出函数名、括号、参数和冒号,然后,在缩进块中编写函数体,函数的返回值用return语句返回
>>> def ma_abs(x):
...     if not instance(x, (int, float)):
...         raise TypeError('bad operand type')
...     if x >= 0:
...         return x+x
...     else:
...         return x-x
... 
>>> ma_abs(-2)
0
>>> ma_abs(2)
4
  • 空函数,什么事也不做的空函数可以用pass语句
  • 实际上,pass一般用来作为占位符,比如现在没想好怎么写函数的代码,可以放一个pass让代码先运行起来
>>> def nop():
...     pass
... 
>>> nop()
>>> 

  • Python函数可以返回多个值?
  • 举个栗子🌰,比如在游戏中经常需要从一个点移动到另一个点,给出坐标、位移和角度,就可以计算出新的新的坐标:
>>> import math #导入math包
>>> def move(x, y, step, angle = 0):
...     nx = x + step * math.cos(angle)
...     ny = y - step * math.sin(angle)
...     return nx, ny
... 
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
(151.96152422706632, 70.0)  # 通过结果可见:Python函数返回的其实仍然是单一值,是一个tuple,语法上,tuple可以省略括号。多个变量按位置赋值tuple的对应值

3 函数的参数

Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数。使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。

  • 位置参数、默认参数
>>> def power(x):
...     return x * x
... 
>>> power(2)
4
>>> power(6)
36

# x,n 即为函数的位置参数,传入的两个值按照位置顺序依次赋给参数x和n,n可以赋予默认值,称为默认参数
>>> def powerPlus(x, n = 2):
...     s = 1
...     while n > 0:
...         n = n - 1
...         s = s * x
...     return s
... 
>>> powerPlus(2, 3)
8
>>> powerPlus(4, 4)
256
  • 可变参数
    举个栗子🌰:以数学题为例子,给定一组数字a,b,c……,请计算a2 + b2 + c2 + ……
>>> def calc(numbers):
...     sum = 0
...     for n in numbers:
...         sum = sum + n * n
...     return sum
... 
# 普通的参数只能接受一个值,要想实现多值,需要先组装出一个list或tuple
>>> calc([1,2,3])
14
>>> calc((1,2,3,4))
30

# 可变函数出场!!!
>>> def calc(*numbers):
...     sum = 0
...     for n in numbers:
...         sum = sum + n * n
...     return sum
... 
>>> calc(1,2,3,4)
30
>>> calc()
0

        定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数

  • 关键字参数

        可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:

>>> def person(name, age, **kw):
...     print('name:', name, 'age:', age, 'other:', kw) 
... 
# 调用该函数外,可以只传入必选参数
>>> person('Bob', 30)
('name:', 'Bob', 'age:', 30, 'other:', {})
# 也可以传入任意个数的关键字参数
>>> person('Ami', 18, city = 'beijing')
('name:', 'Ami', 'age:', 18, 'other:', {'city': 'beijing'})
>>> person('Hua', 20, gender = 'M', Phone = 188011102222)
('name:', 'Hua', 'age:', 20, 'other:', {'gender': 'M', 'Phone': 188011102222})
# 简化调用方式
>>> extra = {'city':'beijing', 'job':'Teacher', 'Phone':13366667777}
>>> person('Lw', 26, **extra)
('name:', 'Lw', 'age:', 26, 'other:', {'city': 'beijing', 'job': 'Teacher', 'Phone': 13366667777})
  • 命名关键字参数
    对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。
    如果要限制关键字参数的名字,就可以用命名关键字参数,例如:只接收city和job作为关键字参数
>>> def person(name, age, **kw):
...     if 'city' in kw:
...         pass
...     if 'job' in kw:
...         pass
...     print('name:', name, 'age:', age, 'other:', kw)
... 
# 调用者传入参数个数不受限制
>>> person('Jack', 24, city='beijing', addr='chaoyang', job='coder')
name: Jack age: 24 other: {'city': 'beijing', 'addr': 'chaoyang', 'job': 'coder'}
# 采用**命名关键字参数**来限制参数个数,倘若多传参数进去则会报错
>>> def person(name, age, *, city, job):
...     print(name, age, city, job)

>>> person('Jack', 22, city='beijing', job='coder')
Jack 22 beijing coder

# 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
>>> def person(name, age, *args, city, job):
...     print(name, age, args, city, job)
... 
>>> 
>>> person('Bob', 23, 1, 2, city = 'taiyuan', job = 'coder' )
Bob 23 (1, 2) taiyuan coder

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

4 递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用本身,这个函数就是递归函数。
举个栗子🌰:
计算阶层 n! = 1 * 2 * 3 * 4 * 5 ... * n,用函数fact(n)表示:
fact(n) = n! = 1* 2 * 3 * ... * (n-1) * n = fact(n-1) * n
所以,fact(n) 可以表示为 n * fact(n-1),只有n = 1时需要特殊处理

>>> def fact(n):
...     if n == 1:
...         return 1
...     return n * fact(n - 1)
... 
>>> fact(1)
1
>>> fact(5)
120

# 溢出
>>> faxt(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'faxt' is not defined

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出,在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,就会减一层栈帧。由于栈的大小不是无限的,所以递归调用的次数过多,会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化

尾递归优化是指:在函数返回的时候,调用函数本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身不论调用多少次,都只占用一个栈帧,不会出现溢出的情况。

>>> def fact(n):
...     return fact_iter(n, 1)
... 
>>> def fact_iter(num, product):
...     if num == 1:
...         return product
...     return fact_iter(num - 1, num * product)
... 
>>> fact(2)
2
fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • 函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:a = ab...
    bjchenli阅读 620评论 0 0
  • 《别,哭》 秋风带上落叶悄然离开, 不曾回首, 留给自然的唯一片孤独。 冬日在幕后徘徊, 它想代替秋风, 用雪花作...
    爱诗爱你阅读 306评论 0 0