如何处理父类和子类的情况

3 投票
1 回答
58 浏览
提问于 2025-04-14 16:43

问题描述:

我有成百上千个.py文件,这些文件定义了pydantic的模式。突然之间,我需要把空字符串当作None来处理。我希望在所有文件中做的改动尽量少。

我采取的做法:

我创建了一个继承类,像这样:

class ConstrainedStr(str):
        @classmethod
def __get_validators__(cls):
    yield cls.validate

@classmethod
def validate(cls, v: str, field: Field) -> Optional[str]:
    v = v.strip()
    if v == "":
        return None
    return v

然后,在所有的.py文件中,我只添加了一条导入语句:

from package.module import ConstrainedStr as str

幸运的是,第一次尝试就成功了。但我遇到了一个问题,我有一个函数:

用户文件:

from package.module import ConstrainedStr as str

def validate(cls, value:str): //sample value 'asd'
    if isinstance(value, str):
        validation_rule()

在这里,这个条件判断失败了。

问题

  1. 我怎么才能避免大改动来实现这个?有没有办法?
  2. 为什么那个isinstance检查失败了?ConstrainedStr也是一个str,对吧?我的理解错了吗?

当我进一步深入研究时,只是为了理解第二个问题的行为,我发现了以下内容。

type(ConstrainedStr) 

返回 type<class>

`type('123')` 

返回 type<str>

但是当我检查builtins包时,发现str也是一个类。但type函数返回str的类型是str,而对于ConstrainedStr却返回class。所以,我的第二个问题就出现了。

另一个例子:

class A(int):
    pass

isinstance(2, A)
# False

这就是我的第二个问题。

1 个回答

3

当一个子类的实例被创建时,它自动也成为了父类的实例,但反过来就不一定成立。这就像每只猫都是哺乳动物,但并不是每个哺乳动物都是猫。

同样,因为 ConstrainedStrstr 的子类,所以 ConstrainedStr 的实例自动也是 str 的实例。但是,反过来就不成立,比如 'asd' 这个 str 的实例就不能自动被认为是 ConstrainedStr 的实例。

如果你想让反向的实例检查也成立,可以通过在元类中定义 __instancecheck__ 来定制 isinstance 的行为。同时,通过定义 __subclasscheck__ 来定制 issubclass,以保持这些关系的一致性。

为了实现你想要的行为,你自定义的 __instancecheck__ 应该在给定的实例是任何父类的实例时返回 True

class SubstitutableMeta(type):
    def __instancecheck__(cls, instance):
        return any(isinstance(instance, base) for base in cls.__bases__)

    def __subclasscheck__(cls, subclass):
        return any(issubclass(subclass, base) for base in cls.__bases__)

class ConstrainedStr(str, metaclass=SubstitutableMeta):
    pass

print(isinstance('asd', ConstrainedStr)) # outputs True

撰写回答