Python类初始化传入列表报错,如何复用外部build函数?
问题分析与解决方案
为什么你的代码会报错?
核心原因是在Python的__init__方法中给self赋值不会改变已经创建的实例。当你调用Test([1,2,3])时,Python首先会调用__new__方法创建一个Test类的实例,然后才会调用__init__来初始化这个实例。此时self是指向这个新创建实例的局部变量,你执行self = build(x)只是让这个局部变量指向了build函数返回的另一个Test实例,但原来那个被__new__创建的实例并没有被修改——它依然没有val和next属性,所以当你尝试打印它时就会报错。
简单说,__init__的作用是初始化已存在的实例,而不是创建实例。你不能通过给self赋值来替换掉已经创建好的实例。
正确的实现方式
方式1:使用类方法(推荐)
把build函数改成类方法,作为Test类的替代构造函数,同时让__init__专注于单个整数的初始化逻辑。这样既复用了build的逻辑,又符合Python的面向对象设计规范:
class Test(): def __init__(self, x): # 只处理单个整数的情况 self.val = x self.next = None def __str__(self): node = self test_str = '' while node: test_str += str(node.val) node = node.next return test_str @classmethod def from_list(cls, array: list) -> 'Test': # 把原来的build逻辑改成类方法 root = cls(array.pop(0)) cur = root while array: cur.next = cls(array.pop(0)) cur = cur.next return root # 使用方式 b = Test.from_list([1,2,3]) print(b) # 输出123 b = Test(5) print(b) # 输出5
方式2:支持Test([1,2,3])直接调用(重写__new__)
如果你希望保留Test([1,2,3])这种调用方式,可以重写__new__方法——它是Python中负责创建实例的方法,在__init__之前执行:
class Test(): def __new__(cls, x): if isinstance(x, list): # 如果是列表,调用类方法创建实例并返回 return cls.from_list(x) # 如果是整数,创建普通实例 return super().__new__(cls) def __init__(self, x): if isinstance(x, int): self.val = x self.next = None def __str__(self): node = self test_str = '' while node: test_str += str(node.val) node = node.next return test_str @classmethod def from_list(cls, array: list) -> 'Test': root = cls(array.pop(0)) cur = root while array: cur.next = cls(array.pop(0)) cur = cur.next return root # 现在两种调用方式都支持 b = Test([1,2,3]) print(b) # 输出123 b = Test(5) print(b) # 输出5
方式3:在__init__中复用现有build函数(不复制代码)
如果你不想修改原有的build函数,也可以把build返回的实例属性复制到当前实例上(这种方式会额外创建一个实例,仅作为临时解决方案):
def build(array: list) -> Test: root = Test(array.pop(0)) cur = root while array: cur.next = Test(array.pop(0)) cur = cur.next return root class Test(): def __init__(self,x): if isinstance(x, int): self.val = x self.next = None elif isinstance(x, list): # 调用build创建实例,复制其属性到当前self built_instance = build(x) self.val = built_instance.val self.next = built_instance.next def __str__(self): node = self test_str = '' while(node): test_str += str(node.val) node = node.next return test_str b = Test([1,2,3]) print(b) # 输出123
补充提示
尽量用isinstance(x, int)替代type(x) == int,因为isinstance支持继承场景——如果有子类继承int,isinstance依然能正确识别,而type则不行。
内容的提问来源于stack exchange,提问作者Haoming Jin
相关产品推荐
相关产品推荐

