Есть что-нибудь статичное в вызовах функций / методов python? - PullRequest
3 голосов
/ 20 января 2011

В задают вопрос об отражении Я спрашиваю:

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

Который получил ответ:

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

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

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

t = Thing()

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

t.m()

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

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

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

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

Ответы [ 5 ]

7 голосов
/ 20 января 2011

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

В частности, обратите внимание на инструкцию LOAD_ATTR с динамическим поиском атрибута по имени. Согласно документации, он «заменяет TOS [верх стека] на 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        
3 голосов
/ 20 января 2011

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

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

t = Thing()
t.m = lambda : 1
t.m()
2 голосов
/ 20 января 2011

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

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

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

1 голос
/ 20 января 2011

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

>>> setattr(t, '3', lambda : 4)
>>> t.3()
  File "<stdin>", line 1
    t.3()
      ^
SyntaxError: invalid syntax
>>> getattr(t, '3')()
4
1 голос
/ 20 января 2011

Изменяются не только классы во время выполнения (как в примере HS );но даже ключевое слово class выполняется во время выполнения:

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

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

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

...