如何派生内置不可变类型并修改其实例化行为

高级编程技巧 学习笔记

一、如何派生内置不可变类型并修改其实例化行为


1.1、问题

        我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素,例如:IntTuple([2,-2,'jr',['x','y'],4]) => (2,4)

        如何继承内置 tuple 实现 IntTuple ?

1.2、解决

  1. 创建 IntTuple 类继承 tuple ,通过 for 循环实现
class IntTuple(tuple):
    def __init__(self, iterable):
        for i in iterable:
            if isinstance(i, int) and i > 0:
                super().__init__(i)

int_t = IntTuple([2, -2, 'li', ['x', 'y'], 4])
print(int_t)

# 报错 TypeError: object.__init__() takes exactly one argument (the instance to initialize)

有问题,object 类出错,说明代码存在问题,打印 self 看看

class IntTuple(tuple):
    def __init__(self, iterable):
        # for i in iterable:
        #     if isinstance(i, int) and i > 0:
        #         super().__init__(i)
        print(self)

int_t = IntTuple([2, -2, 'li', ['x', 'y'], 4])
print(int_t)

# 输出
(2, -2, 'li', ['x', 'y'], 4)
(2, -2, 'li', ['x', 'y'], 4)

输出的 self 是一个元组对象,说明在执行 __init__ 方法之前就已经创建好 self 对象

所以,上面的报错是代码顺序存在问题

  1. 创建对象的是 __new__ 方法

        __new__ 方法的第一个参数是这个类,而其余的参数会在调用成功后全部传递给__init__方法初始化,所以,__new__ 方法(第一个执行)先于 __init__ 方法执行;若没有返回值,则 __init__ 方法也不会执行:

class A(object):
    def __new__(cls, *args, **kwargs):
        print("A.__new__", cls, args)
        # return super.__new__(cls)			# 取消注释观察
        # return object.__new__(cls)

    def __init__(self, *args):
        print("A.__init__")


a = A(1,2)

# 是如何执行的?(下面两句相当于上面一句)
a = A.__new__(A, 1, 2)
A.__init__(a, 1, 2)

        我们比较两个方法的参数,可以发现__new__方法是传入类 (cls),而__init__方法传入类的实例化对象 (self),而有意思的是,__new__方法返回的值就是一个实例化对象(如果__new__方法返回 None,则__init__方法不会被执行,并且 返回值只能调用父类中的__new__方法,而不能调用毫无关系的类的__new__方法 )。

  1. 对代码进行修改
class IntTuple(tuple):
    def __new__(cls, iterable):
    	# 生成器
        f = (i for i in iterable if isinstance(i, int) and i>0)
        return super().__new__(cls, f)		# cls 其实就是 IntTuple , 因为类名可能会变, 所以用 cls 代替


int_t = IntTuple([2, -2, 'li', ['x', 'y'], 4])
print(int_t)
发布了85 篇原创文章 · 获赞 0 · 访问量 1236

猜你喜欢

转载自blog.csdn.net/qq_43621629/article/details/104015713