如何以Pythonic方式实现Python类构造器:传入自身实例时直接返回该实例
如何以Pythonic方式实现Python类构造器:传入自身实例时直接返回该实例
要解决这个问题,我们需要让MyIndex的构造器支持传入自身实例时直接返回原实例(类似pathlib.Path的行为),同时保留原有的两种初始化方式。原代码的问题在于,当传入MyIndex实例时,构造器会尝试对该实例调用split()方法,导致AttributeError。
解决方案:重写__new__方法处理实例复用
Python的__new__方法负责创建类的实例,它在__init__之前执行。我们可以在__new__中检查传入的参数是否为MyIndex实例,如果是则直接返回它,否则正常创建新实例并初始化。
修改后的完整代码如下:
from typing import Union, Dict, Any class MyIndex(object): def __new__( cls, string: Union[str, 'MyIndex'] = None, letter: str = None, number: int = None, ): # 若传入的是MyIndex实例,直接返回原实例,避免创建新对象 if isinstance(string, MyIndex): return string # 否则创建新实例 return super().__new__(cls) def __init__( self, string: Union[str, 'MyIndex'] = None, letter: str = None, number: int = None, ): # 当__new__返回原实例时,__init__不会被执行,无需处理MyIndex实例的情况 if string is not None: self.letter, self.number = string.split() else: self.letter, self.number = letter, number def __repr__(self): return f"MyIndex(letter='{self.letter}', number={self.number})" def __str__(self): return f"{self.letter}{self.number}" # 实现哈希和相等判断,确保实例可作为字典键 def __hash__(self): return hash((self.letter, self.number)) def __eq__(self, other): if not isinstance(other, MyIndex): return False return self.letter == other.letter and self.number == other.number # 基础功能测试 i1 = MyIndex("A 1") print(i1) # 输出: A1 i2 = MyIndex(letter='B', number=2) print(i2) # 输出: B2 # 传入MyIndex实例的测试 i3 = MyIndex(i2) print(i3 is i2) # 输出: True(返回原实例) print(i3) # 输出: B2 # 测试use_index函数 def use_index( index_dict: Dict[MyIndex, Any], index: Union[str, MyIndex], ): index = MyIndex(index) return index_dict[index] test_dict = {i1: "value1", i2: "value2"} print(use_index(test_dict, "A 1")) # 输出: value1 print(use_index(test_dict, i2)) # 输出: value2
关键说明:
__new__方法的核心作用:- 当传入的
string参数是MyIndex实例时,直接返回该实例,不创建新对象,完全对齐pathlib.Path的行为。 - 若参数不是
MyIndex实例,调用父类的__new__创建新实例,后续__init__会正常执行初始化逻辑。
- 当传入的
类型提示与兼容性:
- 将
string参数的类型标注为Union[str, 'MyIndex'],明确支持接受字符串或自身实例作为输入。 - 实现
__hash__和__eq__方法,确保MyIndex实例可以安全地作为字典的键(自定义类作为字典键时,这两个方法是规范要求)。
- 将
use_index函数的优化:- 重命名参数
dict为index_dict(dict是Python内置类型名,不建议用作变量名)。 - 现在函数可以无缝处理字符串或
MyIndex实例输入,MyIndex(index)会自动适配两种场景。
- 重命名参数
备选方案(不推荐):
如果不需要严格返回原实例,只是想让初始化不报错(创建新实例并复制属性),可以在__init__中添加判断:
def __init__( self, string: Union[str, 'MyIndex'] = None, letter: str = None, number: int = None, ): if isinstance(string, MyIndex): self.letter = string.letter self.number = string.number elif string is not None: self.letter, self.number = string.split() else: self.letter, self.number = letter, number
但这种方式会创建新的MyIndex实例,和pathlib.Path的行为不符,因此更推荐使用__new__的实现方式。
备注:内容来源于stack exchange,提问作者asimov
相关产品推荐
相关产品推荐

