函数是对象
在Python中,函数是一等对象(first-class object)。
对于一等对象有以下特征:
- 在运行时创建
- 能赋值给变量或数据结构中的元素
>>> def foo(): ... return 1 ... # 赋值给参数 >>> test = foo >>> test() 1 >>> test <function foo at 0x7ff6623babf8> >>> >>> def foo2(): ... return 2 ... # 存储在列表中 >>> funcs = [foo, foo2] >>> funcs [<function foo at 0x7ff6623babf8>, <function foo2 at 0x7ff6623bad90>]
- 能作为参数传给函数
# 以sorted函数举例,我们可以将len函数作为参数传进去 >>> strs = ['a', 'aaa', 'aa'] >>> sorted(strs, key=len) ['a', 'aa', 'aaa']
- 能作为函数的返回结果
>>> def foo(): ... pass ... >>> def foo1(): ... return foo ... >>> foo1() <function foo at 0x7ff6623bae18>
可调用对象
除了用户定义的函数外,调用运算符(即())还可以用在其他对象上, python的数据模型文档上列出了七种可调用对象。
-
用户定义的函数
使用def语句或lambda表达式创建
-
内置函数
如len等
-
内置方法
如dict.get等
-
方法
在类的定义体中定义的函数
-
类
调用类时会运行类的__new__方法创建一个实例,然后运行__init__方法,初始化实例,最后把实例返回给调用方。所以调用类相当与调用函数。
-
类的实例
如果类定义了__call__方法, 那么它的实例可以作为函数调用
-
生成器函数
使用yield关键字的函数或方法。
用户定义的可调用类型
对于任何的Python对象,只要实现实例方法__call__,都可以表现的像函数。
如:
import random
class BingoCage:
def __init__(self, items):
self._items = list(items)
random.shuffle(self._items)
def pick(self):
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty Bingocage')
def __call__(self):
return self.pick()
if __name__ == '__main__':
bingo = BingoCage(range(3))
print(bingo.pick())
print(bingo())
print(callable(bingo))
在上述例子中,我们可以使用bingo()来直接代替bingo.pick(),进行更便捷的操作。
参数
Python提供了一个及其灵活的参数处理机制。其中主要有几种参数,如必选参数,默认参数,可变参数,关键字参数,仅限关键字参数
必选参数
必选参数就是在传参数时,按照顺序,依次传值。
>>> def foo(a, b):
... pass
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() missing 2 required positional arguments: 'a' and 'b'
>>> foo(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() missing 1 required positional argument: 'b'
>>> foo(1, 2)
在上述的例子中,传入的参数中在foo
函数中a=1, b=2
默认参数
对于默认参数,我们通常在函数中直接给参数以默认的值,调用的时候默认参数已经有值,所以可以不用传值,但是如果传值,将改变默认参数
需要注意的是,必选参数一定在默认参数前面
>>> def foo(a, b=1):
... print("a:", a,'b:', b)
...
>>> foo(2)
a: 2 b: 1
>>> foo(1, 2)
a: 1 b: 2
>>> foo(3, b=4)
a: 3 b: 4
可变参数 *
对于可变参数,传入的参数的个数是可变的,然后这些可变参数在函数调用是自动变成一个元组
>>> def foo(a, *b):
... print(type(b))
... print(b)
...
>>> foo(1, 2, 3)
<class 'tuple'>
(2, 3)
>>> foo(1, [2, 3])
<class 'tuple'>
([2, 3],)
>>> foo(1)
<class 'tuple'>
()
关键字参数 **
关键字参数与可变参数都是数量可变的,但是对于关键字参数,我们可以传入任意个含有参数名的参数,而且它只接受含有参数名的参数。这些参数在函数内部自动组成一个dict。
>>> def foo(a, **b):
... print('a:', a, 'b:', b)
...
>>> foo(1, 2, 3, 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes 1 positional argument but 4 were given
>>> foo(1, a=1, b=2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple va对lues for argument 'a'
>>> foo(1, c=1, d=2)
a: 1 b: {'c': 1, 'd': 2}
仅限关键字参数
也叫做命名关键字参数,这可以限制要传入的参数的名字,只能传入已经命名的关键字参数。
仅限关键字参数也可以设置默认值,但这不是强制的。
>>> def foo(a, *, b):
... print('a:', a, 'b:', b)
...
>>> foo(a=1, c=2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'c'
>>> foo(a=1, b=3)
a: 1 b: 3
混合参数
基于以上参数,我们可以将各种格式的参数混合起来,来达到我们的目的。
>>> def foo(a, *content, b=2, **items):
... print('a:', a, '\ncontent:', content, '\nb:', b, '\nitems:', items)
...
>>> foo(1, 2, 3, b=4, c=5)
a: 1
content: (2, 3)
b: 4
items: {'c': 5}
>>> foo(1, 2, 3, c=5)
a: 1
content: (2, 3)
b: 2
items: {'c': 5}
以上述例子,里面虽然包含了仅限关键字参数, 但是我们也有一个关键字参数**items
来处理其余的带有关键字的参数
函数注解
Python对注解所做的唯一的事情就是把它们存储在函数的__annotations__
属性中,除此之外,Python不做检查,不做强制,不做验证,什么操作都不进行。也就是说,注解对Python解释器是毫无用处的
>>> def foo(test: str, max_len: 'int > 0'=80) -> str:
... pass
...
一个简单的函数注解就像上面一样,函数声明的各个参数可以在:
后面增加注解表达式。如果参数有默认值,则放在参数名和=号之间。如果想注解返回值,则在末尾添加->和一个表达式,这个表达式可以是任何类型,最常见的是类(str和int),还有字符串(“int > 0”)。
我们可以使用函数自带的__annotations__
属性来得到他们的注解。
>>> foo.__annotations__
{'test': <class 'str'>, 'max_len': 'int > 0', 'return': <class 'str'>}
匿名函数
lambda关键字创建匿名函数
但是在Python中很少使用lambda表达式。因为一些非平凡的lambda表达式要么难以阅读,要么无法写出,所以lambda通常使用在列表参数中,或者传递给其他高阶参数。
>>> a = [-1, -2, 1, 2, 3, 4]
>>> sorted(a, key=lambda num: num**2)
[-1, 1, -2, 2, 3, 4]
函数内省
在上一个我们了解了函数的__annoatations__
属性,函数还拥有很多参数,我们可以使用dir()
来查看
>>> dir(foo)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
其中较为重要的有__annotations__
, __call__
等,我暂时对他完全想不到应用场景,用到再说, 官方文档,其中关于callable types
是对可调用对象的介绍。