编程学习网 > 编程语言 > Python > 图解 Python:数字也能调用方法?一文搞懂“一切皆对象”
2026
08-11

图解 Python:数字也能调用方法?一文搞懂“一切皆对象”


写运维脚本时,我们经常会见到两类很反常的代码。

数字居然能调用方法:

number = 10
print(number.bit_length())

函数居然能放进字典,再像普通数据一样取出来:

def restart(service):
    return f"restart {service}"
commands = {"restart": restart}
print(commands["restart"]("nginx"))

为什么 10 不是一块只能参与计算的裸数据?为什么 restart 可以被保存、传递,取出来以后还能执行?

答案是 Python 常说的那句话:「一切皆对象。」

不过,这句话并不是说 iffor 或赋值语句本身都是普通对象值。更准确的范围是:在 Python 程序运行时,数字、字符串、容器、函数、类、模块等,都以对象的形式存在。

01 名称绑定对象继续往里看

上一篇我们拆解了这行代码:

age = 18

它不是创建一个只能装整数的 age 盒子,而是让名称 age 绑定到整数对象 18

名称 age
   │
   │ 绑定
   ▼
int 对象 18

这解释了为什么 Python 变量不用提前声明固定类型,却也留下了一个新问题:这个对象到底是什么?

先看几个运行时的值:

age = 18
service = "nginx"
ports = [80, 443]
print(type(age).__name__)
print(type(service).__name__)
print(type(ports).__name__)

输出:

int
str
list

它们的值不同,支持的操作也不同,但都具有对象的共同特征。名称没有被固定成某一种类型,名称当前绑定的对象却一定有类型。

02 对象到底由什么构成

Python 的数据模型看,每个对象都有三项核心信息:身份、类型和值。

身份:是不是同一个对象

对象被创建后,在它的生命周期内会保持自己的身份。is 比较的就是两个名称是否绑定到同一个对象:

a = []
b = []
c = a
print(a == b)
print(a is b)
print(a is c)

输出:

True
False
True

 的内容相等,却是两个不同的列表对象; 才绑定着同一个对象。

id() 可以返回对象的身份标识,但不要把它当成跨 Python 实现都成立的原始内存地址接口。

类型:对象支持什么操作

类型决定对象可以参与哪些运算、如何查找属性,以及它支持哪些协议。

print(10 + 5)
print("10" + "5")

同一个 +,遇到 int 对象时执行数值相加,遇到 str 对象时执行字符串拼接。

值:对象当前承载的内容或状态

列表对象的内容可以原地变化:

services = ["nginx"]
services.append("redis")
print(services)

整数、字符串等不可变对象则不能原地改变自身的值。相关计算会得到另一个对象。

入门时可以把对象概括为身份 + 类型 + 。方法不宜简单地画成每个对象内部各存一份代码;通常是对象的类型提供相应能力,属性查找时再找到它。

03 整数为什么也能调用方法

 

回到开头的代码:

number = 10
print(number.bit_length())

输出 4,因为十进制整数 10 的二进制形式是 1010,需要四个有效二进制位。

number 绑定的是一个 int 对象,而 int 本身是 Python 的内置类。bit_length  int 类型提供的方法。

通过实例访问它时,可以把查找过程理解为:

找到 number 绑定的对象
        ↓
确认对象类型是 int
        ↓
int 上找到 bit_length
        ↓
得到绑定到 number 的方法
        ↓
加括号执行

下面两种写法得到相同结果:

number = 10
print(number.bit_length())
print(int.bit_length(number))

输出:

4
4

还可以先保存这个绑定方法,再调用它:

method = number.bit_length
print(method())
print(method.__self__ is number)

输出:

4
True

所以,整数有方法,不是每个整数对象都单独复制了一套方法代码,而是对象通过自己的类型获得相应操作。

04 def创建的是函数对象

 

Python 执行 def 语句时,会创建一个函数对象,再把函数名绑定到它。函数体不会在定义时运行,只有函数对象被调用时才会执行。

def check_disk():
    return "ok"

关系可以表示为:

名称 check_disk
        │
        │ 绑定
        ▼
   function 对象

既然函数是对象,它就能被赋给另一个名称:

task = check_disk
print(task is check_disk)
print(task())

输出:

True
ok

这里没有复制函数代码,task  check_disk 绑定着同一个函数对象。

函数也可以保存到容器、作为参数传递,或者作为返回值返回:

jobs = {"disk": check_disk}

def run(job):
    return job()

def choose_job():
    return check_disk

print(jobs["disk"]())
print(run(check_disk))
print(choose_job()())

三个结果都是 ok

这里最容易混淆的是括号:

task = check_disk,保存的是函数对象;

task = check_disk(),先执行函数,再保存返回值;

return check_disk,返回函数对象;

return check_disk(),返回函数的执行结果。

05 class创建的也是对象

 

函数是对象,类也一样。

class Server:
    def __init__(self, name):
        self.name = name

执行 class 语句时,Python 会准备类的命名空间、执行类体,再创建类对象并把名称 Server 绑定到它。

对于这个使用默认元类的普通类:

print(type(Server) is type)
print(callable(Server))

输出:

True
True

Server 本身是一个类对象。把它赋给另一个名称,不会创建新类:

factory = Server
print(factory is Server)

调用这个普通类对象,通常会创建并初始化一个实例:

web = factory("web-a")
print(type(web) is Server)
print(web.name)

输出:

True
web-a

类对象和它创建的实例是两个不同对象。类对象可以被保存和传递,因此插件系统、对象工厂与依赖注入经常把作为参数交给其他代码。

06 统一对象模型,能力却不相同

数字、字符串、函数和类都可以被名称绑定,也可以放进容器、作为参数传递,或从函数返回。

一切皆对象不等于一切都能调用。只有支持调用协议的对象才能使用 ()

def hello():
    return "hello"

class Server:
    pass

print(callable(hello))
print(callable(Server))
print(callable(10))
print(callable("nginx"))

输出:

True
True
False
False

对象能做什么,最终取决于它的类型与实现的协议,而不是对象这个身份本身。

统一对象模型也为一些更高级的能力打下基础:

装饰器可以接收函数对象、包装它,再让原名称绑定返回的新对象;

闭包可以返回函数对象,并依靠词法作用域保留对外层绑定的访问;

元编程可以检查、创建或替换函数与类对象。

这些能力还依赖名称绑定、作用域、描述器、调用协议或元类等机制,一切皆对象是共同基础,不是唯一条件。后面的装饰器、迭代器和上下文管理器,也会继续沿用类型决定协议能力这条主线。

07 一张图总结

把前两篇连起来,Python 的变量与对象模型已经形成一条完整关系:

变量更准确地说是名称,名称负责绑定对象;

每个对象都有身份、类型和值;

类型决定对象支持哪些操作和协议;

数字、字符串、容器、函数、类等运行时值都遵守对象规则;

函数和类可调用,但可调用只是部分对象拥有的能力。

现在看下面的代码:

a = [1, 2]
b = a
b.append(3)
print(a)
print(a is b)

输出:

[1, 2, 3]
True

 都是名称,并且绑定着同一个列表对象。append(3) 修改的是这个共享对象的内容,而不是名称 本身。

这又带来一组新问题:为什么整数不能原地修改,列表却可以?什么时候赋值只增加一个引用,什么时候需要浅拷贝或深拷贝?

以上就是“图解 Python:数字也能调用方法?一文搞懂“一切皆对象”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。 

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取