Python子类化threading.Thread的属性命名规范及安全属性咨询
我阅读了threading.Thread的文档,其中提到:
指定线程活动有两种方式:向构造函数传入可调用对象,或在子类中重写run()方法
我编写了如下测试程序:
class MyThread(threading.Thread): def __init__(self): super().__init__() self._started = False def run(self): self._started = True obj = MyThread() obj.start() obj.join() print(self._started)
但程序运行失败,报错信息如下:
$ python3 ./1.py Traceback (most recent call last): File "/dev/shm/.1000.home.tmp.dir/./1.py", line 10, in <module> obj.start() File "/usr/lib/python3.11/threading.py", line 951, in start if self._started.is_set(): ^^^^^^^^^^^^^^^^^^^^ AttributeError: 'bool' object has no attribute 'is_set'
文档中并未提及_started是threading.Thread的内部属性,我有以下疑问:
- Python中是否存在关于实现保留字段的约定?
- 子类化
threading.Thread时是否不应使用下划线开头的字段? - 哪些属性是安全可设置的?
1. Python中关于实现保留字段的约定
Python有明确的命名约定:
- 单下划线开头的属性(如
_x)属于内部实现细节,语法上不强制私有,但约定俗成是类内部使用的,子类和外部代码应避免直接访问或覆盖。 - 双下划线开头且结尾无双下划线的属性(如
__x)会触发名称修饰,Python会自动将其改为_类名__x,避免子类意外覆盖父类内部属性。 - 前后双下划线的属性(如
__init__)是Python特殊方法/属性,属于语言层面约定,不能随意使用。
这些规则在PEP 8规范中有明确说明,核心是用下划线区分「公开接口」和「内部实现」。
2. 子类化threading.Thread时是否不应使用下划线开头的字段
不是绝对不能用,但强烈不建议。threading.Thread内部会用到大量单下划线开头的属性(比如_started、_target、_args等),这些属性不会出现在公开文档中,但却是类正常运行的关键。
如果子类使用同名下划线属性,会直接覆盖父类内部属性,导致父类方法执行出错——就像你的代码中,父类start()方法期望_started是带is_set()方法的Event对象,你却把它改成布尔值,直接引发属性错误。
如果一定要用自定义内部属性,建议用双下划线开头的名称(如self.__started),触发名称修饰后会变成_MyThread__started,不会和父类的_started冲突。
3. 哪些属性是安全可设置的
threading.Thread的公开属性是文档明确列出的,这些可以安全访问或设置:
name:线程名称,可直接设置或读取ident:线程标识符,只读daemon:是否为守护线程,可在start()之前设置
另外,你可以安全添加自己的公开属性(不用下划线开头),比如self.started_flag = False,既不会和父类内部属性冲突,也符合命名规范。
重写run()方法是文档明确允许的,但要注意不要破坏父类初始化逻辑——你的代码中__init__调用super().__init__()是正确的,若要添加自定义属性,优先用非下划线开头的名称,或用双下划线避免冲突。
内容的提问来源于stack exchange,提问作者KamilCuk

