如何修改__new__函数调用不可变类型类方法的父类方法?DateTime定制咨询
嘿,咱们一步步拆解你的问题,解决不可变类型的__new__用法,还有定制DateTime的需求~
一、修改__new__调用不可变类型的父类方法
不可变类型(比如datetime.datetime、tuple、str)的实例创建逻辑全在__new__里——毕竟实例一旦创建就没法修改,__init__只能做做无关痛痒的操作。所以子类化时,你的__new__得先处理自己的逻辑,再委托父类的__new__生成实例。
举个核心示例,比如子类化datetime.datetime:
from datetime import datetime class RestrictedDateTime(datetime): def __new__(cls, date_str): # 第一步:验证并转换参数(只接受"%Y-%m-%d %H:%M:%S"格式) try: parsed_dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") except ValueError: raise ValueError("必须传入格式为 YYYY-MM-DD HH:MM:SS 的字符串!") # 第二步:调用父类的__new__创建实例——不可变类型必须靠父类生成实例 return super().__new__( cls, parsed_dt.year, parsed_dt.month, parsed_dt.day, parsed_dt.hour, parsed_dt.minute, parsed_dt.second )
这里的关键是:必须用super().__new__(cls, ...)传递父类需要的原始参数,不能自己凭空造不可变实例。父类的__new__会返回一个子类的实例,你直接返回它就行。
二、实现符合要求的DateTime类
要满足「构造仅接受特定格式字符串、str输出默认该格式」的需求,只需要在上面的基础上重写__str__:
def __str__(self): # 强制输出指定格式 return self.strftime("%Y-%m-%d %H:%M:%S")
这样当你print(RestrictedDateTime("2024-05-20 14:30:00"))时,就会输出2024-05-20 14:30:00,完美符合要求。
三、子类化不可变类型是不是最佳方案?
这得看你的具体需求,咱们对比两种思路:
1. 子类化(推荐,如果你需要完整的DateTime功能)
优点:自动继承datetime的所有方法(比如timedelta加减、比较、strftime自定义其他格式等),不用自己重复造轮子。
但有个坑:datetime的类方法(比如now()、fromtimestamp())会直接调用子类的__new__,而你的__new__只接受字符串参数,直接调用会报错!解决办法是重写这些类方法:
@classmethod def now(cls): # 获取当前时间后转成指定格式字符串,再创建实例 current_dt = datetime.now() return cls(current_dt.strftime("%Y-%m-%d %H:%M:%S")) @classmethod def fromtimestamp(cls, timestamp): dt = datetime.fromtimestamp(timestamp) return cls(dt.strftime("%Y-%m-%d %H:%M:%S"))
把你需要用到的类方法都重写一遍,就能完美兼容了。
2. 组合(更安全,但代码繁琐)
如果你担心子类化会暴露一些不符合要求的创建方式(比如有人直接调用RestrictedDateTime(2024,5,20)),可以用组合:创建一个类,内部封装一个datetime实例,只暴露你需要的方法。
示例核心代码:
class RestrictedDateTime: def __init__(self, date_str): try: self._dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") except ValueError: raise ValueError("必须传入格式为 YYYY-MM-DD HH:MM:SS 的字符串!") def __str__(self): return self._dt.strftime("%Y-%m-%d %H:%M:%S") # 代理datetime的方法,比如加减时间 def __add__(self, other): result_dt = self._dt + other return RestrictedDateTime(result_dt.strftime("%Y-%m-%d %H:%M:%S")) # 代理属性,比如year、month @property def year(self): return self._dt.year
这种方式完全控制实例的创建逻辑,但缺点是需要手动代理所有你需要的方法和属性,代码量会大很多。
总结
- 如果你需要完整的
datetime功能,子类化是最佳方案,只要处理好类方法的重写即可; - 如果你只需要部分功能且追求绝对安全,再考虑组合方式;
- 不可变类型的
__new__核心逻辑就是:先处理参数,再委托父类创建实例并返回。
内容的提问来源于stack exchange,提问作者tharple

