专门化通用Python类定义

问题描述 投票:0回答:1

我正在编写遗传算法实现。我的基因可以有多种形状和形式,所以我创建了这个类:

from typing import Self, TypeVar, Generic
from abc import ABC, abstractmethod

TYPE = TypeVar('TYPE')
class Gene(ABC, Generic[TYPE]):
    def __init_subclass__(cls, *args, **kwargs) -> None:
        super().__init_subclass__(*args, **kwargs)
    def __init__(self, *args, specific: TYPE|None = None, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.value = self.randomise() if specific is None else specific
    def __str__(self) -> str: 
        return f'{self.__class__.__name__} = {self.value}'
    def __repr__(self) -> str: 
        return self.__str__()
    def clone(self) -> Self:
        return type(self)(specific=self.value)
    @abstractmethod
    def randomise(self) -> TYPE:
        ...
    @abstractmethod
    def combine(self, other: Self) -> tuple[Self, Self]:
        ...
    @abstractmethod
    def mutate(self) -> Self:
        ...

但是,如果我的基因是

Gene[float]
Gene[int]
,我已经知道这些
@abstractmethod
的实现是什么:

import random

class Gene[float]:
    def randomise(self) -> float: 
        return random.random() * (self.LOWER - self.HIGHER) + self.LOWER
    def combine(self, other: Self) -> tuple[Self, Self]: 
        mix = random.random()
        return type(self)(specific=self.value*mix + other.value*(1-mix)), type(self)(specific=self.value*(1-mix) + other.value*mix)
    def mutate(self) -> Self:
        return type(self)()

Python 有没有办法在 TYPE=float

专门化
这些方法?

附注- 我发现了一个非常不优雅的解决方案,即创建一个

GeneFloat
类作为子类
Gene[float]
。我不喜欢!

python generics
1个回答
0
投票

虽然不漂亮,但很管用...

class Gene[CONTENT: (int, float, str)](Protocol):
    value: CONTENT # this is an INSTANCE variable!
    LOW: ClassVar[int|float]
    HIGH: ClassVar[int|float]
    TYPE: ClassVar[type] 
    def __init_subclass__(cls, *, content: type[CONTENT], **kwargs) -> None:
        cls.TYPE = content
        if cls.TYPE == int or cls.TYPE == float:
            if 'low' not in kwargs or 'high' not in kwargs:
                raise ValueError(f'When creating a gene of type int or float, you MUST specifiy both "low" and "high" on the class arguments. You got {kwargs}!')
            cls.LOW = kwargs['low']
            cls.HIGH = kwargs['high']
            for key in ['low', 'high']: del kwargs[key]
        super().__init_subclass__(**kwargs)
    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        self.value = self.randomise()
    def __str__(self) -> str:
        if type(self.value) == float: return f'{self.value:.3f}'
        elif type(self.value) == int: return f'{self.value:>{max(len(f'{self.LOW:,d}'), len(f'{self.HIGH:,d}'))},d}'
        elif type(self.value) == str: return self.value
        else: raise ValueError(f'Cannot have a gene of type {self.TYPE.__name__}!')
    def update(self, value: CONTENT) -> Self:
        self.value = value
        return self
    def clone(self) -> Self:
        return type(self)().update(self.value)
    def randomise(self) -> CONTENT:
        if self.TYPE == float: return self.TYPE(random.random() * (self.HIGH - self.LOW) + self.LOW)
        elif self.TYPE == int: return self.TYPE(random.randint(self.TYPE(self.LOW), self.TYPE(self.HIGH)))
        elif self.TYPE == str: return self.TYPE('')
        else: raise ValueError(f'Cannot have a gene of type {self.TYPE.__name__}!')
    def combine(self, other: Self) -> tuple[Self, Self]:
        if type(self.value) == str and type(other.value) == str: 
            return self.clone(), other.clone()
        elif type(self.value) == float and type(other.value) == float:
            mix = random.random()
            father = self.value*mix + other.value*(1-mix)
            mother = self.value*(1-mix) + other.value*mix
            return type(self)().update(father), type(self)().update(mother)
        elif type(self.value) == int and type(other.value) == int:
            mix = random.random()
            father = int(self.value*mix + other.value*(1-mix))
            mother = int(self.value*(1-mix) + other.value*mix)
            return type(self)().update(father), type(self)().update(mother)
        else:
            raise ValueError(f'Cannot combine genes of type "{self.__class__.__name__}" (self) and "{other.__class__.__name__}" (other)!')
    def mutate(self) -> Self:
        return type(self)()

你可以简单地用这样的方式来调用它:

class Variable(Gene[float], content=float, low=-10.0): pass
© www.soinside.com 2019 - 2024. All rights reserved.