文档章节

python 中的函式定义

lemos
 lemos
发布于 2017/08/31 15:59
字数 1590
阅读 14
收藏 0

示例:定义一个斐波拉契数列

>>> def fib(n):    # 打印 Fibonacci 序列到 n
...     """打印到 n 的 Fibonacci 序列."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> # 现在调用我们刚定义的函式:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

 

几个概念:

文档字串(docstring),用于文档注释。

符号表(symbol table),函数中的符号表用于该函式的局部变量,因此称作局部符号表。在函式中被赋值的变量会存储在局部符号表中。变量引用会先在局部符号表中寻找,然后才是闭包函式的局部符号表,再然后是全局变量,最后是内建名字表。在函式中的全局变量尽管可以引用,但是不能赋值。函数内赋值只会创建一个新的同名的局部变量。

函式的实参,在它被调用时被引入到这个函式的局部变量表。并且参数是按值传递( 总是对象的一个 引用 , 而不是对象本身的值)。当一个函式调用另一个时, 对应这次调用,一个新的局部符号表就会被创建.

函式定义会在当前的符号表里引入该函式的名字. 函式名对应的值被解释器认定为自定义函式类型 函式名的值可以被赋予另一个名字, 使其也能作为函式使用. 

若函数没有返回值,默认返回 None(内建名字)。如果要唯一输出的值是 None, 那么解释器会正当的抑制这次返回. 如你实在想看看这个值,可以使用 print():

>>> fib(0)
>>> print(fib(0))
None

* 关于符号表

import dis

c = 3

def abc():
    a = 1
    b = a + c

dis.dis(abc)

输出

 19           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (a)

 20           6 LOAD_FAST                0 (a)
              9 LOAD_GLOBAL              0 (c)
             12 BINARY_ADD
             13 STORE_FAST               1 (b)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

 

默认参数

使用默认参数,可以方便进行调用

def meet(you='lisi', him='wangwu'):
    print(you,'meet',him)

meet()
meet('wo')
meet('wo','zhangsan')

重要警告: 默认参数的值只会被求一次值. 但这在默认参数是可变参数的情况下就不一样了, 如列表, 字典, 或大多类的对象时. 例如, 下面的函式在随后的调用中会累积参数值:

通常情况

i = 5

def f(arg=i):
    print(arg)

i = 6
f()

# output:
5

特殊情况

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

# output:
[1]
[1, 2]
[1, 2, 3]

如果不希望参数值被后续调用共享:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

* 为什么默认参数会变?

默认参数和函数对象本身是一一对应的,一个函数拥有一个默认参数的 tuple。这也很好理解,默认参数随函数一起定义,并且出现在函数签名里,理所应当是函数的一部分。

问题的点在于 Python 中对象传递全部为“引用”,list 对象又都是 mutable 的,所以函数调用时往 list 对象作为的默认参数中 append 会造成额外副作用。

事实上,Python 开发是倡导用 immutable 对象作为默认参数的。比如用 None 就是个不错的选择:

def foo(bar=None):
    bar = bar or []

又或者用了 mutable 参数一定记得创建副本:

def foo(bar=[]):
    bar = list(bar)

 

关键字参数

函数在定义或者调用时,可以指定参数名称

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")

可以使用下列方法调用(在函式调用时, 关键字参数必须跟在位置参数之后. )

parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')

当最后一个形参的形式为 **name 时, 则排除其他的形参的值, 它将以字典 (参阅 映射类型——字典) 的形式包含所有剩余关键字参数. 这种调用可以与具有 *name 形式的形式参数 (在下一小节中介绍) 联合使用, 这种形参接受所有超出函式接受范围的位置参数. ( *name 必须在 **name 之前使用) 例如, 如果我们像这样定义一个函式:

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    keys = sorted(keywords.keys())
    for kw in keys:
        print(kw, ":", keywords[kw])

它可以如下调用

cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

打印结果(注意, 关键字参数名的列表是通过之前对字典 keys() 进行排序操作而创建的; 如果不这样做, 参数打印的顺序是不确定的.)

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

 

任意参数列表

指定函式能够在调用时接受任意数量的参数. 这些参数会被包装进一个元组 (参看 元组和序列). 在变长参数之前, 可以使用任意多个正常参数。

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

一般地, 这种 variadic 参数必须在形参列表的末尾, 因为它们将接收传递给函式的所有剩余输入参数.除此之外,任何出现在 *arg 之后的形式参数只能是关键字参数

>>> def concat(*args, sep="/"):
...    return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

 

参数列表解包

当参数存在于一个既存的列表或者元组之中, 但却需要解包以若干位置参数的形式被函数调用。

下面是一个利用 * 操作符解从列表或者元组中解包参数以供函数调用的例子

def cute(sex, age, height):
    print(sex,age,height,end=' ')
    print()

t = ['female', 18, 167]
cute(*t)

# output:
female 18 167 

同样的, 字典可以通过 ** 操作符来解包参数:

d = {'sex': 'male', 'age':18, 'height': 172}
cute(**d)

# output:
male 18 172 

* 或 **操作符有两个作用:

*arg 表示分散的参数,接受分散的参数。解析后的 arg 表示tuple或者map,可以传入函数参数为元组的函数。

© 著作权归作者所有

共有 人打赏支持
lemos
粉丝 6
博文 178
码字总数 90644
作品 0
芜湖
后端工程师
Lubuntu 14.04 下 安装Ulipad

Ulipad – 超级好用的国产Python IDE,开发者是limodou。此IDE本身就是用Python + wxPython编写的。在Windows系统中一直使用Ulipad,最近想在Linux(Ubuntu 12.04)进行学习和实验,安装过程还...

yeyunxiaopan
2014/11/20
0
0
解析Kotlin 函数用法与函数式编程

导读 本篇文章主要介绍Kotlin函数的用法,以及自己对函数式编程的一些理解。并且会和Python,C++做一些比较。 自从Google爸爸宣布Kotlin为自己的干儿子之后,Kotlin被各大社区炒的火热。 如果...

问题终结者
2017/10/19
0
0
Python函数式编程指南(一):概述

这大概算是Python最难啃的一块骨头吧。在我 Python生涯的这一年里,我遇到了一些Pythoner,他们毫无例外地完全不会使用函数式编程(有些人喜欢称为Pythonic),比如,从来不会 传递函数,不知...

icheer
2012/05/31
0
0
Python - __getattr__() 和 __getattribute__() 方法的区别

python 再访问属性的方法上定义了getattr() 和 getattribute() 2种方法,其区别非常细微,但非常重要。 如果某个类定义了 方法,在 每次引用属性或方法名称时 Python 都调用它(特殊方法名称...

索隆
2012/05/13
0
0
Python 学习总结(二):理解函数式编程,丰富开发

回顾上一节,我们已经将python的基础知识了解掌握,现在可以开始进一步的学习了。函数,无论在Java、C还是其他语言,都不可避免,这也从侧面反映了函数的重要性。所以,也就产生了函数式编程...

海岸线的曙光
01/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

ajax 提交返回map集合 获取不到值

后台java代码 @RequestMapping("/cameraList") @ResponseBody public Map<String, Object> cameraListForPage(@RequestParam(defaultValue = "1", value = "page") Integer page......

S三少S
8分钟前
0
0
TypeScrip最污的技术课-技术胖TypeScript图文视频教程

近日Node.js之父瑞安达尔(Ryan Dahl)发布新的开源项目 deno,从官方介绍来看,可以认为它是下一代 Node,使用 rust 语言代替 C++ 重新编写跨平台底层内核驱动,上层仍然使用 V8 引擎,最终...

JamesView
10分钟前
5
0
Es学习笔记

1.过滤排重聚合查询 筛选出某一个聚合值的个数统计。相当于mysql的distinct. 关键字:cardinality "aggs": { "2":{ "cardinality": { "field": "field" } ...

Gmupload
12分钟前
0
0
h5语义化标签

语义化HTML:用最恰当的HTML元素标签做恰当的事情。 优点: 提升可访问性; SEO; 结构清晰,利于维护; 通用容器:div——块级通用容器;span——短语内容无语义容器。 <title></title>:简...

莫西摩西
18分钟前
0
0
修改11g rac中 asm diskstring的发现路径

问题 : 如果我 们asm_disking以前是/dev/oracleasm/disks/* ,并且现在已经有磁盘组再用这个磁盘串了,那么,我们无法直接修改这个发现串为 ORCL:*,修改会报错,提示存在的磁盘无法使用新的...

tututu_jiang
21分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部