Есть ли что-нибудь статичное в вызовах функций / методов python?

StackOverflow https://stackoverflow.com/questions/4747093

Вопрос

В задавая вопрос о рефлексии - Спросил я:

Хороший ответ.Но есть разница между словами myobject.foo() и x = getattr(myobject, "foo"); x();.Даже если это всего лишь косметика.В первом случае foo() компилируется статически.Во втором случае строка может быть создана любым количеством способов.– Джо , 1 час назад

На что получил ответ:

Эх, картофель / solanum tuberosum...в python niether компилируется статически, поэтому они более или менее эквивалентны.– SWeko 1 час назад

Я знаю, что элементы объектов Python хранятся в словаре и что все является динамическим, но я предположил, что, учитывая следующий код:

class Thing():
  def m(self):
    pass

t = Thing()

Следующий код каким - то образом будет статически скомпилирован при создании файла .pyc:

t.m()

т. е.компилятор знает адрес m(), так что нет привязки точки во время выполнения.Это или среда выполнения будет кэшировать последующие запросы.

Принимая во внимание, что это всегда будет включать в себя обращение к словарю:

meth = getattr(t, "m")
meth()

Все ли вызовы обрабатываются как поиск строк в словарях?Или эти два примера на самом деле идентичны?

Это было полезно?

Решение

Они не совсем идентичны, но оба являются поиском по словарю, как можно показать с помощью дизассемблера dis.dis.

В частности, обратите внимание на LOAD_ATTR инструкция с помощью динамически выполняет поиск атрибута по имени.Согласно документам, он "заменяет TOS [top of stack] на getattr(TOS, co_names[namei])".

>>> from dis import dis
>>> dis(lambda: t.m())
  1           0 LOAD_GLOBAL              0 (t)
              3 LOAD_ATTR                1 (m)
              6 CALL_FUNCTION            0
              9 RETURN_VALUE        
>>> dis(lambda: getattr(t, 'm')())
  1           0 LOAD_GLOBAL              0 (getattr)
              3 LOAD_GLOBAL              1 (t)
              6 LOAD_CONST               0 ('m')
              9 CALL_FUNCTION            2
             12 CALL_FUNCTION            0
             15 RETURN_VALUE        

Другие советы

Все вызовы обрабатываются как поиск по словарю (ну, внутри python, возможно, выполняет какую-то оптимизацию, но насколько это работает, вы можете предположить, что это поиск по словарю).

В python нет статической компиляции.Возможно даже сделать это:

t = Thing()
t.m = lambda : 1
t.m()

Это зависит от того, спрашиваете ли вы о языке Python или о конкретной реализации, такой как CPython.

Сам язык не говорит, что эти два параметра идентичны, поэтому вполне возможно, что прямой доступ к атрибуту может быть каким-то образом оптимизирован.Однако динамическая природа Python затрудняет выполнение этого последовательно, поэтому в CPython они фактически идентичны.

Сказав это, прямой t.m() может быть примерно в два раза быстрее, чем при использовании getattr() потому что это включает в себя один поиск по словарю вместо двух, которые вы получаете с помощью getattr():т. е.глобальное название getattr() само по себе должно быть просмотрено в словаре.

Не только классы могут быть изменены во время выполнения (как на Пример HS);но даже тот class ключевое слово "выполняется" во время выполнения:

Определение класса - это исполняемый оператор .Сначала он оценивает список наследования , если таковой имеется.Каждый элемент в списке наследования должен соответствовать объекту класса или class типу, который допускает создание подклассов.Затем набор класса выполняется в новом фрейме выполнения (см. Раздел Именование и привязка), используя вновь созданное локальное пространство имен и исходное глобальное пространство имен.(Как правило, набор содержит только функции определениями.) Для класса люкс завершает выполнение, его исполнения фрейм отбрасывается, но его локальное пространство имен сохраняется.[4] Затем создается объект класса с использованием списка наследования для базовых классов и сохраненного локального пространства имен для словаря атрибутов.Имя класса привязано к этому объекту класса в исходном локальном пространстве имен.

(Справочник по языку Python 7.7:Определения классов)

Другими словами, во время запуска интерпретатор выполняет код внутри class блокирует и сохраняет результирующий контекст.Во время компиляции нет никакого способа узнать, как будет выглядеть класс.

С getattr вы можете получить доступ к атрибутам, имена которых не являются допустимыми идентификаторами, хотя я не уверен, есть ли вариант использования таких атрибутов вместо использования словаря.

>>> setattr(t, '3', lambda : 4)
>>> t.3()
  File "<stdin>", line 1
    t.3()
      ^
SyntaxError: invalid syntax
>>> getattr(t, '3')()
4
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top