Как интерпретатор Python ищет типы?

Если я напишу что-то вроде:

>>> a = float()

как интерпретатор Python знает, где искать тип 'float'?

Я знаю, что "float" - это переменная, определенная в Lib / types.py и ссылающаяся на встроенный тип types.FloatType, Но как интерпретатор создает полный список всех возможных типов сценария (включая определяемый пользователем и определяемый импортируемым модулем)? В каких местах это выглядит? И что мне делать, чтобы построить такой список внутри скрипта Python?

3 ответа

Решение

В своем вопросе вы написали "как интерпретатор создает полный список всех возможных типов", но это содержит ложное предположение. Нет полного списка возможных типов. Существует эффективный набор существующих типов, но они не расположены в списке.

У вас есть пространство имен, которое обычно выглядит в определенном порядке: функция (локальные), модуль (глобальные), встроенные. При разборе кода компилятор также выбирает конструкторы типов для литералов. Каждое имя в каждом месте может относиться к любому объекту, среди которых много типов:

>>> [name for name in dir(__builtins__) 
     if isinstance(getattr(__builtins__,name), type)]
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 
 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 
 'EnvironmentError', 'Exception', 'FloatingPointError', 'FutureWarning', 
 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 
 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 
 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 
 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 
 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 
 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 
 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 
 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', 'basestring', 
 'bool', 'buffer', 'bytearray', 'bytes', 'classmethod', 'complex', 'dict', 
 'enumerate', 'file', 'float', 'frozenset', 'int', 'list', 'long', 'memoryview', 
 'object', 'property', 'reversed', 'set', 'slice', 'staticmethod', 'str', 'super', 
 'tuple', 'type', 'unicode', 'xrange']

Интерпретатор на самом деле не интересуется количеством типов, а только тем, как каждый из них применяется для интерпретации кода, который нужно запустить. Он находит это с помощью указателя типа, который есть у каждого объекта:

>>> type(1)
<type 'int'>

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

>>> 1+"foo"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> "foo"+1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
>>>

Первый + на самом деле int.__add__ и второе str.__add__, просто посмотрел вверх через объект слева. (Это становится немного сложнее с альтернативными попытками, такими как __radd__, но основной принцип тот же.)

Ваш вопрос, кажется, "Как это объявление типа?" Ответ таков: это не объявление типа. Имена в Python не имеют типа, связанного с ними. Имена относятся к значениям, а значения имеют тип, определенный во время выполнения.

Когда Python выполняется a = float(), он ищет название float, и находит его во встроенных, это функция. Он вызывает эту функцию без аргументов. Возвращаемое значение - это объект с плавающей точкой. Имя a затем делается для ссылки на этот объект. Это все, что он делает. Прежде чем выполнить эту строку кода, Python понятия не имеет, что a станет, и он понятия не имеет, что поплавки будут задействованы.

Python динамический, поэтому ваша строка кода могла быть в этой программе:

def float():
    return "I'm not a float!"

a = float()

Теперь когда a = float() выполняется, встроенный не имеет ничего общего с этим, и нет нигде плавает, и a ссылается на строку.

Дополнительные сведения об именах и значениях см. В разделе " Факты и мифы об именах и значениях Python".

Если имя должно быть разрешено как глобальное, и не существует соответствующего глобального (у вас нет функции с именем float в вашем модуле), то Python смотрит на __builtins__ объект, который является частью глобального пространства имен для каждого модуля.

Этот объект является встроенным модулем, определенным в C. См. Python/bltinmodule.c,

В случае float, имя связано с C PyFloat_Type структура, в серии заданий, а также PyFloat_Type структура определяется в Objects/floatobject.c

По сути, в Python нет "объявлений типов"; float а также str и т. д. - это просто C-структуры, соответствующие определенному поведению; они производят значения, которые Python может распознать как определенный тип при запросе. Таким образом, все, что производится float() указывает на float как его тип.

И потому что __builtins__ Консультируется только в том случае, если у вас уже нет глобала с таким же именем, вы можете легко представить свою собственную реализацию float() отозваны; тот, который возвращает что-то совершенно другое при вызове.

Если вы хотите перечислить все объекты встроенного типа, зациклите встроенные объекты и перечислите все, что является экземпляром type():

import __builtin__

for name, obj in vars(__builtin__).iteritems():
    if isinstance(obj, type) and not issubclass(obj, BaseException):
        print name, obj

Как __builtins__ works - это деталь реализации CPython, но те же объекты, что и __builtin__ Модуль используется здесь.

Обратите внимание, что вы никогда не сможете надеяться собрать все возможные типы кода, которые может произвести код Функции могут создавать новые типы, которые являются локальными для функции и не перечислены в глобальных переменных модуля; Типы не должны быть перечислены там, чтобы работать:

def dynamic_types(name, base=object):
    class DynamicType(base):
        def __repr__(self):
            '<{} (DynamicType) at {:#x}>'.format(self.__name__, id(self))
    DynamicType.__name__ = name
    return DynamicType()

Приведенная выше функция создает объекты с новым типом (классы Python также являются типами) каждый раз, когда вы вызываете его. Вы не найдете этот тип ни в глобальных переменных модуля, ни во встроенных.

Другие вопросы по тегам