← All · tech

9. 类 9. 类 — Python 3.14.4 文档

https://docs.python.org/zh-cn/3/tutorial/classes.html

📅 2026-04-16 📁 tech 🏷️ python, classes, programming
Python classes tutorial

Linked skills: Job Radar, Github Ai Digest

url: "https://docs.python.org/zh-cn/3/tutorial/classes.html"

title: "9. 类 9. 类 — Python 3.14.4 文档"

date_saved: 2026-04-16

category: tech

tags: [python, classes, programming]

source: direct

reminder: false

cross_skills: [job-radar, github-ai-digest]

session_mention: never

url_hash: "370e38f9e177"

9. 类 9. 类 — Python 3.14.4 文档

**Summary**: Python classes tutorial

Key Points

Content

主题 自动 明亮 黑暗 目录 9. 类 9.1. 名称和对象 9.2. Python 作用域和命名空间 9.2.1. 作用域和命名空间示例 9.3. 初探类 9.3.1. 类定义语法 9.3.2. Class 对象 9.3.3. 实例对象 9.3.4. 方法对象 9.3.5. 类和实例变量 9.4. 补充说明 9.5. 继承 9.5.1. 多重继承 9.6. 私有变量 9.7. 杂项说明 9.8. 迭代器 9.9. 生成器 9.10. 生成器表达式 上一主题 8. 错误和异常 下一主题 10. 标准库概览 当前页 报告代码错误 改进本页面 显示源码 显示翻译源 导航 索引 模块 | 下一页 | 上一页 | Python » 3.14.4 Documentation » Python 教程 » 9. 类 | 主题 自动 明亮 黑暗 | 类 ¶ 类提供了把数据和功能绑定在一起的方法。创建新类时创建了新的对象 类型 ,从而能够创建该类型的新 实例 。实例具有能维持自身状态的属性,还具有能修改自身状态的方法(由其所属的类来定义)。 和其他编程语言相比,Python 的类只使用了很少的新语法和语义。Python 的类有点类似于 C++ 和 Modula-3 中类的结合体,而且支持面向对象编程(OOP)的所有标准特性:类的继承机制支持多个基类、派生的类能覆盖基类的方法、类的方法能调用基类中的同名方法。对象可包含任意数量和类型的数据。和模块一样,类也支持 Python 动态特性:在运行时创建,创建后还可以修改。 如果用 C++ 术语来描述的话,类成员(包括数据成员)通常为 public (例外的情况见下文 私有变量 ),所有成员函数都为 virtual 。与 Modula-3 中一样,没有用于从对象的方法中引用本对象成员的简写形式:方法函数在声明时,有一个显式的第一个参数代表本对象,该参数由方法调用隐式提供。与在 Smalltalk 中一样,Python 的类也是对象,这为导入和重命名提供了语义支持。与 C++ 和 Modula-3 不同,Python 的内置类型可以用作基类,供用户扩展。此外,与 C++ 一样,具有特殊语法的内置运算符(算术运算符、下标等)都可以为类实例重新定义。 由于缺乏关于类的公认术语,本章中偶尔会使用 Smalltalk 和 C++ 的术语。本章还会使用 Modula-3 的术语,Modula-3 的面向对象语义比 C++ 更接近 Python,但估计听说过这门语言的读者很少。 9.1. 名称和对象 ¶ 对象之间相互独立,多个名称(甚至是多个作用域内的多个名称)可以绑定到同一对象。这在其他语言中通常被称为别名。Python 初学者通常不容易理解这个概念,处理数字、字符串、元组等不可变基本类型时,可以不必理会。但是,对于涉及可变对象(如列表、字典,以及大多数其他类型)的 Python 代码的语义,别名可能会产生意料之外的效果。这样做,通常是为了让程序受益,因为别名在某些方面就像指针。例如,传递对象的代价很小,因为实现只传递一个指针;如果函数修改了作为参数传递的对象,调用者就可以看到更改——无需像 Pascal 那样用两个不同的机制来传参。 9.2. Python 作用域和命名空间 ¶ 在介绍类前,首先要介绍 Python 的作用域规则。类定义对命名空间有一些巧妙的技巧,了解作用域和命名空间的工作机制有利于加强对类的理解。并且,即便对于高级 Python 程序员,这方面的知识也很有用。 接下来,我们先了解一些定义。 namespace (命名空间)是从名称到对象的映射。现在,大多数命名空间都使用 Python 字典实现,但除非涉及到性能优化,我们一般不会关注这方面的事情,而且将来也可能会改变这种方式。命名空间的例子有:内置名称集合(包括 abs() 函数以及内置异常的名称等);一个模块的全局名称;一个函数调用中的局部名称。对象的属性集合也是命名空间的一种形式。关于命名空间的一个重要知识点是,不同命名空间中的名称之间绝对没有关系;例如,两个不同的模块都可以定义 maximize 函数,且不会造成混淆。用户使用函数时必须要在函数名前面加上模块名。 点号之后的名称是 属性 。例如,表达式 z.real 中, real 是对象 z 的属性。严格来说,对模块中名称的引用是属性引用:表达式 modname.funcname 中, modname 是模块对象, funcname 是模块的属性。模块属性和模块中定义的全局名称之间存在直接的映射:它们共享相同的命名空间! [ 1 ] 属性可以是只读的或者可写的。 在后一种情况下,可以对属性进行赋值。 模块属性是可写的:你可以写入 modname.the_answer = 42 。 也可以使用 del 语句删除可写属性。 例如, del modname.the_answer 将从名为 modname 对象中移除属性 the_answer 。 命名空间是在不同时刻创建的,且拥有不同的生命周期。内置名称的命名空间是在 Python 解释器启动时创建的,永远不会被删除。模块的全局命名空间在读取模块定义时创建;通常,模块的命名空间也会持续到解释器退出。从脚本文件读取或交互式读取的,由解释器顶层调用执行的语句是 __main__ 模块调用的一部分,也拥有自己的全局命名空间。内置名称实际上也在模块里,即 builtins 。 函数的局部命名空间在函数被调用时被创建,并在函数返回或抛出未在函数内被处理的异常时,被删除。(实际上,用“遗忘”来描述实际发生的情况会更好一些。)当然,每次递归调用都有自己的局部命名空间。 一个命名空间的 作用域 是 Python 代码中的一段文本区域,从这个区域可直接访问该命名空间。“可直接访问”的意思是,该文本区域内的名称在被非限定引用时,查找名称的范围,是包括该命名空间在内的。 作用域虽然是被静态确定的,但会被动态使用。执行期间的任何时刻,都会有 3 或 4 个“命名空间可直接访问”的嵌套作用域: 最内层作用域,包含局部名称,并首先在其中进行搜索 那些外层闭包函数的作用域,包含“非局部、非全局”的名称,从最靠内层的那个作用域开始,逐层向外搜索。 倒数第二层作用域,包含当前模块的全局名称 最外层(最后搜索)的作用域,是内置名称的命名空间 如果一个名称被声明为全局,则所有引用和赋值都将直接指向“倒数第二层作用域”,即包含模块的全局名称的作用域。 要重新绑定在最内层作用域以外找到的变量,可以使用 nonlocal 语句;如果未使用 nonlocal 声明,这些变量将为只读(尝试写入这样的变量将在最内层作用域中创建一个 新的 局部变量,而使得同名的外部变量保持不变)。 通常,当前局部作用域将(按字面文本)引用当前函数的局部名称。在函数之外,局部作用域引用与全局作用域一致的命名空间:模块的命名空间。 类定义在局部命名空间内再放置另一个命名空间。 划重点,作用域是按字面文本确定的:模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用。另一方面,实际的名称搜索是在运行时动态完成的。但是,Python 正在朝着“编译时静态名称解析”的方向发展,因此不要过于依赖动态名称解析!(局部变量已经是被静态确定了。) Python 有一个特殊规定。如果不存在生效的 global 或 nonlocal 语句,则对名称的赋值总是会进入最内层作用域。赋值不会复制数据,只是将名称绑定到对象。删除也是如此:语句 del x 从局部作用域引用的命名空间中移除对 x 的绑定。所有引入新名称的操作都是使用局部作用域:尤其是 import 语句和函数定义会在局部作用域中绑定模块或函数名称。 global 语句用于表明特定变量在全局作用域里,并应在全局作用域中重新绑定; nonlocal 语句表明特定变量在外层作用域中,并应在外层作用域中重新绑定。 9.2.1. 作用域和命名空间示例 ¶ 下例演示了如何引用不同作用域和名称空间,以及 global 和 nonlocal 对变量绑定的影响: def scope_test (): def do_local (): spam = "local spam" def do_nonlocal (): nonlocal spam spam = "nonlocal spam" def do_global (): global spam spam = "global spam" spam = "test spam" do_local () print ( "After local assignment:" , spam ) do_nonlocal () print ( "After nonlocal assignment:" , spam ) do_global () print ( "After global assignment:" , spam ) scope_test () print ( "In global scope:" , spam ) 示例代码的输出是: After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam 注意, 局部 赋值(这是默认状态)不会改变 scope_test 对 spam 的绑定。 nonlocal 赋值会改变 scope_test 对 spam 的绑定,而 global 赋值会改变模块层级的绑定。 而且, global 赋值前没有 spam 的绑定。 9.3. 初探类 ¶ 类引入了一点新语法,三种新的对象类型和一些新语义。 9.3.1. 类定义语法 ¶ 最简单的类定义形式如下: class ClassName : < 语句 - 1 > . . . < 语句 - N > 与函数定义 ( def 语句) 一样,类定义必须先执行才能生效。把类定义放在 if 语句的分支里或函数内部试试。 在实践中,类定义内的语句通常都是函数定义,但也可以是其他语句。这部分内容稍后再讨论。类里的函数定义一般是特殊的参数列表,这是由方法调用的约定规范所指明的 --- 同样,稍后再解释。 当进入类定义时,将创建一个新的命名空间,并将其用作局部作用域 --- 因此,所有对局部变量的赋值都是在这个新命名空间之内。 特别的,函数定义会绑定到这里的新函数名称。 当 (从结尾处) 正常离开类定义时,将创建一个 类对象 。 这基本上是一个围绕类定义所创建的命名空间的包装器;我们将在下一节中了解有关类对象的更多信息。 原始的 (在进入类定义之前有效的) 作用域将重新生效,类对象将在这里与类定义头所给出的类名称进行绑定 (在这个示例中为 ClassName )。 9.3.2. Class 对象 ¶ 类对象支持两种操作:属性引用和实例化。 属性引用 使用 Python 中所有属性引用所使用的标准语法: obj.name 。 有效的属性名称是类对象被创建时存在于类命名空间中的所有名称。 因此,如果类定义是这样的: class MyClass : """一个简单的示例类""" i = 12345 def f ( self ): return 'hello world' 那么 MyClass.i 和 MyClass.f 就是有效的属性引用,将分别返回一个整数和一个函数对象。 类属性也可以被赋值,因此可以通过赋值来改变 MyClass.i 的值。 __doc__ 也是一个有效的属性,将返回所属类的文档字符串: "A simple example class" 。 类的 实例化 使用函数表示法。 可以把类对象视为是返回该类的一个新实例的不带参数的函数。 举例来说(假设使用上述的类): x = MyClass () 创建类的新 实例 并将此对象分配给局部变量 x 。 实例化操作 (“调用”类对象) 会创建一个空对象。 许多类都希望创建的对象实例是根据特定初始状态定制的。 因此一个类可能会定义名为 __init__() 的特殊方法,就像这样: def __init__ ( self ): self . data = [] 当一个类定义了 __init__() 方法时,类的实例化会自动为新创建的类实例唤起 __init__() 。 因此在这个例子中,可以通过以下语句获得一个已初始化的新实例: x = MyClass () 当然, __init__() 方法还有一些参数用于实现更高的灵活性。 在这种情况下,提供给类实例化运算符的参数将被传递给 __init__() 。 例如, >>> class Complex : ... def __init__ ( self , realpart , imagpart ): ... self . r = realpart ... self . i = imagpart ... >>> x = Complex ( 3.0 , - 4.5 ) >>> x . r , x . i (3.0, -4.5) 9.3.3. 实例对象 ¶ 现在我们能用实例对象做什么? 实例对象所能理解的唯一操作是属性引用。 有两种有效的属性名称:数据属性和方法。 数据属性 相当于 Smalltalk 中的 "实例变量" 和 C++ 中的 "数据成员"。数据属性无需声明;与局部变量 一样,它们在首次赋值时就会出现。 例如,如果 x 是上面创建的 MyClass 的实例 ,那么下面的代码将打印值 16 ,而不会留下任何痕迹: x . counter = 1 while x . counter < 10 : x . counter = x . counter * 2 print ( x . counter ) del x . counter 另一种实例属性引用称为 方法 。 方法是“从属于”对象的函数。 实例对象的有效方法名称依赖于其所属的类。 根据定义,一个类中所有是函数对象的属性都是定义了其实例的相应方法。 因此在我们的示例中, x.f 是有效的方法引用,因为 MyClass.f 是一个函数,而 x.i 不是方法,因为 MyClass.i 不是函数。 但是 x.f 与 MyClass.f 并不是一回事 --- 它是一个 方法对象 ,不是函数对象。 9.3.4. 方法对象 ¶ 通常,方法在绑定后立即被调用: x . f () 如果 x = MyClass() ,就像上面这样,则将返回字符串 'hello world' 。 不过,方法并非必须立即被调用: x.f 是一个方法对象,并可被存储起来并在以后再调用。 例如: xf = x . f while True : print ( xf ()) 将持续打印 hello world ,直到结束。 当一个方法被调用时究竟会发生什么? 你可能已经注意到尽管 f() 的函数定义指定了一个参数,但上面调用 x.f() 时却没有带参数。 这个参数发生了什么事? 当一个需要参数的函数在不附带任何参数的情况下被调用时 Python 肯定会引发异常 --- 即使参数实际上没有被使用... 实际上,你可能已经猜到了答案:方法的特殊之处就在于实例对象会作为函数的第一个参数被传入。 在我们的示例中,调用 x.f() 其实就相当于 MyClass.f(x) 。 总之,调用一个具有 n 个参数的方法就相当于调用再多一个参数的对应函数,这个参数值为方法所属实例对象,位置在其他参数之前。 总而言之,方法的运作方式如下。 当一个实例的非数据属性被引用时,将搜索该实例所属的类。 如果名称表示一个属于函数对象的有效类属性,则指向实例对象和函数对象的引用将被打包为一个方法对象。 当传入一个参数列表调用该方法对象时,将基于实例对象和参数列表构造一个新的参数列表,并传入这个新参数列表调用相应的函数对象。 9.3.5. 类和实例变量 ¶ 一般来说,实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法: class Dog : kind = 'canine' # 类变量被所有实例所共享 def __init__ ( self , name ): self . name = name # 实例变量为每个实例所独有 >>> d = Dog ( 'Fido' ) >>> e = Dog ( 'Buddy' ) >>> d . kind # 被所有的 Dog 实例所共享 'canine' >>> e . kind # 被所有的 Dog 实例所共享 'canine' >>> d . name # 为 d 所独有 'Fido' >>> e . name # 为 e 所独有 'Buddy' 正如在 名称和对象 中所讨论的,共享数据可能在涉及 mutable 对象如列表和字典时导致令人惊讶的结果。 例如,以下代码中的 tricks 列表不应被用作类变量因为所有 Dog 实例将共享一个单独的列表: class Dog : tricks = [] # 类变量的错误用法 def __init__ ( self , name ): self . name = name def add_trick ( self , trick ): self . tricks . append ( trick ) >>> d = Dog ( 'Fido' ) >>> e = Dog ( 'Buddy' ) >>> d . add_trick ( 'roll over' ) >>> e . add_trick ( 'play dead' ) >>> d . tricks # 非预期地被所有的 Dog 实例所共享 [ 'roll over' , 'play dead' ] 正确的类设计应该使用实例变量: class Dog : def __init__ ( self , name ): self . name = name self . tricks = [] # 为每个 Dog 实例新建一个空列表 def add_trick ( self , trick ): self . tricks . append ( trick ) >>> d = Dog ( 'Fido' ) >>> e = Dog ( 'Buddy' ) >>> d . add_trick ( 'roll over' ) >>> e . add_trick ( 'play dead' ) >>> d . tricks [ 'roll over' ] >>> e . tricks [ 'play dead' ] 9.4. 补充说明 ¶ 如果同样的属性名称同时出现在实例和类中,则属性查找会优先选择实例: >>> class Warehouse : ... purpose = 'storage' ... region = 'west' ... >>> w1 = Warehouse () >>> print ( w1 . purpose , w1 . region ) storage we

Images

![image-0](https://docs.python.org/zh-cn/3/_static/py.svg)

![image-1](https://docs.python.org/zh-cn/3/_static/py.svg)

![image-2](https://docs.python.org/zh-cn/3/_static/py.svg)

Related Skills