深入讲解Python函数中参数的使用及默认参数的陷阱

C++里函数可以设置缺省参数,Java不可以,只能通过重载的方式来实现,python里也可以设置默认参数,最大的好处就是降低函数难度,函数的定义只有一个,并且python是动态语言,在同一名称空间里不能有想多名称的函数,如果出现了,那么后出现的会覆盖前面的函数。

def power(x, n=2):
  s = 1
  while n > 0:
    n = n - 1
    s = s * x
  return s

看看结果:

>>> power(5)
25
>>> power(5,3)
125

注意: 必选参数在前,默认参数在后,否则Python的解释器会报错。
建议:*当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
默认参数也有坑,看看下面的代码,先定义一个list,添加一个end再返回:

def add_end(L=[]):
  L.append('END')
  return L

看看调用结果:

>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

这里需要解释一下,Python函数在定义的时候,默认参数L的值就被计算出来了,即[]。此时L指向[]。所以如果L中的内容改变了,下次调用引用的内容也就不再是[]了。所以要牢记一点定义默认参数必须指向不可变对象!。

可变参数
第一种方法,传入的参数为一个list或者tuple。

def calc(numbers):
  sum = 0
  for n in numbers:
    sum = sum + n * n
  return sum

调用方式:

>>> calc([1, 2, 3])
14
>>> calc((1, 3, 5, 7))
84

第二种方式,直接传入多个参数,函数内部会自动用一个tuple接收。

def calc(*numbers):
  sum = 0
  for n in numbers:
    sum = sum + n * n
  return sum

调用方式:

>>> calc(1, 2)
5
>>> calc()
0

这个时候如果还想把一个list或者tuple里的数据传进去,可以这样:

>>> nums = [1, 2, 3]
>>> calc(*nums)
14

关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

def person(name, age, **kw):
  print 'name:', name, 'age:', age, 'other:', kw

调用示例:

>>> person('Michael', 30)
name: Michael age: 30 other: {}
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数。
递归函数
基本的也没什么可讲的,和Java/C++里一样,就是调用本身的一种。这里重点介绍一下尾递归优化。事实上尾递归和循环效果是一样的,很显然的一个优点那就是可以防止递归调用栈溢出。
定义:在函数返回的时候调用自身,并且,return语句不能包含表达式。编译器或者解释器可以对其做优化,无论调用多少次,只占用一个栈帧,不会出现溢出的情况。
举个简单的例子,以阶乘函数为例:

def fact(n):
  if n==1:
    return 1
  return n * fact(n - 1)

如果传入的n很大,就可能会溢出,这是由于return n * fact(n - 1)引入了乘法表达式,就不是尾递归了。把代码改一下:

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)

默认参数陷阱
Python的函数定义提供了默认参数这个选择,使得函数的定义和使用更加的灵活,但是也会带来一些坑,例如之前的一个例子:
函数定义:

def add_end(L=[]):
  L.append('END')
  return L

调用函数的结果:

>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

很明显这个与函数的定义初衷不符,用一句话解释就是:
Default values are computed once, then re-used.
为了深入研究这个问题,我们来看看另一个例子:

# coding=utf-8

def a():
  print "a executed"
  return []

def b(x=a()):
  print "id(x):", id(x)
  x.append(5)
  print "x:", x

for i in range(2):
  print "不带参数调用,使用默认参数"
  b()
  print b.__defaults__
  print "id(b.__defaults__[0]):", id(b.__defaults__[0])

for i in range(2):
  print "带参数调用,传入一个list"
  b(list())
  print b.__defaults__
  print "id(b.__defaults__[0]):", id(b.__defaults__[0])

NOTE:稍微解释一下,所有默认值都存储在函数对象的__defaults__属性中,这是一个列表,每一个元素均为一个默认参数值。
来看看输出结果:

a executed
不带参数调用,使用默认参数
id(x): 140038854650552
x: [5]
([5],)
id(b.__defaults__[0]): 140038854650552
不带参数调用,使用默认参数
id(x): 140038854650552
x: [5, 5]
([5, 5],)
id(b.__defaults__[0]): 140038854650552
带参数调用,传入一个list
id(x): 140038854732400
x: [5]
([5, 5],)
id(b.__defaults__[0]): 140038854650552
带参数调用,传入一个list
id(x): 140038854732472
x: [5]
([5, 5],)
id(b.__defaults__[0]): 140038854650552

简单分析一下输出结果:
第1行
在定义函数b(),即执行def语句,代码第7行def b(x=a()):的时候,这句话使用了默认参数,所以在定义的时候会计算默认参数x的值,这个时候会调用a(),所以打印出了a executed。
第2~6行
第一次执行循环,代码第14行调用b()没有传递参数,使用默认参数,此时x=[],所以调用一次之后

print b.__defaults__

输出结果为

复制代码 代码如下:

([5],)

第7~11行
第二次循环,代码第14行调用b()没有传递参数,使用默认参数。
注意:默认参数只会计算一次,也就是说那个内存区域就固定了,但是这个地址所指向的是一个list,内容可以改变,此时由于上一次调用x: [5],所以

print b.__defaults__

输出结果为

([5, 5],)

第12~16行
第二个循环语句,第一次循环,代码第20行传入一个空的list,所以不使用默认参数,此时x=[],所以

print b.__defaults__

输出结果为

复制代码 代码如下:

([5],)

第18~21行
第二个循环语句,第二次循环,代码第20行传入一个空的list,所以也不使用默认参数,此时仍然是x=[],所以

print b.__defaults__

输出结果依然为

复制代码 代码如下:

([5],)

函数也是对象,因此定义的时候就被执行,默认参数是函数的属性,它的值可能会随着函数被调用而改变。其他对象不都是如此吗?
牢记: 默认参数必须指向不变对象!代码改一下如下:

# coding=utf-8

def a():
  print "a executed"
  return None

def b(x=a()):
  print "id(x):", id(x)
  if x is None:
    x = []
  x.append(5)
  print "x:", x

for i in range(2):
  print "不带参数调用,使用默认参数"
  b()
  print b.__defaults__
  print "id(b.__defaults__[0]):", id(b.__defaults__[0])

for i in range(2):
  print "带参数调用,传入一个list"
  b(list())
  print b.__defaults__
  print "id(b.__defaults__[0]):", id(b.__defaults__[0])

此时的输出结果看看是什么:

a executed
不带参数调用,使用默认参数
id(x): 9568656
x: [5]
(None,)
id(b.__defaults__[0]): 9568656
不带参数调用,使用默认参数
id(x): 9568656
x: [5]
(None,)
id(b.__defaults__[0]): 9568656
带参数调用,传入一个list
id(x): 140725126699632
x: [5]
(None,)
id(b.__defaults__[0]): 9568656
带参数调用,传入一个list
id(x): 140725126699704
x: [5]
(None,)
id(b.__defaults__[0]): 9568656
时间: 2016-03-10

python下函数参数的传递(参数带星号的说明)

函数参数的使用又有俩个方面值得注意:1.函数参数是如何定义的 2.在调用函数的过程中参数是如何被解析 先看第一个问题,在python中函数参数的定义主要有四种方式: 1.F(arg1,arg2,...) 这 是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,用这种方式定义的函数在调用的的时候也必须在函数名后的小括号里提供个数相等的 值(实际参数),而且顺序必须相同,也就是说在这种调用方式中,形参和实参的个数必须一致,而且必须一一对应,也就是说第一个形参对应这第一个实参.例 如

Python def函数的定义、使用及参数传递实现代码

Python编程中对于某些需要重复调用的程序,可以使用函数进行定义,基本形式为: def 函数名(参数1, 参数2, --, 参数N): 执行语句函数名为调用的表示名,参数则是传入的参数,可以更具需要定义,也可以没有. # 例1:简单的函数使用 # coding=gb2312 # 定义函数 def hello(): print 'hello python!' # 调用函数 hello() >>> hello python! 函数可以带参数和返回值,参数将按从左到右的匹配,参数可设置默认值

python 装饰器功能以及函数参数使用介绍

简单的说:装饰器主要作用就是对函数进行一些修饰,它的出现是在引入类方法和静态方法的时候为了定义静态方法出现的.例如为了把foo()函数声明成一个静态函数 复制代码 代码如下: class Myclass(object): def staticfoo(): ............ ............ staticfoo = staticmethod(staticfoo) 可以用装饰器的方法实现: 复制代码 代码如下: class Myclass(object): @staticmethod

Python函数中定义参数的四种方式

Python中函数参数的定义主要有四种方式: 1. F(arg1,arg2,-) 这是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,用这种方式定义的函数在调用的的时候也必须在函数名后的小括号里提供个数相等 的值(实际参数),而且顺序必须相同,也就是说在这种调用方式中,形参和实参的个数必须一致,而且必须一一对应,也就是说第一个形参对应这第一个实参.例如: 复制代码 代码如下: def a(x,y):print x,y 调用该函数,a(1,2)则x取1,y取2,形参与实参相对应

Python函数可变参数定义及其参数传递方式实例详解

本文实例讲述了Python函数可变参数定义及其参数传递方式.分享给大家供大家参考.具体分析如下: python中 函数不定参数的定义形式如下: 1.func(*args) 传入的参数为以元组形式存在args中,如: def func(*args): print args >>> func(1,2,3) (1, 2, 3) >>> func(*[1,2,3]) #这个方式可以直接将一个列表的所有元素当作不定参数 传入(1, 2, 3) 2.func( **kwargs)

python进阶教程之函数参数的多种传递方法

我们已经接触过函数(function)的参数(arguments)传递.当时我们根据位置,传递对应的参数.我们将接触更多的参数传递方式. 回忆一下位置传递: 复制代码 代码如下: def f(a,b,c):     return a+b+c print(f(1,2,3)) 在调用f时,1,2,3根据位置分别传递给了a,b,c. 关键字传递 有些情况下,用位置传递会感觉比较死板.关键字(keyword)传递是根据每个参数的名字传递参数.关键字并不用遵守位置的对应关系.依然沿用上面f的定义,更改调用

Python进阶-函数默认参数(详解)

一.默认参数 python为了简化函数的调用,提供了默认参数机制: def pow(x, n = 2): r = 1 while n > 0: r *= x n -= 1 return r 这样在调用pow函数时,就可以省略最后一个参数不写: print(pow(5)) # output: 25 在定义有默认参数的函数时,需要注意以下: 必选参数必须在前面,默认参数在后: 设置何种参数为默认参数?一般来说,将参数值变化小的设置为默认参数. python标准库实践 python内建函数: prin

详细介绍Python函数中的默认参数

import datetime as dt def log_time(message, time=None): if time is None: time=dt.datetime.now() print("{0}: {1}".format(time.isoformat(), message)) 最近我在一段Python代码中发现了一个因为错误的使用默认参数而产生的非常恶心的bug.如果您已经知道关于默认参数的全部内容了,只是想嘲笑一下我这可笑的错误,请直接跳到本文末尾.哎,这段代码是我

Python中函数及默认参数的定义与调用操作实例分析

本文实例讲述了Python中函数及默认参数的定义与调用操作.分享给大家供大家参考,具体如下: #coding=utf8 ''''' Python中的函数使用小括号调用.函数在调用之前必须先定义. 如果函数中没有return语句,就会自动返回None对象. Python是通过引用调用的.如果函数内对参数的改变会影响到原始对象. 只有可变对象会受此影响,对不可变对象,它的行为类似按值调用. ''' ''''' 定义函数: def function_name([arguments]): "option

Python自定义函数的创建、调用和函数的参数详解

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可以自己创见函数,这被叫做用户自定义函数.一.定义一个函数你可以定义一个由自己想要功能的函数,以下是简单的规则: 1.函数代码块以def关键词开头,后接函数标识符名称和圆括号().2.任何传入参数和自变量必须放在圆括号中间.圆括号之间可以用于定义参数.3.函数的第一行语句可以选择性地使用文档字符串-用于存放函数说明.4.

探讨PHP调用时间格式的参数详解

1.调用语法使用函式date() 实现获取当前的时间格式,例如: 2.时间格式参数设定上面语法中"Y-m-d H:i:s",显示的格式为: 年-月-日 小时:分钟:秒这样看来,我们只要获取对应的相关时间参数就可以替换出自己想要的时间格式了! a – "am" 或是 "pm"    A – "AM" 或是 "PM"    d – 几日,二位数字,若不足二位则前面补零; 如: "01″ 至 "

python通过getopt模块如何获取执行的命令参数详解

前言 python脚本和shell脚本一样可以获取命令行的参数,根据不同的参数,执行不同的逻辑处理. 通常我们可以通过getopt模块获得不同的执行命令和参数.下面话不多说了,来一起看看详细的介绍吧. 方法如下: 下面我通过新建一个test.py的脚本解释下这个模块的的使用 #!/usr/bin/python # -*- coding: utf-8 -*- import sys import getopt if __name__=='__main__': print sys.argv opts,

Python函数参数匹配模型通用规则keyword-only参数详解

Python3对函数参数的排序规则更加通用化了,即Python3 keyword-only参数,该参数即为必须只按照关键字传递而不会有一个位置参数来填充的参数.该规则在处理人一多个参数是很有用的. keyword-only kword_only(1, 2, 3, c=4) print('-' * 20) kword_only(a=1, c=3) 示例结果: 1 (2, 3) 4 -------------------- 1 () 3 在 *args 之后的参数都需要在调用中使用关键字的方式传递,

js匿名函数作为函数参数详解

由衷的感叹,js真是烦. 学到现在,渐渐理解了什么是:语言都是通用的,没有好不好,只有擅长不擅长. 继承,多态,甚至指针,c能实现,c++,java有,javascript(和java是雷锋和雷峰塔的区别,名字上不知道坑了多少人)也能变通实现. 温故知新,今天又回味了一遍,匿名函数作为函数参数. 代码很短,五脏俱全. <!DOCTYPE html> <html lang="en"> <head> </head> <body>

javascript如何使用函数random来实现课堂随机点名方法详解

如何使用函数random来实现课堂随机点名 1.最初的样子如下 2.点击开始点名,上面一行的文字变成名字,名字在不停的变化,开始点名变成停止点名,如下 3.点击停止点名,上面名字不动,停止点名变成开始点名,如下:李四同学回答老师问题 代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style> #d2{

对python调用RPC接口的实例详解

要调用RPC接口,python提供了一个框架grpc,这是google开源的 rpc相关文档: https://grpc.io/docs/tutorials/basic/python.html 需要安装的python包如下: 1.grpc安装 pip install grpcio 2.grpc的python protobuf相关的编译工具 pip install grpcio-tools 3.protobuf相关python依赖库 pip install protobuf 4.一些常见原型的生成

基于Python中单例模式的几种实现方式及优化详解

单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. 比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息.如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪

Python中的None与 NULL(即空字符)的区别详解

1.首先要了解Python的对象的概念: Python中,万物皆对象,所有的操作都是针对对象的,那什么是对象,5是一个int对象,'oblong'是一个str对象,异常也是一个对象,抽象一点是,人,猫,够也是一个对象 那对于一个对象,它就有包括两方面的特征:  属性:去描述它的特征  方法: 它所具有的行为 所以,对象=属性+方法 (其实方法也是一种属性,一种区别于数据属性的可调用属性 把具有相同属性和方法的对象就可以归为一类,即Classl.类就好比是一张蓝图,使用一个类可以创建多个对象实例