吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1542|回复: 15
收起左侧

[学习记录] 论Python中1和True的关系

  [复制链接]
hrh123 发表于 2025-8-3 00:07
本帖最后由 hrh123 于 2025-9-5 23:48 编辑

一个学习记录贴,随便写点,大家见笑了

这几天有人来问我一个问题,Python里1和True是不是完全等价的,因为1 == True结果是True,再加上if 1if True效果一样,以及我印象中c里面1和true是完全等价的,所以我和ta说是的。但ta反驳我说这俩类型不一样,1 is True的结果是False,这一下给我也问懵了,于是就去研究了一下(开源的好处不就在这了吗),发现还挺有意思,就分享一下(把这当水区了)。

首先从Python的老祖宗c说起,1和true是定义在<stdbool.h>里的:

#define bool _Bool
#define true 1
#define false 0

就是这么简单粗暴,一样,没啥好说的。

小声明:本文引用的Pthon源代码均由Python软件基金会许可证开源

至于Python,先说if 1的问题吧,先引用一段官方文档里的话吧

在执行布尔运算的情况下,或是当表达式被用于流程控制语句时,以下值会被解读为假值: False, None, 所有类型的数字零,以及空字符串和空容器(包括字符串、元组、列表、字典、集合与冻结集合)。 所有其他值都会被解读为真值。

就很难评。也就是说if 2之类的也和if True等价。(原理后面解释)

再说说1 == True吧。在Python中,布尔型是整数型的子类(这个后面解释),True 被定义为 1,而 False 被定义为 0。在进行比较时,True被自动转换为其对应的整数值 1(具体也得后面讲),1 == 1显然是True啦。而两者的标识号(就是id)不同,自然1 is True返回False了。

详细扒一扒吧

Part 1

我去翻了一翻Python的源代码(https://github.com/python/cpython
1的定义应该在Objects/longobject.c还有Include/cpython /longintrepr.h中吧,我没细看,不重要。

再说True吧。cPython中实现布尔型的实现层在Objects/longobject.c,接口层在Include/boolobject.h


struct _longobject _Py_FalseStruct = {
    PyObject_HEAD_INIT(&PyBool_Type)
    { .lv_tag = _PyLong_FALSE_TAG,
        { 0 }
    }
};

struct _longobject _Py_TrueStruct = {
    PyObject_HEAD_INIT(&PyBool_Type)
    { .lv_tag = _PyLong_TRUE_TAG,
        { 1 }
    }
};

由这一坨可以知道TrueFalse分别被定义为全局静态对象_Py_TrueStruct_Py_FalseStruct,使用_longobject结构体,继承自长整型,实际分别储存着整数值1和0,而类型指针指向PyBool_Type
这个PyBool_Type的定义长这个样子

PyTypeObject PyBool_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "bool",
    offsetof(struct _longobject, long_value.ob_digit),  /* tp_basicsize */
    sizeof(digit),                              /* tp_itemsize */
    bool_dealloc,                               /* tp_dealloc */
    0,                                          /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    bool_repr,                                  /* tp_repr */
    &bool_as_number,                            /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
    bool_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    &PyLong_Type,                               /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    bool_new,                                   /* tp_new */
    .tp_vectorcall = bool_vectorcall,
};

其中规定了类型名称为"bool",而&PyLong_Type,这一行说明了ta的基类指向int,这就是为什么说布尔型是整型的子类了。

Part 2

接着来分析前面那句文档中的话,就是关于从其他对象创建布尔值的,在这里是由


PyObject *PyBool_FromLong(long ok)
{
    return ok ? Py_True : Py_False;
}

实现的。可以看出这里的返回值是根据ok是否为零决定的。

非零->True,零->False,也就是上面我引用的文档里的话。

Part 3

最后再说说True和1的转换问题吧。
可以看到代码中定义了很多布尔型的运算,我以&举例,代码长这样:

static PyObject *
bool_and(PyObject *a, PyObject *b)
{
    if (!PyBool_Check(a) || !PyBool_Check(b))
        return PyLong_Type.tp_as_number->nb_and(a, b);
    return PyBool_FromLong((a == Py_True) & (b == Py_True));
}

其中用的类型检查宏在这

#define PyBool_Check(x) (Py_IS_TYPE(x, &PyBool_Type))

以及布尔型作为整数时的运行模式在这


static PyNumberMethods bool_as_number = {
    0,                          /* nb_add */
    0,                          /* nb_subtract */
    0,                          /* nb_multiply */
    0,                          /* nb_remainder */
    0,                          /* nb_divmod */
    0,                          /* nb_power */
    0,                          /* nb_negative */
    0,                          /* nb_positive */
    0,                          /* nb_absolute */
    0,                          /* nb_bool */
    bool_invert,                /* nb_invert */
    0,                          /* nb_lshift */
    0,                          /* nb_rshift */
    bool_and,                   /* nb_and */
    bool_xor,                   /* nb_xor */
    bool_or,                    /* nb_or */
    0,                          /* nb_int */
    0,                          /* nb_reserved */
    0,                          /* nb_float */
    0,                          /* nb_inplace_add */
    0,                          /* nb_inplace_subtract */
    0,                          /* nb_inplace_multiply */
    0,                          /* nb_inplace_remainder */
    0,                          /* nb_inplace_power */
    0,                          /* nb_inplace_lshift */
    0,                          /* nb_inplace_rshift */
    0,                          /* nb_inplace_and */
    0,                          /* nb_inplace_xor */
    0,                          /* nb_inplace_or */
    0,                          /* nb_floor_divide */
    0,                          /* nb_true_divide */
    0,                          /* nb_inplace_floor_divide */
    0,                          /* nb_inplace_true_divide */
    0,                          /* nb_index */
};

注意:bool_and 实现的是按位与运算符 & 的行为,而不是逻辑关键字and。逻辑and是解释器在字节码层面实现的短路运算,不会调用这个函数。

总结

就以一段代码来解释Python中True 和1的区别和联系吧

assert True == 1          #值相等
assert True is not 1      #但对象不同
assert isinstance(True, int)  #是整型的子类
assert True + True == 2   #支持整数运算

结尾

写的太水了,本来打算放水区的,但我觉得这个问题还挺有意义的,也欢迎看到大家的讨论与分析吧

免费评分

参与人数 5吾爱币 +11 热心值 +5 收起 理由
Carinx + 1 + 1 用心讨论,共获提升!
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
三滑稽甲苯 + 3 + 1 用心讨论,共获提升!
怜渠客 + 1 + 1 用心讨论,共获提升!
yangyu1115 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

bester 发表于 2025-8-3 00:57
深度去理解了is 这个问题就很好解答,不过据说is在python 某些情况下有不同的表现,
https://www.zhihu.com/tardis/bd/art/670884793?source_id=1001

作者还是推荐大家只在判None时使用is

点评

is就是比较内存地址嘛,主要是c里面从定义来看这俩基本是完全一样的,让我一时间有点迷  详情 回复 发表于 2025-8-3 01:00
 楼主| hrh123 发表于 2025-8-3 01:00
本帖最后由 hrh123 于 2025-8-3 01:01 编辑
bester 发表于 2025-8-3 00:57
深度去理解了is 这个问题就很好解答,不过据说is在python 某些情况下有不同的表现,
https://www.zhihu.co ...

is就是比较内存地址嘛,主要是c语言里从定义来看这俩基本是完全一样的,让我一时间有点迷惑
bester 发表于 2025-8-3 01:04
hrh123 发表于 2025-8-3 01:00
is就是比较内存地址嘛,主要是c语言里从定义来看这俩基本是完全一样的,让我一时间有点迷

高级语言一般都会对一些基础类型进行一定的封装,js似乎也有这种情况,所以出现了== 和=== 吧,不过深入去扒了源码也好,不然就只知道是比较了内存地址,就理解的没那么深刻
huayu666 发表于 2025-8-3 06:54
真是学到了
wukong999 发表于 2025-8-3 07:03
佩服深扒的能力!!!66
三滑稽甲苯 发表于 2025-8-3 10:29
写代码的时候先自己写逻辑转换成 bool 再放到 if 里可以避免很多小问题
liyitong 发表于 2025-8-3 11:10
python里面的is是比较地址的,1 is True,肯定结果为假。
print(True==1)结果就是真。
非0即为真,所以if !0就是if 真
4everlove 发表于 2025-8-3 11:17
类似js里面的==和===
mooncity 发表于 2025-8-3 11:56
佩服钻研精神
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-21 19:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表