Python类(class)中self的理解

简介: Python类(class)中self的理解
0. 前言

按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解,但是内容不乏不准确的地方,希望批评指正,共同进步。


对于Python新手(甚至部分老手)而言,在面向对象编程类(class)中,都有一个比较困扰的参数——self。


因此写作本文加强自己的理解,也帮助别人理解。


1. 从一个问题入手理解"self"

下面是一段关于class的python代码,在这段代码中完全没有"self"这个参数,请思考下这段代码中的4个"print"哪个是可以运行的,哪个会报错?


答案后面揭晓。


class test():
    def __init__(x):
        x.ten = 10

    def multi_ten(y, z):
        return z*y.ten

    def divide_ten(w, v):
        return v/w.ten

    def hello(k):
        return 'hello'

    def world():
        return 'world'

TEST = test()

print(TEST.multi_ten(9))
print(TEST.divide_ten(100))
print(TEST.hello())
print(TEST.world())
2. "self"的定义


Python规定在类(class)的方法(method)中,都必须有一个形参(parameter),而且必须是第一个形参,用于传递类的实例(instance)。而这个形参,一般约定熟成取名为"self"。


类中的函数称为方法,例如上面的multi_ten()、divide_ten()这些都是方法。在Pycharm中,在类中编写方法的时候也会自动跳出“self”这个参数。


拿上面的例子来说,在multi_ten()这个方法中,第一个形参(parameter)是"y",在test()类实例化后,它传递的实参(argument)是"TEST"实例(instance),所以这里的"y"就相当于"self"。


因为"TEST"这个实参已经传入multi_ten()这个方法中,所以"TEST"实例可以调用multi_ten(),即TEST.multi_ten()。


3. __init__()方法

__init__()本质上是一个方法,所以其实和"self"(形参)关系不大,为什么经常把这两者联系到一起呢?


因为__init__()是一个特殊的方法,每次创建类的新实例时__init__()都会自动运行,这样就可以在__init__()方法中定义以"self"为前缀的变量,例如上面的x.ten,供类中的所有方法使用。


4. 问题解析

回到最开始的问题,通过对"self"的理解,可以知道前3个print是可以运行的,最后一个会报错。

C:\Users\Lenovo\Desktop\DL\Pytest\Scripts\python.exe C:/Users/Lenovo/Desktop/DL/Pytest/test_main.py
90
10.0
hello
Traceback (most recent call last):
  File "C:\Users\Lenovo\Desktop\DL\Pytest\test_main.py", line 22, in <module>
    print(TEST.world())
TypeError: world() takes 0 positional arguments but 1 was given

Process finished with exit code 1

而其报错的原因“TypeError: world() takes 0 positional arguments but 1 was given”就是再说:在定义world()这个方法的时候,没有填位置形参(positional parameter),调用的时候应该也是给0个位置实参(positional argument),但是实际使用却给了一个位置实参(positional argument)——“TEST”,所以会报错。

5. 结语

最后再说明一下,虽然Python的类在"self"的上可以用其他变量替代,但是这种操作都是极其不推荐的,无论新手老手,在任何情况下,在"self"的位置上都应该老老实实地写成"self"。上面那段代码应该这样写:

class test():
    def __init__(self):
        self.ten = 10

    def multi_ten(self, z):
        return z*self.ten

    def divide_ten(self, v):
        return v/self.ten

    def hello(self):
        return 'hello'

    def world(self):
        return 'world'

TEST = test()

print(TEST.multi_ten(9))
print(TEST.divide_ten(100))
print(TEST.hello())
print(TEST.world())


相关文章
|
14天前
|
缓存 监控 程序员
Python中的装饰器是一种特殊类型的声明,它允许程序员在不修改原有函数或类代码的基础上,通过在函数定义前添加额外的逻辑来增强或修改其行为。
【6月更文挑战第30天】Python装饰器是无侵入性地增强函数行为的工具,它们是接收函数并返回新函数的可调用对象。通过`@decorator`语法,可以在不修改原函数代码的情况下,添加如日志、性能监控等功能。装饰器促进代码复用、模块化,并保持源代码整洁。例如,`timer_decorator`能测量函数运行时间,展示其灵活性。
18 0
|
11天前
|
存储 JSON 测试技术
python中json和类对象的相互转化
针对python中类对象和json的相关转化问题, 本文介绍了4种方式,涉及了三个非常强大的python库jsonpickle、attrs和cattrs、pydantic,但是这些库的功能并未涉及太深。在工作中,遇到实际的问题时,可以根据这几种方法,灵活选取。 再回到结构化测试数据的构造,当需要对数据进行建模时,也就是赋予数据业务含义,pydantic应该是首选,目前(2024.7.1)来看,pydantic的生态非常活跃,各种基于pydantic的工具也非常多,建议尝试。
|
1天前
|
Python
`scipy.signal`模块是SciPy库中的一个子模块,它提供了信号处理、滤波、频谱分析等功能。这个模块包含了许多用于信号处理的函数和类,其中`butter()`和`filtfilt()`是两个常用的函数。
`scipy.signal`模块是SciPy库中的一个子模块,它提供了信号处理、滤波、频谱分析等功能。这个模块包含了许多用于信号处理的函数和类,其中`butter()`和`filtfilt()`是两个常用的函数。
5 0
|
4天前
|
Python
关于Python的类的一些理解
关于Python的类的一些理解
|
4天前
|
存储 算法 索引
1124. 表现良好的最长时间段 (python) 前缀和 分类讨论 最大长度 力扣 面试题
1124. 表现良好的最长时间段 (python) 前缀和 分类讨论 最大长度 力扣 面试题
|
8天前
|
设计模式 Python
深度揭秘!Python元类:掌握它,让你的代码拥有创造类的能力
【7月更文挑战第6天】Python元类探秘:**元类是类的类,用于控制类的创建。通过定义元类,可自定义类的行为,如动态添加方法或改变继承结构。示例中,`my_metaclass`在创建类时添加`new_method`。元类强大且适用于高级编程,如动态修改、注册类或实现设计模式。理解并善用元类能提升Python编程技巧。
16 0
|
18天前
|
Python
经验大分享:python类函数,实例函数,静态函数
经验大分享:python类函数,实例函数,静态函数
12 0
|
应用服务中间件 开发者 Python
self的使用 | 手把手教你入门Python之五十二
通过 self.属性名 可以访问到这个对象的属性;通过 self.方法名() 可以调用这个对象的方法。
self的使用 | 手把手教你入门Python之五十二
|
5天前
|
安全 Python
告别低效编程!Python线程与进程并发技术详解,让你的代码飞起来!
【7月更文挑战第9天】Python并发编程提升效率:**理解并发与并行,线程借助`threading`模块处理IO密集型任务,受限于GIL;进程用`multiprocessing`实现并行,绕过GIL限制。示例展示线程和进程创建及同步。选择合适模型,注意线程安全,利用多核,优化性能,实现高效并发编程。
18 3
|
7天前
|
开发者 Python
Python元类实战:打造你的专属编程魔法,让代码随心所欲变化
【7月更文挑战第7天】Python的元类是编程的变形师,用于创建类的“类”,赋予代码在构建时的变形能力。
30 1