Search code examples
pythonpython-3.xpython-dataclasses

How to check the type of a variable defined by python dataclass directly?


If I create and specify a type directly as follows,

INT64 = int
BIGINT = int
VARCHAR256 = str
VARCHAR512 = str

@dataclass
class TempClass(object):
    v1: INT64
    v2: BIGINT
    v3: VARCHAR256
    v4: VARCHAR512

When checking the type of a dataclass variable, is there a way to check whether it is a directly specified type?

As a simple example, if you check the type as shown below, it would be nice to be able to output the specified INT64, BIGINT, VARCHAR256, or VARCHAR512 or check the type!

for _i in TempClass.__dataclass_fields__:
    print(type(_i))

Solution

  • You can iterate over the fields() of a dataclass in order to retrieve each field's name and type, as shown below.

    However, note that doing so will only print the resolved type of each field, i.e. the type to the right of the equals = sign below.

    For instance, it will resolve the type of the first field -- v1 -- to int, which is likely not what you intend or want.

    from dataclasses import dataclass, fields
    
    INT64 = int
    BIGINT = int
    VARCHAR256 = str
    VARCHAR512 = str
    
    @dataclass
    class TempClass(object):
        v1: INT64
        v2: BIGINT
        v3: VARCHAR256
        v4: VARCHAR512
    
    
    for field in fields(TempClass):
        print(field.name, '-', field.type)
    

    Prints:

    v1 - <class 'int'>
    v2 - <class 'int'>
    v3 - <class 'str'>
    v4 - <class 'str'>
    

    To work around this, you can add the following to the top of your module:

    from __future__ import annotations
    

    This will convert all annotations in the module to a str value, so that the annotations are lazy-evaluated.

    This is useful in our case, so we know the actual name of the annotation passed in, rather than the resolved or evaluated type (int for example).

    Example:

    from __future__ import annotations
    
    from dataclasses import dataclass, fields
    from sys import modules
    
    INT64 = int
    BIGINT = int
    VARCHAR256 = str
    VARCHAR512 = str
    
    
    @dataclass
    class TempClass(object):
        v1: INT64
        v2: BIGINT
        v3: VARCHAR256
        v4: VARCHAR512
    
    
    # retrieve globals of the class's module
    cls_globals = modules[TempClass.__module__].__dict__
    
    # iterate over each dataclass field, retrieve its name and type
    for field in fields(TempClass):
        f_name = field.name
        # field's annotation -- will be a "lazy-evaluated" string value
        f_ann = field.type
        # field's resolved type (for example `str` or `int`)
        f_type = cls_globals[f_ann]
        # custom name for the field's type
        f_type_name = 'STRING' if f_type is str else 'NUMBER' if issubclass(f_type,  (int, float)) else 'UNKNOWN'
    
        print(f_name, '-->')
        print(f'  annotation (str):  {f_ann!r}')
        print(f'  type:              {f_type.__qualname__}')
        print(f'  type_name:         {f_type_name}')
    

    Output:

    v1 -->
      annotation (str):  'INT64'
      type:              int
      type_name:         NUMBER
    v2 -->
      annotation (str):  'BIGINT'
      type:              int
      type_name:         NUMBER
    v3 -->
      annotation (str):  'VARCHAR256'
      type:              str
      type_name:         STRING
    v4 -->
      annotation (str):  'VARCHAR512'
      type:              str
      type_name:         STRING