Python面向对象:__init__构造方法的参数与执行
一、开篇:对象诞生的初始化仪式
__init__是Python中最常写的"魔法方法"。每当创建一个类的实例时,Python自动调用它来初始化对象的属性。它被称为"初始化方法"——不是"构造函数"(那是__new__的职责)。
⌨️ 先看最基本的:
classUser:def__init__(self,username,email,age=18):"""初始化新用户——设置初始属性"""self.username=username self.email=email self.age=age self.is_active=True# 默认值,不需要从参数传入self.login_count=0# 初始化为0# 创建实例时自动调用__init__user=User("zhangsan","zs@test.com",age=25)print(user.username)# zhangsanprint(user.age)# 25print(user.is_active)# True二、__init__的规则详解
2.1 参数传递和self
classProduct:"""商品类——展示__init__的参数用法"""def__init__(self,name,price,category="通用",*,discount=0,stock=0):""" name: 商品名(必填) price: 价格(必填) category: 分类(可选,默认"通用") discount: 折扣(关键字专用参数) stock: 库存(关键字专用参数) """self.name=name self.price=price self.category=category self.discount=discount self.stock=stock# 在__init__中进行参数验证ifprice<=0:raiseValueError("价格必须大于0")ifdiscount<0ordiscount>100:raiseValueError("折扣必须在0-100之间")defget_final_price(self):"""计算折后价"""returnself.price*(1-self.discount/100)# 各种创建方式p1=Product("笔记本电脑",5999)p2=Product("鼠标",199,category="外设",stock=50)p3=Product("键盘",899,discount=15,stock=30)print(f"p1:{p1.name}¥{p1.get_final_price():.0f}")print(f"p3:{p3.name}¥{p3.get_final_price():.0f}")2.2 __init__的返回值
# ⚠️ __init__必须返回None!# 如果返回其他值会报错classDemo:def__init__(self):# return 42 # TypeError: __init__() should return Nonepass# __init__不是用来返回值的# 它的职责只是初始化self的属性# 创建实例的"返回值"由__new__控制,默认就是self2.3 没有__init__的类
# 不是所有类都需要__init__classMathUtils:"""工具类——全是静态方法,不需要__init__"""@staticmethoddefadd(a,b):returna+b@staticmethoddefmultiply(a,b):returna*bprint(MathUtils.add(3,5))# 8 —— 不需要创建实例classConstants:"""常量类——只存数据"""PI=3.14159E=2.71828print(Constants.PI)# 3.14159 —— 不需要__init__三、__init__中的输入验证
importreclassUser:"""用户类——在__init__中做输入验证"""def__init__(self,username,email,password,age):# 1. 验证用户名ifnotusernameorlen(username.strip())<3:raiseValueError("用户名至少需要3个字符")ifnotusername.isalnum():raiseValueError("用户名只能包含字母和数字")# 2. 验证邮箱email_pattern=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'ifnotre.match(email_pattern,email):raiseValueError(f"无效的邮箱格式:{email}")# 3. 验证密码强度iflen(password)<8:raiseValueError("密码至少需要8个字符")# 4. 验证年龄ifnotisinstance(age,int)orage<0orage>150:raiseValueError(f"无效的年龄:{age}")# 验证通过——设置属性self.username=username.strip()self.email=email.lower()self.password=password# 实际项目中应该哈希存储self.age=age# 测试try:user=User("张三","not-an-email","123",-5)exceptValueErrorase:print(f"创建失败:{e}")try:user=User("zhangsan","zs@test.com","secure_password",25)print(f"创建成功:{user.username}")exceptValueErrorase:print(f"创建失败:{e}")四、常见模式和最佳实践
4.1 提供多种构造方式
fromdatetimeimportdatetimeclassEvent:"""事件类——支持多种创建方式"""def__init__(self,title,date,description=""):self.title=title self.date=date self.description=description self.created_at=datetime.now()@classmethoddeffrom_string(cls,event_string):"""从字符串创建:'标题,2024-06-15,描述'"""parts=event_string.split(",")title=parts[0].strip()date=datetime.strptime(parts[1].strip(),"%Y-%m-%d")description=parts[2].strip()iflen(parts)>2else""returncls(title,date,description)@classmethoddeftoday(cls,title,description=""):"""创建今天的事件"""returncls(title,datetime.now(),description)# 多种创建方式e1=Event("会议",datetime(2024,6,15),"项目评审")e2=Event.from_string("团建,2024-07-01,户外烧烤")e3=Event.today("日报","写今日工作总结")print(e1.title,e1.date.date())print(e2.title,e2.date.date())print(e3.title,e3.date.date())4.2 __init__中避免复杂操作
# ⚠️ __init__应该轻量——只做属性设置和基本验证# 不要在__init__中做耗时操作classDatabaseConnection:"""数据库连接——__init__只保存参数,不连接"""def__init__(self,host,port,database):self.host=host self.port=port self.database=database self._connection=None# 延迟连接defconnect(self):"""显式连接——不在__init__中自动连接"""print(f"连接到{self.host}:{self.port}/{self.database}")self._connection="已连接"def__enter__(self):self.connect()returnselfdef__exit__(self,*args):print("断开连接")self._connection=None# 使用withDatabaseConnection("localhost",5432,"mydb")asdb:print("执行查询...")五、总结
__init__是对象初始化的核心方法。它负责将参数转化为实例的初始状态。
💡核心要点:
__init__不是构造函数——__new__才是,__init__是初始化方法- 必须返回None
- 在
__init__中做输入验证——尽早发现错误 - 保持
__init__轻量——避免耗时操作,延迟到需要时 - 使用
@classmethod提供替代构造器——给用户多种创建方式
✅__init__是你定义"如何初始化一个对象"的地方——参数检查、默认值、属性绑定。