解开Python中self的四个秘密

开发 前端
毫无疑问,几乎每一门Python课程都有一个关于(class)类的讲座——面向对象编程语言的基本构建模块之一。

self的谜团

毫无疑问,几乎每一门Python课程都有一个关于(class)类的讲座——面向对象编程语言的基本构建模块之一。

当您通过一些示例学习它时,您将注意到在Python类中定义的许多函数都将self作为它们的第一个参数。例如,在下面的代码片段中,我们声明了一个名为Student的类,它的greet()方法的第一个参数是self。但是,函数根本没有使用self,所以这里的self到底是从哪里来的呢?这对许多初学者来说是第一个谜。

  1. >>> class Student: 
  2. ...     def greet(self, name): 
  3. ...         print('Good Morning, ' + name) 
  4. ...  
  5. >>> student = Student() 
  6. >>> student.greet('John'
  7. Good Morning, John 

同样奇怪的是,当我们使用这个函数时,我们并没有给self参数设置任何东西,这是困扰我们的另一个谜题。在这篇文章中,我们将与学习者分享一些Python中self的奥秘。

[[378103]]

1. 它代表什么?

在开始处理这个谜题之前,我们需要理解两个基本的相关概念:类和实例。当然,解决所有这些谜题需要额外的知识,而不仅仅是类和实例,我将在接下来的讨论中澄清这一点。如果你对这两个概念都很了解,你可以跳过下一段,这段只是对这两个概念的简要概述。

创建Python类就是声明一种新的对象类型,它提供了一种将数据和功能捆绑在一起的机制。在上面的示例中,我们创建了一个名为Student的类,并使用它创建了一个名为Student的学生类型的对象。这个对象被称为student类的实例。此外,类还可以提供通常称为属性的特定功能,例如示例中的greet()函数。我们使用三个内省(introspection)函数(type()、isinstance()和hasattr())来检查相关信息。

  1. >>> type(Student) 
  2. <class 'type'
  3. >>> type(student) 
  4. <class '__main__.Student'
  5. >>> isinstance(student, Student) 
  6. True 
  7. >>> hasattr(Student, 'greet'
  8. True 

我可以简单地告诉您,greet()函数中的self参数是上面示例中的student实例。更一般地说,是实例调用这个函数。以下是支持证据:

  1. >>> class Student: 
  2. ...     def greet(self, name): 
  3. ...         print(id(self)) 
  4. ...         print('Good Morning, ' + name) 
  5. ...  
  6. >>> student = Student() 
  7. >>> student.greet('John'
  8. 4546580944 
  9. Good Morning, John 
  10. >>> id(student) 
  11. 4546580944 

在上面的代码中,我们修改了greet()函数,要求它使用内省id()函数向我们显示self参数的内存地址。如您所见,self参数和实例student是同一个对象,因为它们具有相同的内存地址。

解开Python中self的四个秘密

2. 为什么不需要在函数调用中设置它呢?

继续上一节中展示的示例,当我们使用实例student调用greet()函数时,这个函数通常被称为实例方法——一个对某个类的实例可用的函数。但是,如果我们检查这个属性的类型,就会显示一些不同的东西。

  1. >>> student = Student() 
  2. >>> student.greet 
  3. <bound method Student.greet of <__main__.Student object at 0x10eff5750>> 

如上所述,实例student的greet属性称为绑定方法。具体来说,它被绑定到Student类的greet属性。

为了准确理解这意味着什么,让我们看看下面的代码:

  1. >>> Student.greet(student, 'John'
  2. Good Morning, John 

结合开头的示例,您可能会注意到这段代码中的三件事:

  1. 这个函数的调用者是类Student,而不是实例student。
  2. 在这个调用中设置了self和name参数,这与student调用初始函数时忽略self参数不同。
  3. 两个函数调用都产生了相同的输出。它们本质上用的是同一个函数。

通过实现这些信息,您可能已经猜到在使用实例student调用greet()函数时,幕后发生了什么。

解开Python中self的四个秘密

如上图所示,当实例student调用greet(' John ')方法时,解释器将处理此函数调用,作为类Student将调用者(即实例student)和name参数(即' John ')发送给greet(self, name)函数,该函数打印“Good Morning, John”。

对于感兴趣的读者,这里有几件事要知道,可以帮助你更深入地了解这个谜。当创建一个Python类时,它声明的函数就是这个类的属性(称为函数对象)。换句话说,类“拥有”这些函数。类的实例不会直接实现这些函数。相反,它们将具有与类中实现的相应函数绑定的相同属性(即实例方法)。

3.self是一个关键词吗?

似乎在所有这些已定义的函数中,我们都使用self作为它们的第一个参数。有些人可能错误地认为self是Python为这些用例保留的关键字。然而,事实并非如此。请看下面一个简单的例子:

  1. >>> def=5 
  2.   File "<stdin>", line 1 
  3.     def=5 
  4.        ^ 
  5. SyntaxError: invalid syntax 
  6. >>> class=4 
  7.   File "<stdin>", line 1 
  8.     class=4 
  9.         ^ 
  10. SyntaxError: invalid syntax 
  11. >>> self=3 

你可能知道,def和class是Python中的关键字,我们不能用它们作为变量名。然而,我们可以在定义函数的上下文之外使用self作为变量名,这表明它在Python中不是保留关键字。

解开Python中self的四个秘密

4. 我们必须在这些函数声明中使用self吗?

在上面的例子中,我们重复引用了greet()函数。正如我们已经讨论过的,我们将这个函数实现为一个实例方法,这样它就可以被这个Student类的所有实例使用。在这种情况下,self是必需的。下面是一些证据:

  1. >>> class Teacher: 
  2. ...     def say_hello(name): 
  3. ...         print('Hello, ' + name) 
  4. ... 
  5. >>> teacher = Teacher() 
  6. >>> teacher.say_hello('John'
  7. Traceback (most recent call last): 
  8.   File "<stdin>", line 1, in <module> 
  9. TypeError: say_hello() takes 1 positional argument but 2 were given 

这里有一些分析。如前所述,当实例teacher调用say_hello()方法时,发生的事情是执行teacher .say_hello()函数,并将实例对象teacher和' John '设置为函数调用。这就是为什么错误说“2是给定的。这与函数的定义相反,函数的定义只有一个参数(name)。

然而,还有两件事与这个谜有关,你可能想知道:

  • 虽然声明实例方法需要包含self参数,但它不必命名为self。在这个场景中使用这个名称只是每个Python程序员都能欣赏的一种约定。下面是一个例子,它可以被命名为其他东西而不会引起任何问题。尽管它在语法上是正确的,但不推荐使用,因为它只会让其他Python程序员感到困惑:
  1. >>> class Teacher: 
  2. ...     def say_hello(professor, name): 
  3. ...         print('Hello, ' + name) 
  4. ... 
  5. >>> teacher = Teacher() 
  6. >>> teacher.say_hello('John'
  7. Hello, John 
  • 在声明其他函数(如类和静态方法)时,不需要使用self参数。对类和静态方法的清晰解释将是以后文章的主题。但我在这里可以展示的是,当我们声明一个类方法时,函数确实有一些类似于在实例方法中使用self的东西,它通常被称为cls,引用类对象本身。它与具体实例无关。下面是一个例子:
  1. >>> class Student: 
  2. ...     def __init__(self, name): 
  3. ...         self.name = name 
  4. ...     @classmethod 
  5. ...     def with_names(cls, first_name, last_name): 
  6. ...         return cls(first_name + ' ' + last_name) 
  7. ...  
  8. >>> student = Student.with_names('John''Smith'
  9. >>> student.name 
  10. 'John Smith' 

英文原文:

Unlock the 4 Mysteries of self in Python | by Yong Cui | Better Programming | Medium

 

 

 

责任编辑:张燕妮 来源: 今日头条
相关推荐

2013-04-11 12:56:14

费道明HTML

2022-05-04 12:44:57

Python编程语言

2013-10-22 09:30:22

2022-01-12 15:50:24

JavaScript开发循环

2020-08-13 10:29:55

项目管理项目经理CIO

2021-08-14 09:48:02

ReentrantLock多线编程

2011-07-14 15:23:34

java

2013-03-18 13:31:28

2022-02-23 15:09:18

数字化转型国有企业数据

2010-03-30 11:00:46

Oracle 数据

2023-10-26 07:47:35

JavaScript代码变量

2023-08-21 13:39:57

开发桌面Ubuntu

2010-06-29 09:06:39

Java思想Java虚拟机

2011-05-18 09:32:14

java

2022-10-08 15:32:24

Python开发技巧

2020-06-04 08:15:53

Kubernetes容器PaaS

2022-06-27 23:31:01

JavaScript框架开发

2022-10-26 14:55:53

AIoT物联网人工智能

2016-11-04 13:27:25

AWS安全性失误

2021-08-24 10:51:19

多云云计算云平台
点赞
收藏

51CTO技术栈公众号