You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Genome类异质列表添加动态类型提示以移除type:ignore

解决Genome类__mul__方法的动态类型提示问题

问题背景

我正在编写模拟生物遗传机制的Python代码,其中Genome类可由Gene或Genome自身组成异质列表,用于模拟单基因控制(如眼色)和多基因协同控制(如血型)的遗传机制。当前Genome的__mul__方法需要添加精准的动态类型提示,让类型检查器识别同一类的父代与母代序列对应类型一致,从而移除# type: ignore注释,实现完全类型可检查的代码。

核心问题代码片段:

class Genome(list['Gene | Genome']):
    # ... 其他代码 ...
    def __mul__(self, mother: Self) -> Self:
        return type(self)(*(gene * mother[sequence] for sequence, gene in enumerate(self))) # type: ignore - Because gene * mother[sequence] is Gene | Genome x Gene | Genome  

示例用法:

class ABOAntigens(Allele):
    A = 'A', 1
    B = 'B', 1
    O = 'O', 0
class ABO(Gene, Allele=ABOAntigens): pass

class RhesusAntigen(Allele):
    Positive = '+', 1
    Negative = '-', 0
class Rhesus(Gene, Allele=RhesusAntigen): pass

class Blood(Genome, genome=[ABO, Rhesus]): pass

class EyeColour(Allele):
    Brown = 'Brown', 2
    Green = 'Green', 1
    Blue = 'Blue', 0
class Eye(Gene, Allele=EyeColour): pass

class Human(Genome, genome=[Eye, Blood]): pass

解决方案

要让类型检查器正确识别序列对应类型的匹配关系,需结合泛型(Generics)、可变类型参数和Self类型实现。具体步骤如下:

1. 导入必要的类型工具

确保导入Python typing模块中的相关工具(Python 3.11+支持TypeVarTuple和Unpack):

from typing import Generic, TypeVarTuple, Unpack, Self, Type
import itertools
import random
from enum import StrEnum

2. 定义可变长度的类型变量

用TypeVarTuple定义可变数量的类型参数,适配Genome子类中任意长度的序列类型:

T = TypeVarTuple('T')

3. 将Genome改造为泛型类

让Genome继承Generic[Unpack[T]],并在__init_subclass__中绑定子类的具体序列类型:

class Genome(list[Unpack[T]], Generic[Unpack[T]]):
    GENOME: list[Type[Unpack[T]]] = []
    
    def __init_subclass__(cls, *args, genome: list[Type[Unpack[T]]], **kwargs) -> None:
        super().__init_subclass__(*args, **kwargs)
        cls.GENOME = genome

4. 完善__mul__方法的类型提示

现在可以给__mul__方法添加精确的类型提示,移除type: ignore注释:

def __mul__(self, mother: Self) -> Self:
    return type(self)(*(gene * mother[sequence] for sequence, gene in enumerate(self)))

5. 同步完善其他方法的类型提示(可选)

为了让整个类的类型检查更完整,同步调整__init__和__add__方法:

def __init__(self, *args: Unpack[T], pure: bool = False) -> None:
    super().__init__([])

    for sequence, GeneType in enumerate(self.GENOME):
        if sequence < len(args):
            gene = args[sequence]
        else:
            gene = GeneType(pure=pure)

        if not isinstance(gene, GeneType):
            raise ValueError(f'Given sequence {args} is not compatible with Genome mapping {", ".join(g.__name__ for g in self.GENOME)}!')
        
        self.append(gene)

def __add__(self, mother: Self) -> list[Self]:
    return [type(self)(*genome) for genome in list(itertools.product(*[self[sequence] + mother[sequence] for sequence in range(len(self.GENOME))]))]

完整修改后的Genome类片段

from typing import Generic, TypeVarTuple, Unpack, Self, Type
import itertools
import random
from enum import StrEnum

class Allele(StrEnum):
    dominant: int
    
    def __new__(cls, value: str, dominant: int) -> Self:
        allele = str.__new__(cls, value)
        allele._value_ = value
        allele.dominant = dominant
        return allele

    def __str__(self) -> str:
        return self.value
    
    def __repr__(self) -> str:
        return self.__str__()

class Gene(tuple[Allele, Allele]):
    ALLELE: type[Allele] = Allele

    def __init_subclass__(cls, *args, Allele: type[Allele], **kwargs) -> None:
        super().__init_subclass__(*args, **kwargs)
        cls.ALLELE = Allele
    
    def __str__(self) -> str:
        for dominance in sorted([allele.dominant for allele in self.ALLELE], reverse=True): 
            group = set(item for item in self if item.dominant == dominance)
            if len(group) != 0: return ''.join(sorted(group))
        
        return ''
    
    def __repr__(self) -> str:
        return f'({self[0]}, {self[1]})'
    
    def __mul__(self, mother: Self) -> Self:
        return type(self)(random.choice(list(self)), random.choice(list(mother)))

    def __add__(self, mother: Self) -> list[Self]:
        return [type(self)(one, other) for one in self for other in mother]

T = TypeVarTuple('T')

class Genome(list[Unpack[T]], Generic[Unpack[T]]):
    GENOME: list[Type[Unpack[T]]] = []
    
    def __init_subclass__(cls, *args, genome: list[Type[Unpack[T]]], **kwargs) -> None:
        super().__init_subclass__(*args, **kwargs)
        cls.GENOME = genome
    
    def __init__(self, *args: Unpack[T], pure: bool = False) -> None:
        super().__init__([])

        for sequence, GeneType in enumerate(self.GENOME):
            if sequence < len(args):
                gene = args[sequence]
            else:
                gene = GeneType(pure=pure)

            if not isinstance(gene, GeneType):
                raise ValueError(f'Given sequence {args} is not compatible with Genome mapping {", ".join(g.__name__ for g in self.GENOME)}!')
            
            self.append(gene)
    
    def __str__(self) -> str: 
        if all(issubclass(GENE, Gene) for GENE in self.GENOME):
            return ''.join(str(gene) for gene in self)
        
        return ', '.join(str(gene) for gene in self)
    
    def __mul__(self, mother: Self) -> Self:
        return type(self)(*(gene * mother[sequence] for sequence, gene in enumerate(self)))
    
    def __add__(self, mother: Self) -> list[Self]:
        return [type(self)(*genome) for genome in list(itertools.product(*[self[sequence] + mother[sequence] for sequence in range(len(self.GENOME))]))]

原理说明

  • TypeVarTuple与Unpack:支持定义可变长度的类型参数,完美适配Genome子类中genome参数的任意长度序列类型。
  • 泛型Genome类:每个子类(如Blood、Human)会自动绑定自身genome列表对应的类型,类型检查器能精准识别每个位置的元素类型。
  • Self类型:确保__mul__方法的mother参数与self是同一子类实例,返回值也为该子类实例,同时保证每个序列位置的gene * mother[sequence]操作符合对应类型的__mul__方法签名。

经过上述修改后,类型检查器(如mypy)可正确识别所有类型关系,无需再添加# type: ignore注释。

内容的提问来源于stack exchange,提问作者Nuno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:40:53