2013-05-03 4 views
0

нормально, я могу использовать Eval так:Eval не только со словарями

new_dict={'color':'green'} 
eval("color=='green'",new_dict) 

в этом случае она будет возвращена истина, и поэтому я знал бы, что цвет в new_dict на самом деле зеленый. Теперь я нашел код, в котором кто-то хотел использовать eval, но с более общим объектом вместо dict. В следующем коде пояснит, что этот человек сделал:

class myStuff(object): 
    def __init__(self): 
     self.color = "green" 

class Dummy(dict): 
    def __init__(self, node): 
     dict.__init__(self) 
     self.node = node 

    def __getitem__(self, key): 
     return(getattr(self.node, key)) 


node=myStuff() 
new_dict = Dummy(node) 
print eval("color=='green'",new_dict) 

Интересно, сейчас - как же разработчик кода выше знает, что Eval использует метод __getitem__ на new_dict для цвета? Я нашел документацию и функцию справки python, но я не могу найти пошаговую документацию для метода (или фактического кода), так что я никогда бы не придумал идею сделать именно то, что выше код делает. Или плохо использовать вышеупомянутый метод, потому что никто действительно не знает, как метод eval был реализован, и поэтому код может возникнуть с некоторыми странными ошибками в будущем?

EDIT: Вот почему eval используется в программе: представьте, что вы получили 20 объектов myStuff в списке mylist, и вы хотите их фильтровать по желтому цвету, тогда можно просто вызвать [n для n в mylist if eval (query, Dummy (n)] с `query =" color == 'yellow' ". Я не эксперт, но я просто хочу знать, может ли этот метод привести к проблемам.

+7

Почему вы используете 'eval' таким образом? Что не так с 'd ['color'] == 'green''? – Blender

+0

Совершенно разумным вопросом является вопрос о том, как работает * eval * (т. Е. Оценки выполняются в пространстве имен, которое может быть произвольным отображением с использованием \ _ \ _ getitem \ _ \ _). Этот метод является универсальным и позволяет легко реализовать последовательные вычисления, такие как те, которые используются в электронных таблицах (что нелегко сделать с помощью обычного словаря). –

+2

@ RaymondHettinger: Да, разумно задаться вопросом, как работает 'eval', и на самом деле было бы здорово, если бы кто-то написал учебник, который ищет OP. Но это не меняет того факта, что, как и 99% людей, у которых есть проблема с 'eval', OP не должен был использовать его здесь. Вот почему всегда стоит спросить: «Почему вы хотите использовать« eval »здесь?» первый. Наихудший сценарий заключается в том, что у них есть хороший ответ (который может включать «потому что я хочу понять« eval »), что делает их вопрос более полезным для будущих читателей, не так ли? – abarnert

ответ

2

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

Справочная документация для последовательностей и отображений находится в Data model, но лучшим местом для начала является, вероятно, модуль collections.abc и ссылки оттуда.

Резюмируя основную идею, когда вы пишете код, как это:

foo[bar] 

Python переводит его в *:

foo.__getitem__(bar) 

Там нет ничего плохого с определением __getitem__ для имитации dict.

И сделать это, чтобы создать объект, который рассматривает его атрибуты как элементы dict, является таким общим шаблоном, что он имеет имя («attrdict»).

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


В вашем конкретном случае, нет никаких оснований использовать eval в первую очередь. Вместо этого:

eval("color=='green'",new_dict) 

Просто сделай это:

new_dict['color']=='green' 

Одна из причин Python новички (особенно те, кто вырос на старых версиях PHP, Tcl или JavaScript) часто требуется использовать eval является получить выражение, которое они могут легко пройти. Но в Python (и, если на то пошло, современные PHP и JS), функции являются первоклассными значениями, так же легко переносится как строки, и, в отличие от строк, конечно, они могут быть вызваны. Вы можете создавать именованные или лямбда-функции или использовать partial, закрыть любые локальные переменные, которые вы хотите, и т. Д.

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

Таким образом, вместо того, чтобы что-то вроде этого:

expr = "color=='green'" 
# ... 
eval(expr, new_dict) 

... просто это сделать:

expr = lambda x: x.color=='green' 
# ... 
expr(new_dict) 

В вас отредактированный вопрос:

Вот почему Eval является используется в программе: представьте, что вы получили 20 объектов myStuff в списке mylist, и вы хотите отфильтровать их по желтому цвету, тогда можно просто вызвать [n для n в mylist, если eval (query, Dummy (n)] с `query =" color == 'yellow' ".

Таким образом, вы, вероятно делаете что-то вроде этого:

query = "color=={}'.format(color) 
# ... 
[n for n in mylist if eval(query, Dummy(n)] 

Но вы могли бы так же легко сделать это:

[n for n in mylist if n.color == color] 

Даже если вам нужно что-то более динамичное, вы можете строить функции динамически, еще проще, чем строки:

query = lambda n: n.color == color 
[n for n in mylist if query(n)] 

В самом деле, если вы действительно хотите, вы можете даже сделать это полностью функциональны:

filter(compose(partial(operator.eq, color), attrgetter('color')), mylist) 

Но большая вещь о Python является то, что вы не должны идти полностью функциональным или полностью императив, вы можете написать что-то на полпути между - или 25% или 75%, что бы ни было проще читать и писать.


Тем:

Или это плохо использовать вышеупомянутый метод, потому что никто не знает, как реализован метод Eval и, следовательно, код может придумать некоторые странные ошибки в будущем ?

Нет, это почти никогда проблема.

Во-первых, documentation for eval, как правило, достаточно, чтобы точно предсказать, что он будет делать, и все реализации Python должны следовать этой документации.

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


* Это не совсем точно. реальный код фактически ищет __getitem__ в классе, а не объекте (немного по-другому для старого стиля и новых классов в 2.x), а также обрабатывает типы расширений из модулей C/Java-пакетов/независимо от того, что подходит для вашей реализации Python, сделок с фрагментами (по-разному в 2.x против 3.x) и т. д. Но это основная идея.

** eval кода постепенно переработан на протяжении многих лет, так что в этот момент вы могли бы в значительной степени переописать eval в нескольких строках чистого Python с помощью ast модуля и друзей, или несколько строк C с использованием PyEval*, поэтому вам трудно указать точную строку кода, чтобы не задумываться о том, какую реализацию и версию вы волнуете.

+0

Эй, благодарю вас за ваш очень длинный ответ. Из моего примера использовать eval не было смысла. Я просто хотел мотивировать код. Я написал в разделе редактирования, где eval фактически используется в коде. Вы бы сказали, что его не рекомендуется использовать там? – Adam

+0

Да, это по-прежнему не рекомендуется. Позвольте мне отредактировать ответ, чтобы объяснить, как это сделать. – abarnert

2

__getitem__ - это то, что dict s используйте для извлечения элементов по их ключу. Переопределяя __getitem__ и заставляя его возвращать атрибуты self.node, вы по существу превращаете node в словарь.

Простым способом было бы просто использовать атрибут __dict__, который делает то же самое:

print eval("color=='green'", node.__dict__) 

Но на самом деле, не используйте это. Пожалуйста. eval() редко является подходящим инструментом для работы, и это прекрасный пример того, где вам не нужно использовать eval.

+0

Спасибо за ваш ответ. На самом деле я сейчас работаю над готовой программой на github, и я пытаюсь понять, что сделал мне человек в коде (он ушел сейчас, но по крайней мере я могу связаться с ним по телефону). Представьте, что у вас есть 20 объектов myStuff в списке «mylist», и вы хотите отфильтровать их по цвету желтый, тогда просто можно вызвать '[n для n в mylist if eval (query, Dummy (n)]' или как вы say '[n для n в mylist, если eval (query, n .__ dict__]' с 'query =" color == 'yellow' ".Я не эксперт, но я просто хочу знать, может ли этот метод привести к проблемам. Почему вы рекомендуете не использовать его? – Adam

Смежные вопросы