当前位置: 首页 > news >正文

【Python中的self】Python中的`self`:从基础到进阶的实战指南

Python中的self

  • 一、简介
  • 二、是不是关键字?哦,其实它根本不是关键字
  • 三、self到底是什么?它的本质是什么?
  • 四、那self还能怎么用?这些进阶玩法你得知道
    • 4.1. 支持链式调用的方法设计
    • 4.2. 动态管理实例属性
    • 4.3. 在继承中正确使用self
  • 五、实战案例:状态机的实现
  • 六、常见陷阱提醒
    • 6.1. 忘记在方法中加self
    • 6.2. 在类方法中误用self
    • 6.3. 不推荐的方式访问类变量

一、简介

刚开始学Python的时候,你可能遇到过这样的错误提示:

TypeError: missing 1 required positional argument: 'self'

一脸懵?很正常。或者你也好奇过,为什么我们写方法的时候要写成def method(self, …),但调用的时候却直接是instance.method(),不需要传self?今天我们就来聊聊这个既熟悉又陌生的关键词 —— self

二、是不是关键字?哦,其实它根本不是关键字

你可能会惊讶,其实self并不是Python的关键字,也不是语法层面的硬性规定。它只是一个约定俗成的名字,大家都这么叫,所以我们也跟着这么写。

你可以把它换成别的名字,比如thisme,甚至……banana,代码一样能跑!

class MyClass:def my_method(banana, x, y):return banana.value + x + ydef __init__(banana, initial_value=0):banana.value = initial_value

上面这段代码虽然看起来有点离谱,但确实完全合法。不过别真这么干哈~
使用self已经成为Python社区的共识,大家看到它就知道这是实例本身。遵循这个习惯,别人读你的代码也会轻松很多。

三、self到底是什么?它的本质是什么?

简单来说,self就是对当前对象的一个引用。当你调用一个实例方法时,Python会自动把当前对象作为第一个参数传进去。

举个例子你就明白了:

class Counter:count = 0# 类变量def increment(self):self.count += 1# 这里实际上创建了实例变量return self.count@classmethoddef class_increment(cls):cls.count += 1# 修改的是类变量return cls.count

这里有个容易踩坑的地方:如果你调用increment()方法,它并不会修改类变量count,而是会在实例中创建一个新的count属性。
为什么?因为self.count += 1这个操作会让Python先去实例自己的命名空间找count,找不到再去类里找。一旦赋值,就会在实例中新建一个同名变量。
所以如果你想改的是类变量,最好显式地写成Counter.count += 1,这样就不会出错了。

四、那self还能怎么用?这些进阶玩法你得知道

4.1. 支持链式调用的方法设计

有时候我们会看到这种写法:

TextProcessor("Hello").append(", ").replace("Hello", "Hi").upper()

这种写法之所以能成立,是因为每个方法都返回了self。通过返回自身,就可以连续调用多个方法。

下面是一个简单的实现示例:

class TextProcessor:def __init__(self, text=""):self.text = textdef append(self, more_text):self.text += more_textreturn selfdef replace(self, old, new):self.text = self.text.replace(old, new)return selfdef upper(self):self.text = self.text.upper()return selfdef __str__(self):return self.textresult = TextProcessor("Hello").append(", ").append("World!").replace("World", "Python").upper()
print(result)  # 输出: HELLO, PYTHON!

这种模式在像pandas或SQLAlchemy这类库中非常常见,是一种构建流畅API的好方法。

4.2. 动态管理实例属性

有时候我们希望动态地给对象添加属性,这时候self就能派上大用场了。

来看一个例子:

class DynamicObject:def __init__(self, **kwargs):for key, value in kwargs.items():setattr(self, key, value)def update(self, **kwargs):for key, value in kwargs.items():setattr(self, key, value)return selfdef has_attribute(self, attr_name):return hasattr(self, attr_name)def get_all_attributes(self):# 只返回自定义的属性,过滤掉内置内容return {key: value for key, value in self.__dict__.items() if not key.startswith('__')}

这样我们就可以灵活地创建带各种属性的对象,比如:

user = DynamicObject(name="张三", age=30)
user.update(email="zhangsan@example.com", role="admin")
print(user.get_all_attributes())
# 输出: {'name': '张三', 'age': 30, 'email': 'zhangsan@example.com', 'role': 'admin'}

4.3. 在继承中正确使用self

继承是面向对象编程的重要特性之一,在子类中使用self时也要注意一些细节。

来看一个例子:

class Parent:def identify(self):return f"I am a {self.__class__.__name__}"def parent_method(self):return "This is a parent method"class Child(Parent):def child_method(self):return f"This is a child method, and {self.parent_method()}"

当我们分别创建父类和子类的实例并调用 == identify() ==方法时:

p = Parent()
c = Child()print(p.identify())  # 输出: I am a Parent
print(c.identify())  # 输出: I am a Child
print(c.child_method())  # 输出: This is a child method, and This is a parent method

你会发现,即使是父类定义的方法,当被子类调用时,self指向的依然是子类的实例。这也是多态的一种体现。

五、实战案例:状态机的实现

为了让大家更好地理解self的实际应用,我们来看一个稍微复杂点的例子 —— 状态机。
这个状态机可以用于跟踪工单处理流程的状态转换:

class StateMachine:def __init__(self, initial_state):self.state = initial_stateself.transitions = {}self.callbacks = {}def add_transition(self, source, event, target, callback=None):if source not in self.transitions:self.transitions[source] = {}self.transitions[source][event] = targetif callback:if source not in self.callbacks:self.callbacks[source] = {}self.callbacks[source][event] = callbackreturn selfdef trigger(self, event, *args, **kwargs):if self.state not in self.transitions or event not in self.transitions[self.state]:raise ValueError(f"无法从状态 '{self.state}' 通过事件 '{event}' 进行转换")if self.state in self.callbacks and event in self.callbacks[self.state]:self.callbacks[self.state][event](self, *args, **kwargs)old_state = self.stateself.state = self.transitions[self.state][event]print(f"状态从 '{old_state}' 转换到 '{self.state}'")return self

然后我们可以用它来模拟一个工单流程:

def notify_customer(machine, message=None):print(f"通知客户:您的工单已被处理,当前状态:{machine.state}")if message:print(f"附加信息:{message}")ticket = StateMachine("新建")
ticket.add_transition("新建", "分配", "待处理") \.add_transition("待处理", "开始处理", "处理中") \.add_transition("处理中", "解决", "已解决", notify_customer) \.add_transition("已解决", "关闭", "已关闭") \.add_transition("已解决", "重新打开", "待处理")# 模拟流程
ticket.trigger("分配") \.trigger("开始处理") \.trigger("解决", message="问题已修复,请确认")

在这个例子中,我们巧妙地利用了self来实现:

  • 方法链式调用(通过返回self)
  • 回调函数中传递self,以便回调访问状态机的数据和方法
  • 动态管理状态和回调逻辑

六、常见陷阱提醒

在使用self的过程中,有一些常见的“坑”,新手和老手都有可能踩到:

6.1. 忘记在方法中加self

这是最常见的错误之一:

class MyClass:def get_value():  # 缺少self参数return self.value

运行时会抛出TypeError,因为它没有收到self参数。

6.2. 在类方法中误用self

@classmethod
def increment_counter(self):  # 应该用clsself.counter += 1

虽然也能运行,但不符合规范,容易引起混淆。

6.3. 不推荐的方式访问类变量

def __init__(self, timeout=None):if timeout is None:self.timeout = self.default_timeout  # 不推荐

更好的做法是:

self.timeout = Config.default_timeout  # 明确指定类名

这样即使有子类覆盖了default_timeout,也不会出错。

http://www.xdnf.cn/news/675415.html

相关文章:

  • roo code调用手搓mcp server
  • Python filter()函数详解:数据筛选的精密过滤器
  • 在promise中,多个then如何传值
  • sqli_labs第二十九/三十/三十一关——hpp注入
  • 《计算机组成原理》第 6 章 - 计算机的运算方法
  • 大模型的参数高效微调;大模型的对齐
  • Linux显示进程状态——ps命令详解与实战
  • 用C#最小二乘法拟合圆形,计算圆心和半径
  • chrome打不开axure设计的软件产品原型问题解决办法
  • 尚硅谷redis7 41-46 redis持久化之AOF异常恢复演示
  • 从零开始理解机器学习:知识体系 + 核心术语详解
  • 从中控屏看HMI设计的安全与美学博弈
  • FileZillaServer(1) -- 记录
  • Git 克隆别人的远程仓库以后,推到自己的远程仓库
  • BSRN地表基准辐射网数据批量下载
  • SQL基础教程:第一章与第二章内容总结(新手入门指南)
  • 文档注释:删还是不删
  • 关于 smali:3. Smali 与 APK 结构理解
  • LWIP 中,lwip_shutdown 和 lwip_close 区别
  • 深入剖析Java CompletableFuture:原理、陷阱与高并发场景优化指南
  • R语言基础| 可视化初探(ggplot2)
  • 预测式外呼与自动外呼的区别
  • 【博客系统】博客系统第十弹:实现对数据库存储的用户密码进行加密功能、更新登录接口的密码校验功能
  • 【监控】pushgateway中间服务组件
  • openresty+lua+redis把非正常访问的域名加入黑名单
  • threejs顶点UV坐标、纹理贴图
  • SQL Server 和 MySQL 对比
  • 实现单例模式的6种方法(Python)
  • 开源多模态新标杆——BAGEL本地部署教程:7B参数撬动万亿数据
  • 《算法和数据结构》算法篇