python 中的函式定义
python 中的函式定义
中柠檬 发表于5个月前
python 中的函式定义
  • 发表于 5个月前
  • 阅读 13
  • 收藏 0
  • 点赞 0
  • 评论 0

标题:腾讯云 新注册用户域名抢购1元起>>>   

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

>>> 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,可以传入函数参数为元组的函数。

  • 点赞
  • 收藏
  • 分享
粉丝 6
博文 177
码字总数 83819