在 Python 中,一个整数、一个字符串、一个列表、一个字典和一个元组都是对象,而会让从 C/C++ 或者 Java 语言转过来的人意外的是,Python 中的函数也是对象。因为一个函数和常见的整数、字符串等”公民“一样,都是对象,可以在运行时创建,并能被赋值给变量,作为集合对象的元素,还能够作为函数的参数和返回值
而究其原因,Python 中的函数之所以是一等公民,则是因为 Python 中的一切都是对象,也就是『Everything in Python is an object』,整数、浮点数、字符串、列表、字典、元组、函数、类本身、类型本身以及模块等都是对象。因为都是对象,所以 Python 中的函数才和整数、字符串、列表、字典和元组等对象的地位是平等的,成为了“一等公民”。下面,我将按从两个方面进一步解释『Python 中函数是一等公民』的含义,一是 Python 中的一切都是对象,二是不同对象行为的相似性。
一切都是对象
对象是类的实例,而类则是许多相同对象的抽象。在 Python 中,整数 1 是 int 类的一个实例,浮点数 1.0 是 float 类的一个实例,字符串 "hello" 是 str 类的一个实例, 用户自定义的函数 func 则是 funcion 类的一个实例。以此类推,还有常见的列表(list)、字典(dict)、集合(set)、自定义类(class)、模块(module)都有相应的类和实例。同时,int 类、str 类、function 类等类型本身则是 type 类的实例。下面是一段佐证代码:
import re
def factorial(n):
'''returns n!'''
return 1 if n < 2 else n * factorial(n - 1)
class Dumb:
pass
items = [1, 1.0, 'hello', [1], {'a': 1}, {1}, factorial, Dumb(), re, None, object]
for item in items:
print(f'对象 {item} 的类型是 {type(item).__name__},',
f'对象 {type(item).__name__}类 的类型是 {type(type(item)).__name__}.')
运行上面代码的输出结果如下:
对象 1 的类型是 int, 对象 int类 的类型是 type.
对象 1.0 的类型是 float, 对象 float类 的类型是 type.
对象 hello 的类型是 str, 对象 str类 的类型是 type.
对象 [1] 的类型是 list, 对象 list类 的类型是 type.
对象 {'a': 1} 的类型是 dict, 对象 dict类 的类型是 type.
对象 {1} 的类型是 set, 对象 set类 的类型是 type.
对象 的类型是 function, 对象 function类 的类型是 type.
对象 <__main__.Dumb object at ...> 的类型是 Dumb, 对象 Dumb类 的类型是 type.
对象 的类型是 module, 对象 module类 的类型是 type.
对象 None 的类型是 NoneType, 对象 NoneType类 的类型是 type.
对象 的类型是 type, 对象 type类 的类型是 type.
从上面的代码的运行结果可以看到,Python 程序中的所有数据都是某个类的实例,因而是一个对象。此外,所有类对象(int, float, str, list, dict, set, fuction, module, NoneType, object, type) 等则是 type 类的实例,也是一个对象。这里的类对象,表示本身是类型的对象。
我这里将对象定义为一个类的实例,因为我觉着这样理解起来更加直观,符合『面向对象的编程思想』里有关类和对象的定义。而 Python 官方文档里这样解释 对象(object) 的含义:Python 中的对象是对数据的抽象,Python 程序中所有数据都是由对象或者对象间的关系来表示的。每个对象都有各自的编号、类型和值
for item in items:
print(f'类对象 {item.__class__.__name__} 的基类是: {item.__class__.__bases__}')
print(f'类对象 {object.__name__} 的基类是: {object.__bases__}')
代码输出结果为:
类对象 int 的基类是: (,)
类对象 float 的基类是: (,)
类对象 str 的基类是: (,)
类对象 list 的基类是: (,)
类对象 dict 的基类是: (,)
类对象 set 的基类是: (,)
类对象 function 的基类是: (,)
类对象 Dumb 的基类是: (,)
类对象 module 的基类是: (,)
类对象 NoneType 的基类是: (,)
类对象 type 的基类是: (,)
类对象 object 的基类是: ()
参考 《Python Types and Objects》,可将不同类和对象的继承和实例化关系总结如下:
相似的行为
因为函数和整数、字符串等概念一样,都是对象,因而整数、字符串等对象所具有的行为,函数也同样拥有,下面分别进行对比说明。
运行时创建
我们可以在控制台会话中创建一个整数,同样也可以创建一个函数,因而函数和整数一样都可以在运行时创建:
>>> 1
1
>>> def factorial(n):
... '''returns n!'''
... return 1 if n < 2 else n * factorial(n - 1)
...
>>> factorial
# 访问各自的 __doc__ 属性
>>> (1).__doc__
"int([x]) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4"
>>> factorial.__doc__
'returns n!'
# 查看各自的类型
>>> type(1)
>>> type(factorial)
赋值给变量
我们可以将一个整数赋值给一个变量,也可以将一个函数赋值给一个变量:
# 整数 1 被赋值给变量 integer
>>> integer = 1
>>> integer
1
>>> [integer] * 3
[1, 1, 1]
>>> integer.__add__(4)
5
# 函数 factorial 被赋值给变量 fact
>>> fact = factorial
>>> fact
>>> [fact] * 3
[, , ]
>>> fact(3)
6
作为函数参数
我们可以把一个整数作为参数传给 factorial 函数本身,也可以把 factorial 函数作为参数传给 map 函数。
# 整数 1 作为函数 factorial 的参数
>>> factorial(1)
1
# 函数 factorial 作为函数 map 的参数
>>> map(factorial, range(4))
>>> list(map(factorial, range(4)))
[1, 1, 2, 6]
作为函数的返回值
我们可以将一个整数作为返回值返回给函数的调用者,同样也可以将一个函数作为返回值返回给函数的调用者:
# 函数 factorial 的返回值是一个整数
>>> res = factorial(4)
>>> res
24
>>> type(res)
# 函数 dectorate 的返回值是一个函数 inner
>>> import time
>>> from functools import wraps
>>> def decorate(func):
... @wraps(func)
... def inner(*args, **kwargs):
... start = time.time()
... result = func(*args, **kwargs)
... end = time.time()
... print(func.__name__, end - start)
... return func(*args)
... return inner
...
# 返回的函数可以继续被调用
>>> fact_with_time = decorate(factorial)
>>> fact_with_time
>>> fact_with_time(5)
factorial 0.0
120
正是因为 Python 中的一切都是对象,才让函数和其它“公民”一样具有了平等的地位和相似的行为,从而成为了 Python 中的“一等公民”。而理解了函数也是对象,是Python中的“一等公民”的概念,才能更好地理解 Python 中的有关『装饰器』的概念。
参考