Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

98
Visualizações
Usando __new__ en clases de datos heredadas

Supongamos que tengo el siguiente código que se usa para manejar enlaces entre personas y países:

 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)

Todo esto funciona bien, pero decido que quiero hacerlo un poco menos torpe para poder manejar las diferentes formas de entrada. También quiero usar __new__ para asegurarme de que estamos obteniendo un código ISO válido cada vez, y quiero oponerme a que no se cree en ese caso. Por lo tanto, agrego un par de clases nuevas que heredan de esto:

 @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')]

Esto parece funcionar a primera vista, pero luego me encuentro con un problema:

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

devoluciones:

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

El objeto heredado tiene el tipo correcto, pero su atributo country es solo una cadena en lugar del tipo de Country que espero. He puesto instrucciones impresas en __new__ que verifican el tipo de new_obj.country , y es correcto antes de la línea de return .

Lo que quiero lograr es que sea a objeto del tipo CountryLinkFromISO2 que heredará los cambios que realice en CountryLink y que tenga un atributo country que se tome del diccionario countries_by_iso2 . ¿Cómo puedo conseguir esto?

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

El hecho de que la clase de datos lo haga detrás de escena no significa que sus clases no tengan un __init__() . Lo hacen y parece:

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

Cuando creas la clase con:

 CountryLinkFromISO2(123, 'AW')

esa cadena "AW" se pasa a __init__() y establece el valor en una cadena.

Usar __new__() de esta manera es frágil y devolver None de un constructor es bastante poco pitónico (imo). Tal vez sería mejor hacer una función de fábrica real que devuelva None o la clase que desea. Entonces no necesitas meterte con __new__() en absoluto.

 @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')

Si por alguna razón necesita funcionar con __new__() , puede devolver None from new cuando no hay ninguna coincidencia y establecer el país en __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]
over 4 years ago · Santiago Trujillo Relatório

0

El comportamiento que ve se debe a que las clases de datos establecen sus campos en __init__ , lo que sucede después de que se haya ejecutado __new__ .

La forma Pythonic de resolver esto sería proporcionar un constructor alternativo. No haría las subclases, ya que solo se usan para su constructor.

Por ejemplo:

 @dataclass class CountryLink: person_id: int country: Country @classmethod def from_iso2(cls, person_id: int, country_code: str): try: return cls(person_id, countries_by_iso2[country_code]) except KeyError: raise ValueError(f'invalid ISO2 country code {country_code!r}') from None @classmethod def from_iso3(cls, person_id: int, country_code: str): try: return cls(person_id, countries_by_iso3[country_code]) except KeyError: raise ValueError(f'invalid ISO3 country code {country_code!r}') from None country_links = [ CountryLink.from_iso2(123, 'AW'), CountryLink.from_iso3(456, 'AFG'), CountryLink.from_iso2(789, 'AO')]
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda