【发布时间】:2021-10-12 15:44:47
【问题描述】:
假设我有以下代码用于处理个人和国家之间的链接:
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 987654336@。我怎样才能做到这一点?
【问题讨论】:
-
您确定要覆盖
__new__而不是__init__?你也可以考虑使用像 attrs 这样的库。 -
@NathanielFord
__init__将始终返回该类的一个实例,如果输入无效,我不希望发生这种情况。我可以有一个引发异常的__init__,但这意味着每次我尝试调用我的代码时,我都必须将它放在一个 try/except 块中,这很笨拙并且可能导致性能问题。 -
我认为 Mark 提供了正确的课程(工厂方法),但您应该查看 attrs 或类似库的验证器。
标签: python python-3.x inheritance python-dataclasses