暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Python 基础知识整理 - 类(1)

数据库杂货铺 2021-12-22
510

 

类提供了一种将数据和功能捆绑在一起的方法。创建一个新类会创建一个新类型的对象,允许创建该类型的新实例。每个类实例都可以附加属性以维护其状态。类实例还可以具有用于修改其状态的方法(由其类定义)。

 

与其他编程语言相比,Python 的类机制使用最少新语法和语义添加类。它是 C++ Modula-3 的类机制的混合体。Python 类提供了面向对象编程的所有标准特性:类继承机制允许多个基类,派生类可以重写其基类的任何方法,方法可以调用具有相同名称的基类的方法。对象可以包含任意数量和种类的数据。与模块一样,类也具有 Python 的动态特性:它们是在运行时创建的,可以在创建后进一步修改。

 

C++ 术语中,通常类成员(包括数据成员)是 public 的(除了下面提到的私有变量之外),并且所有成员函数都是 virtual 的。与 Modula-3 一样,没有快捷方式从对象的方法中引用对象的成员:方法函数显式声明第一个参数表示对象本身,该参数由调用隐式提供。与 Smalltalk 一样,类本身就是对象。这为导入和重命名提供了语义。与 C++ Modula-3 不同,内置类型可以用作用户扩展的基类。同样,类似 C++,大多数具有特殊语法(算术运算符、下标等)的内置运算符可以被类实例重新定义。

 

关于名称和对象

 

对象具有独特性,可以将多个名称(在多个作用域中)绑定到同一对象。这在其他语言中称为别名。在 Python 中,乍一看这一点通常不受欢迎,在处理不可变的基本类型(数字、字符串、元组)时,可以安全地忽略这一点。然而,别名对 Python 代码的语义可能会产生令人惊讶的影响,这些代码涉及列表、字典和大多数其他类型的可变对象。这通常有利于程序,因为别名在某些方面的行为类似于指针。例如,传递一个对象代价很低,因为实际上只传递一个指针;如果函数修改了作为参数传递进来的对象,调用者将看到变化 —— 这就不需要像 Pascal 那样使用两种不同的参数传递机制。

 

Python 作用域和命名空间

 

在介绍类之前,首先要介绍一些 Python 的作用域规则。类定义使用了命名空间的一些技巧,需要知道作用域和命名空间是如何工作的,才能完全理解发生了什么。顺便说一句,关于这个主题的知识对任何高级 Python 程序员都很有用。

 

让我们从一些定义开始。

 

命名空间是从名称到对象的映射。大多数命名空间目前都是作为 Python 字典实现的,但这通常不会以任何方式被注意到(性能除外),而且将来可能会改变。命名空间的示例有:一组内置名称(包含诸如 abs() 之类的函数和内置异常名称);模块中的全局名称;以及函数调用中的局部名称。从某种意义上说,对象的属性集也形成了命名空间。关于命名空间,需要知道的重要一点是,不同命名空间中的名称之间绝对没有关系;例如,两个不同的模块都可以定义一个 maximize 函数,而不会产生混淆 - 模块的用户必须在其前面加上模块名。

 

顺便说一下,对点后面的任何名称,这里使用单词 attribute (属性)来表述 — 例如,在表达式 z.real 中,real 是对象 z 的属性。严格来说,对模块中名称的引用是属性引用:在表达式 modname.funcname 中,modname 是模块对象,funcname 是模块对象的属性。在这种情况下,模块的属性和模块中定义的全局名称之间正好存在一个简单的映射:它们共享相同的命名空间!

 

属性可以是只读的,也可以是可写的。在后一种情况下,可以对属性赋值。模块属性是可写的:可以这样写 modname.the_answer = 42。还可以使用 del 语句删除可写属性。例如,del modname.the_answer 将从名为 modname 的对象中删除属性 the_answer

 

命名空间是在不同的时刻创建的,具有不同的生命周期。包含内置名称的命名空间是在 Python 解释器启动时创建的,并且永远不会被删除。在读入模块定义时创建模块的全局命名空间;通常,模块命名空间也会持续到解释器退出。解释器的顶级调用执行的语句(无论是从脚本文件读取或以交互方式执行)被视为 __main__ 模块的一部分,因此它们有自己的全局命名空间。(内置名称实际上也存在于模块中;称为 builtins。)

 

函数的局部命名空间在调用函数时创建,在函数返回或引发未在函数内处理的异常时删除。当然,每个递归调用都有自己的局部命名空间。

 

作用域是 Python 程序的一个文本区域,在该区域中可以直接访问命名空间。

 

虽然作用域是静态确定的,但它们是动态使用的。在执行过程中的任何时候,都有34个嵌套作用域的命名空间可以直接访问:

 

 最里面的作用域(首先搜索)包含局部名称

 

 封闭函数的作用域,从最近的封闭作用域开始搜索,包含的既非局部也非全局名称

 

 倒数第二个作用域包含当前模块的全局名称

 

 最外层的作用域(最后搜索)是包含内置名称的命名空间

 

如果一个名称被声明为全局的,那么所有引用和赋值都直接进入包含模块全局名称的中间作用域。要重新绑定在最内层作用域之外的变量,可以使用 nonlocal 语句;如果未声明为 nonlocal 变量,则这些变量是只读的(尝试对此类变量赋值只会在最内部的作用域中创建一个新的局部变量,而保持同名的外部变量不变)。

 

通常,局部作用域引用当前函数的局部名称。在函数外部,局部作用域引用与全局作用域相同的命名空间:模块的命名空间。类定义在局部作用域中放置另一个命名空间。

 

Python 的一个特殊点 – 如果没有生效的 global 或者 nonlocal 语句 — 对名称的赋值总是进入最内部的作用域。赋值不复制数据 - 它们只是将名称绑定到对象。删除也是如此:语句 del x 从局部作用域引用的名称空间中删除 x 的绑定。事实上,所有引入新名称的操作都使用局部作用域:特别是,import 语句和函数定义将模块或函数名称绑定到局部作用域上。

 

 

 

 

官方文档:

https://docs.python.org/3.9/tutorial/classes.html


文章转载自数据库杂货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论