2010-07-07 3 views
16

Что происходит внутри, когда я нажимаю Введите?Как работает симплекс? Как он взаимодействует с интерактивной оболочкой Python и как работает интерактивная оболочка Python?

Моя мотивация для спрашивать, кроме простого любопытства, чтобы выяснить, что происходит, когда вы

from sympy import * 

и введите выражение. Как это идет от Введите называть

__sympifyit_wrapper(a,b) 

в sympy.core.decorators? (Это первое место winpdb взял меня, когда я попытался проверить оценку.) Я бы предположил, что есть некоторая встроенная функция eval, которая вызывается нормально и переопределяется при импорте sympy?

+0

Никто никогда не давал удовлетворительного ответа на этот вопрос, и мне действительно интересно. – Omnifarious

ответ

11

Сразу после игры с ним еще несколько я думаю, что у меня есть. Когда я впервые задал вопрос, который я не знал о operator overloading.

Итак, что происходит в этой сессии python?

>>> from sympy import * 
>>> x = Symbol(x) 
>>> x + x 
2*x 

Оказывается, нет ничего особенного в том, как интерпретатор оценивает выражение; главное, что питон переводит

x + x

в

x.__add__(x)

и Symbol наследует от базового класса, который определяет __add__(self, other) вернуть Add(self, other). (Эти классы находятся в sympy.core.symbol, sympy.core.basic и sympy.core.add, если вы хотите посмотреть.)

Итак, как Jerub говорил, Symbol.__add__() имеет decorator под названием _sympifyit, который в основном преобразует второй аргумент функции в выражение sympy перед оценкой функции, возвращая функцию, называемую __sympifyit_wrapper, что и было ранее.

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

symbolic.py -

class Symbol(object): 
    def __init__(self, name): 
     self.name = name 
    def __add__(self, other): 
     return Add(self, other) 
    def __repr__(self): 
     return self.name 

class Add(object): 
    def __init__(self, left, right): 
     self.left = left 
     self.right = right 
    def __repr__(self): 
     return self.left + '+' + self.right 

Теперь мы можем сделать:

>>> from symbolic import * 
>>> x = Symbol('x') 
>>> x+x 
x+x 

С небольшим рефакторинга его можно легко расширить, чтобы обрабатывать все basic arithmetic:

class Basic(object): 
    def __add__(self, other): 
     return Add(self, other) 
    def __radd__(self, other): # if other hasn't implemented __add__() for Symbols 
     return Add(other, self) 
    def __mul__(self, other): 
     return Mul(self, other) 
    def __rmul__(self, other): 
     return Mul(other, self) 
    # ... 

class Symbol(Basic): 
    def __init__(self, name): 
     self.name = name 
    def __repr__(self): 
     return self.name 

class Operator(Basic): 
    def __init__(self, symbol, left, right): 
     self.symbol = symbol 
     self.left = left 
     self.right = right 
    def __repr__(self): 
     return '{0}{1}{2}'.format(self.left, self.symbol, self.right) 

class Add(Operator): 
    def __init__(self, left, right): 
     self.left = left 
     self.right = right 
     Operator.__init__(self, '+', left, right) 

class Mul(Operator): 
    def __init__(self, left, right): 
     self.left = left 
     self.right = right 
     Operator.__init__(self, '*', left, right) 

# ... 

С чуть более тщательной настройкой мы можем получить то же поведение, что и сеанс sympy с самого начала .. мы изменим Add, чтобы он возвращал экземпляр Mul, если его аргументы равны. Это немного сложнее, так как мы добираемся до него до создание экземпляра; мы должны использовать __new__() instead of __init__():

class Add(Operator): 
    def __new__(cls, left, right): 
     if left == right: 
      return Mul(2, left) 
     return Operator.__new__(cls) 
    ... 

Не забудьте реализовать оператор равенства для символов:

class Symbol(Basic): 
    ... 
    def __eq__(self, other): 
     if type(self) == type(other): 
      return repr(self) == repr(other) 
     else: 
      return False 
    ... 

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

+0

Ах, так ваш вопрос в том, как работает симплекс, а не как работает интерпретатор. Вы получите награду независимо, но я действительно надеялся на некоторые подробные описания того, как интерпретатор действительно выполнял свою работу. Я предполагаю, что это не слишком сложно и, вероятно, включает в себя 'eval' и библиотеку readline, но мне было очень любопытно. Если на этот мой вопрос не будет дан ответ, я постараюсь изменить название вопроса, чтобы быть более точным. – Omnifarious

+0

Извините, что .. посмотрев несколько своих сообщений, вы ясно знаете намного больше о питоне, чем я, и, вероятно, ничего не получил от вашей щедрости:/Я ошибочно подумал, что sympy делает что-то волшебное с тем, как интерпретатор оценивал выражения, но «магия» была только у операторов. Я довольно новичок в python и программировании в целом, и я не знал, что существует перегрузка операторов. Почему бы вам не попробовать написать интерпретатор python? Я не понимал симпатии, пока я не пытался написать свою собственную систему символической алгебры. – daltonb

+1

@secondbanana - Это хороший совет. :-) Мне очень интересно, как много изучает вопрос о том, какой вопрос задавать. Есть много вопросов о StackOverflow, где ясно, что у кого-то действительно был другой вопрос, и вместо того, чтобы спрашивать об этом, они задали другой вопрос, потому что они предпочли решение. «Почему ты хочешь это сделать?» является одним из лучших ответов/разъяснений. – Omnifarious

5

Я только что проверил код sympy (на http://github.com/sympy/sympy), и это выглядит как __sympifyit_wrapper - это декоратор. Причина, по которой будет называется потому, что есть некоторый код где-то, что выглядит следующим образом:

class Foo(object): 
    @_sympifyit 
    def func(self): 
     pass 

И __sympifyit_wrapper обертка, которая возвращается на @_sympifyit. Если вы продолжите отладку, вы можете найти функцию (в моем примере с именем func).

Я собираю в одном из многих модулей и пакетов, импортированных в sympy/__init__.py, некоторые встроенные в код заменены на симпатичные версии. Эти симпатичные версии, вероятно, используют этот декоратор.

exec как используется >>> не будет заменен, объекты, которые будут эксплуатироваться, были бы.

6

Это не имеет много общего с реальным вопрос secondbanana - он просто выстрел в Баунти всевозможный;)

Сам интерпретатор очень прост. На самом деле вы могли бы написать простой (нигде почти идеальной, не обрабатывает исключения, и т.д.) самостоятельно:

print "Wayne's Python Prompt" 

def getline(prompt): 
    return raw_input(prompt).rstrip() 

myinput = '' 

while myinput.lower() not in ('exit()', 'q', 'quit'): 
    myinput = getline('>>> ') 
    if myinput: 
     while myinput[-1] in (':', '\\', ','): 
      myinput += '\n' + getline('... ') 
     exec(myinput) 

Вы можете сделать большую часть материала вы привыкли в обычной строке :

Waynes Python Prompt 
>>> print 'hi' 
hi 
>>> def foo(): 
...  print 3 
>>> foo() 
3 
>>> from dis import dis 
>>> dis(foo) 
    2   0 LOAD_CONST    1 (3) 
       3 PRINT_ITEM 
       4 PRINT_NEWLINE 
       5 LOAD_CONST    0 (None) 
       8 RETURN_VALUE 
>>> quit 
Hit any key to close this window... 

Настоящая магия происходит в лексере/парсере.

Лексический анализ, или lexing, разбивает входные данные на отдельные жетоны. Токены - это ключевые слова или «неделимые» элементы. Например, =, if, try, :, for, pass и import - все жетоны Python. Чтобы увидеть, как Python выполняет токенизацию программы, вы можете использовать модуль tokenize.

Поместите код в файл с именем «test.py» и запустить следующий в этом каталоге:

из разметить импорта разметить п = открыт («test.py») разметить (ф.Readline)

Для print "Hello World!" вы получите следующее:

1,0-1,5: NAME 'печать'
1,6-1,19: STRING ' "привет мир"'
1,19-1,20: Newline '\ п'
2,0-2,0: маркер конец ''

После того, как код лексема, это parsed в abstract syntax tree. Конечным результатом является представление байт-кода на основе python вашей программы. Для print "Hello World!" вы можете увидеть результат этого процесса:

from dis import dis 
def heyworld(): 
    print "Hello World!" 
dis(heyworld) 

Конечно все языки ЛЕКС, анализировать, обобщать, а затем выполнить свои программы. Python лексики, анализирует и компилирует байт-код. Затем байт-код «компилируется» (перевод может быть более точным) на машинный код, который затем выполняется. Это основное различие между интерпретируемыми и скомпилированными языками. Скомпилированные языки скомпилируются непосредственно в машинный код из исходного источника, а это значит, что перед компиляцией вам нужно только лекс/разбор, а затем вы можете напрямую выполнить программу. Это означает более быстрое время выполнения (без этапа lex/parse), но это также означает, что для достижения этого начального времени выполнения вам нужно потратить намного больше времени, потому что вся программа должна быть скомпилирована.

+0

Спасибо. :-) Знаете, этот вопрос следует разделить на два. :-) – Omnifarious

+0

Добро пожаловать и спасибо. И вы правы, может быть, кто-то с модой власти может это сделать. –

1

Интерактивный интерпретатор Python не делает многого, что ничем не отличается от любого другого кода Python, который запускается. У него есть какая-то магия, чтобы ловить исключения и обнаруживать неполные многострочные операторы перед их исполнением, чтобы вы могли закончить их ввод, но это все.

Если вам действительно интересно, стандарт code module представляет собой довольно полную реализацию интерактивной подсказки Python. Я думаю, что это не совсем то, что использует Python (то есть, я считаю, реализован на C), но вы можете вникнуть в свой каталог системной библиотеки Python и на самом деле посмотреть, как это делается. Mine's at /usr/lib/python2.5/code.py