#πŸ”’ How to call int()/float()/str()/bool() based on type hint?

51 messages Β· Page 1 of 1 (latest)

brittle spear
#

I've got a dataclass with attributes of various types:

@dataclass
class Student:
  __slots__ = ['name', 'age', 'height', 'present', etc...]
  name: str
  age: int
  grade: float
  present: bool
  etc...

class Classroom():
  tree: xml element object
  roster = []

  def get_roster():
    for s in self.tree.find('students'):
      student = Student('', 0, 0.0, False, etc...)
      for attr in student.__slots__:
        attrEntry = s.find(attr)
        if attr is not None:
          value = attrEntry.get('value') # How to convert value into appropriate type according to type annotation?
          setattr(student, attr, value)
      roster.append(student)

How can I apply the correct type conversion according to the type hint?

waxen lindenBOT
#

@brittle spear

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

terse nymph
#

I wouldn't

#

I'd just use keyword arguments in the Student constructor

brittle spear
#

?

#

i don't get what you mean

keen geyser
#

Doing this well will be difficult. You can access the types using __annotations__, but this expects that every type's initialiazer will act as a "cast", which isn't a safe assumption

#

!e

from dataclasses import dataclass

@dataclass
class T:
    a: int
    b: str
    c: float

print(T.__annotations__)
waxen lindenBOT
keen geyser
#

Those class values can be used, in this case, to convert input to the correct type.

remote ibex
#

for less magic, you can use typing.get_type_hints(T)

#

Here's something you can do to make it automatic. ```py
from dataclasses import dataclass, fields

@dataclass(slots=True)
class T:
a: int
b: str
c: float

def post_init(self):
for field in fields(self):
val = getattr(self, field.name)
if not isinstance(val, field.type):
setattr(self, field.name, field.type(val))

brittle spear
#

hmmm i think i'll go with

try:
  int(val)
except TypeError:
  try:
    float(val)
  except TypeError:
    etc...
       bool()
          str()
remote ibex
#

What about this? ```py
@dataclass
class X:
foo: str

X("12")

#

Suddenly X.foo is an int

#

fields() returns a tuple of Field objects containing the field's name and type

brittle spear
remote ibex
#

the annotation (type) is included in fields()

brittle spear
#

how do i get a function like int() from <ckass 'int'>

remote ibex
#

that's it

keen geyser
remote ibex
#

if you print something and it says <class 'int'>, it's not a string

brittle spear
#

oh ok

keen geyser
#

If you take the value and use it in the same way you would int for example, it will work. In both cases, the value is a class that can be called to get an instance of the class.

#

!e

another_name = int
print(another_name("5"))
terse nymph
#

I still vote for ```py
@dataclass
class Student:
slots = ["name", "age", "grade", "height", "present"]
name: str
age: int
grade: float
present: bool

class Classroom:
tree # xml element object
roster = []

def get_roster():
    for s in self.tree.find("students"):
        student = Student(name=s.name, age=int(s.age), grade=float(s.grade), present=bool(s.present))
        roster.append(student)
#

explicit is better than implicit

remote ibex
#

Build the object with a dict first

#
student_attrs = {}
for field in fields(Student):
  entry = s.find(field.name)
  if entry is not None:
    value = entry.get('value')
    student_attrs[field.name] = field.type(value)
student = Student(**student_attrs)
#

^ will break if you start using unions

#

workaround if you do: ```py
import types
import typing

typ = field.type
if isinstance(typ, types.UnionType):
for u_typ in typing.get_args(typ):
if u_typ is types.NoneType:
continue
try:
if not isinstance(value, u_typ):
value = u_typ(value)
except ValueError:
pass
else:
break
else:
raise ValueError

#

complexity always increases when you introduce union types

brittle spear
#

ok thanks

brittle spear
#

it's not reading any of the values from the xml

terse nymph
#

well if you write what I wrote literally, sure

#

but my point was: use the keyword arguments

#

amended

#

yes I have to duplicate the types

#

but I think that's better than the alternatives 🀷

tough brook
weary wren
brittle spear
#

i would prefer to use some kind of loop

weary wren
#

in my opinion, this is also a step any programmer goes through - eventually learning that simplicity is better than complexity.

having a tailored approach, matching the needs and specs within the codebase.

terse nymph
brittle spear
#

luckily i don't have any unions

remote ibex
#

Since you're using xml, maybe you can use lxml.objectify

waxen lindenBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.