在继承的数据类中使用__new__

假设我有以下代码用于处理个人和国家/地区之间的链接:

from dataclasses import dataclass

@dataclass
class Country:
    iso2 : str
    iso3 : str
    name : str

countries = [ Country('AW','ABW','Aruba'),
              Country('AF','AFG','Afghanistan'),
              Country('AO','AGO','Angola')]
countries_by_iso2 = {c.iso2 : c for c in countries}
countries_by_iso3 = {c.iso3 : c for c in countries}

@dataclass
class CountryLink:
    person_id : int
    country : Country

country_links = [ CountryLink(123, countries_by_iso2['AW']),
                  CountryLink(456, countries_by_iso3['AFG']),
                  CountryLink(789, countries_by_iso2['AO'])]

print(country_links[0].country.name)

这一切都很好,但我决定让它不那么笨拙,以便能够处理不同形式的输入。我还想使用它__new__来确保我们每次都获得有效的 ISO 代码,并且我想反对在这种情况下无法创建。因此,我添加了几个继承自此的新类:

@dataclass
class CountryLinkFromISO2(CountryLink):
    def __new__(cls, person_id : int, iso2 : str):
        if iso2 not in countries_by_iso2:
            return None
        new_obj = super().__new__(cls)
        new_obj.country = countries_by_iso2[iso2]
        return new_obj

@dataclass
class CountryLinkFromISO3(CountryLink):
    def __new__(cls, person_id : int, iso3 : str):
        if iso3 not in countries_by_iso3:
            return None
        new_obj = super().__new__(cls)
        new_obj.country = countries_by_iso3[iso3]
        return new_obj

country_links = [ CountryLinkFromISO2(123, 'AW'),
                  CountryLinkFromISO3(456, 'AFG'),
                  CountryLinkFromISO2(789, 'AO')]

乍一看这似乎有效,但后来我遇到了一个问题:

a = CountryLinkFromISO2(123, 'AW')
print(type(a))
print(a.country)
print(type(a.country))

返回:

<class '__main__.CountryLinkFromISO2'>
AW
<class 'str'>

继承的对象具有正确的类型,但它的属性country只是一个字符串,而不是Country我期望的类型。我已经在__new__检查 类型的打印语句中放入了new_obj.country,并且在该return行之前是正确的。

我想要实现的是a成为一个类型的对象,该对象CountryLinkFromISO2将继承我所做的更改,CountryLink并使其具有country从字典中获取的属性countries_by_iso2。我怎样才能做到这一点?

回答

仅仅因为数据类在幕后做这件事,并不意味着你的类没有__init__(). 他们这样做,看起来像:

def __init__(self, person_id: int, country: Country):
    self.person_id = person_id
    self.country = country

当您使用以下命令创建类时:

CountryLinkFromISO2(123, 'AW')

"AW"字符串被传递给__init__()并将值设置为字符串。

__new__()以这种方式使用是脆弱的,并且从构造函数返回 None 是相当不pythonic的(imo)。也许您最好制作一个返回None您想要的类或返回类的实际工厂函数。那你根本就不用纠结了__new__()

@dataclass
class CountryLinkFromISO2(CountryLink):
    @classmethod
    def from_country_code(cls, person_id : int, iso2 : str):
        if iso2 not in countries_by_iso2:
            return None
        return cls(person_id, countries_by_iso2[iso2])

a = CountryLinkFromISO2.from_country_code(123, 'AW')

如果由于某种原因需要使用__new__(),您可以None在没有匹配项时从 new返回,并将国家/地区设置为__post_init__()

@dataclass
class CountryLinkFromISO2(CountryLink):
    def __new__(cls, person_id : int, iso2 : str):
        if iso2 not in countries_by_iso2:
            return None
        return super().__new__(cls)
    
    def __post_init__(self):        
        self.country = countries_by_iso2[self.country]


以上是在继承的数据类中使用__new__的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>