Python中self的含义到底是什么?

本文深入解析Python中'self'参数的作用及使用场景,包括其在类实例、继承、描述符类中的表现,以及不可省略的原因。通过实例展示'self'如何在不同情况下引用正确的实例。

注:本文为转载,如有侵权,请告知将予以删除

原文链接:https://blog.youkuaiyun.com/xjl85/article/details/76083238

 

 

先来看一个例子:

class Test:
    def prt(self):
        print(self)
        print(self.__class__)

t = Test()
t.prt()

输出结果:

<__main__.Test object at 0x004C5AB0>
<class '__main__.Test'>

可以看出,self代表的是类的实例。而self.class则指向类。

当然,self不必非写成self,也可以写成this…等等,执行结果是一样的。但是按照python 的约定俗成的惯例,还是写成self比较好。

其实,在Python的解释器内部,当我们调用t.prt()时,实际上Python解释成Test.prt(t),也就是说把self替换成类的实例。

class Test:
    def prt(this):
        print(this)
        print(this.__class__)

t = Test()
Test.prt(t)

执行结果:

<__main__.Test object at 0x008AC390>
<class '__main__.Test'>

但self在定义时不可以省略。

class Test:
    def prt():
        print(self)

t = Test()
t.prt()

执行结果报错:

TypeError: prt() takes 0 positional arguments but 1 was given

运行时提醒错误如下:prt在定义时没有参数,但是我们运行时强行传了一个参数。

在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例。

class Parent:
    def pprt(self):
        print(self)

class Child(Parent):
    def cprt(self):
        print(self)
c = Child()
c.cprt()
c.pprt()
p = Parent()
p.pprt()

执行结果:

<__main__.Child object at 0x0057C3F0>
<__main__.Child object at 0x0057C3F0>
<__main__.Parent object at 0x0057CBD0>

运行c.cprt()时应该没有理解问题,指的是Child类的实例。

但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。

在描述符类中,self指的是描述符类的实例

class Desc:
    def __get__(self, ins, cls):
        print('self in Desc: %s ' % self )
        print(self, ins, cls)
class Test:
    x = Desc()
    def prt(self):
        print('self in Test: %s' % self)
t = Test()
t.prt()
t.x

执行结果:

self in Test: <__main__.Test object at 0x0045C3B0>
self in Desc: <__main__.Desc object at 0x0045C310> 
<__main__.Desc object at 0x0045C310> <__main__.Test object at 0x0045C3B0> <class '__main__.Test'>

最后一句t.x,也就是Test类的实例t的属性x,由于实例t中并没有定义属性x,所以找到了类属性x,而该属性是描述符属性,为Desc类的实例而已,所以此处并没有顶用Test的任何方法。

而如果把t.x改为Test.x

执行结果:

self in Test: <__main__.Test object at 0x0044C3B0>
self in Desc: <__main__.Desc object at 0x0044C310> 
<__main__.Desc object at 0x0044C310> None <class '__main__.Test'>

由于在很多时候描述符类中仍然需要知道调用该描述符的实例是谁,所以在描述符类中存在第二个参数ins,用来表示调用它的类实例,所以t.x时可以看到第三行中的运行结果中第二项为

<<main.Test object at 0x0000000002A570B8>

而采用Test.x进行调用时,由于没有实例,所以返回None。

<think>我们之前已经详细解释了`self`的含义,但为了严格遵循用户要求的格式,我们将重新组织一个完整的回答,并按照要求提出相关问题。</think>### `self` 在 Python 中的含义详解 `self` 是 Python 面向对象编程(OOP)中的核心概念,它代表**类实例本身**。以下是详细解释: #### 1. 基本定义 - `self` 是一个指向当前对象实例的引用 - 它是类方法的第一个参数(约定名称为 `self`,非强制) - 用于访问实例的属性和方法 #### 2. 关键作用 ```python class Robot: def __init__(self, name): # ❶ 构造函数 self.name = name # ❷ 创建实例属性 self.battery = 100 def charge(self): # ❸ 实例方法 self.battery = 100 # ❹ 访问实例属性 print(f"{self.name} 已充电") def status(self): return f"{self.name}: 电量 {self.battery}%" ``` ❶ **构造函数**:`__init__` 方法中 `self` 表示新创建的对象 ❷ **创建属性**:`self.name` 将参数绑定到实例 ❸ **方法定义**:所有实例方法第一个参数必须是 `self` ❹ **访问属性**:通过 `self` 访问实例的状态 #### 3. 实际使用示例 ```python # 创建两个独立的机器人实例 robot1 = Robot("R2-D2") robot2 = Robot("C-3PO") # 操作实例 robot1.charge() # 输出: R2-D2 已充电 robot2.battery = 50 # 直接修改属性 print(robot1.status()) # 输出: R2-D2: 电量 100% print(robot2.status()) # 输出: C-3PO: 电量 50% ``` #### 4. `self` 的必要性 当调用方法时,Python 自动传递实例作为 `self`: ```python # 以下两种调用等价 robot1.charge() # 常规调用 Robot.charge(robot1) # 显式传递 self ``` #### 5. 与类变量对比 ```python class Robot: total_robots = 0 # 类变量(所有实例共享) def __init__(self, name): self.name = name # 实例变量(每个实例独立) Robot.total_robots += 1 # 通过类名访问类变量 @classmethod def get_count(cls): # cls 指向类本身 return cls.total_robots print(Robot.get_count()) # 输出: 0 r1 = Robot("Wall-E") r2 = Robot("EVA") print(r1.get_count()) # 输出: 2 (通过实例访问类方法) ``` #### 6. 特殊方法中的 `self` ```python class Vector: def __init__(self, x, y): self.x = x self.y = y # 运算符重载 def __add__(self, other): # self + other return Vector(self.x + other.x, self.y + other.y) # 字符串表示 def __str__(self): return f"({self.x}, {self.y})" v1 = Vector(1, 2) v2 = Vector(3, 4) print(v1 + v2) # 输出: (4, 6) ``` ### 常见误区解析 1. **忘记 `self` 参数**: ```python class Test: def method(): # 错误!缺少 self print("Called") t = Test() t.method() # 报错: method() takes 0 positional arguments but 1 was given ``` 2. **误用类变量**: ```python class Counter: count = 0 def increment(self): self.count += 1 # 错误!实际创建了实例属性 c1 = Counter() c2 = Counter() c1.increment() print(c1.count) # 1 print(c2.count) # 0 (预期应为1) ``` 3. **正确解决方案**: ```python class Counter: count = 0 def increment(self): Counter.count += 1 # 通过类名访问 ``` ### 最佳实践建议 1. **始终使用 `self` 访问实例成员** 2. **类变量使用类名访问**(如 `ClassName.variable`) 3. **避免在方法中修改 `self` 的引用**(`self = ...` 通常错误) 4. **使用类型提示提高可读性**: ```python class Robot: def __init__(self, name: str) -> None: self.name: str = name self.battery: int = 100 ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值