专门化通用Python类定义
我正在编写一个遗传算法的实现。我的基因可以有很多种形状和形式,所以我创建了这个类:
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]
的子类。我不喜欢这样做!
1 个回答
0
这看起来不太好,但确实能用...
我基本上把 TypeVar
改成了 int
、float
或 str
其中之一,然后为这三种类型写了 if
语句。
我必须在类定义的时候传入类型,因为 TypeVar
在运行时是不可用的(真可惜)。
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