Лексическое литье из строки в тип

В последнее время я пытался хранить и читать информацию из файлов в Python и сталкивался с небольшой проблемой: мне хотелось прочитать информацию о типе из текстовых файлов. Тип casting от string до int или float довольно эффективен, но тип casting из строки в тип кажется еще одной проблемой. Естественно, я попробовал что-то вроде этого:

var_type = type('int')

Однако type не используется как литой, а как механизм для поиска типа переменной, которая на самом деле str здесь.

Я нашел способ сделать это с помощью:

var_type = eval('int')

Но я вообще стараюсь избегать функций/операторов типа eval или exec, где могу. Поэтому мой вопрос заключается в следующем: существует ли еще один питонический (и более конкретный) способ передать строку в тип?

Ответ 1

Мне нравится использовать locate, который работает со встроенными типами:

>>> from pydoc import locate
>>> locate('int')
<type 'int'>
>>> t = locate('int')
>>> t('1')
1

... а также все, что может найти в пути:

>>> locate('datetime.date')
<type 'datetime.date'>
>>> d = locate('datetime.date')
>>> d(2015, 4, 23)
datetime.date(2015, 4, 23)

... включая ваши пользовательские типы:

>>> locate('mypackage.model.base.BaseModel')
<class 'mypackage.model.base.BaseModel'>
>>> m = locate('mypackage.model.base.BaseModel')
>>> m()
<mypackage.model.base.BaseModel object at 0x1099f6c10>

Ответ 2

Вы немного смущены тем, что вы пытаетесь сделать. Типы, также известные как классы, являются объектами, как и все остальное в python. Когда вы пишете int в своих программах, вы ссылаетесь на глобальную переменную с именем int, которая является классом. То, что вы пытаетесь сделать, это не "лить строку для ввода", она обращается к встроенным переменным по имени.

Как только вы это понимаете, решение легко увидеть:

def get_builtin(name):
    return getattr(__builtins__, name)

Если вы действительно хотели превратить имя типа в объект типа, вот как вы это сделаете. Я использую deque, чтобы выполнить обход дерева по ширине без рекурсии.

def gettype(name):
    from collections import deque
    # q is short for "queue", here
    q = deque([object])
    while q:
        t = q.popleft()
        if t.__name__ == name:
            return t
        else:
            print 'not', t

        try:
            # Keep looking!
            q.extend(t.__subclasses__())
        except TypeError:
            # type.__subclasses__ needs an argument, for whatever reason.
            if t is type:
                continue
            else:
                raise
    else:
        raise ValueError('No such type: %r' % name)

Ответ 3

Почему бы просто не использовать справочную таблицу?

known_types = {
    'int': int,
    'float': float,
    'str': str
    # etc
}

var_type = known_types['int']

Ответ 4

Возможно, это то, что вы хотите, оно смотрит только на встроенные типы:

def gettype(name):
    t = getattr(__builtins__, name)
    if isinstance(t, type):
        return t
    raise ValueError(name)