[BangPypers] BangPypers Digest, Vol 86, Issue 16

Okan bhan rajalokan at gmail.com
Mon Oct 27 16:47:28 CET 2014


Hi Noufal,

Thanks for responding and helping me explaining these. I see lots of my
doubts are clear, except last (c). My explanations are inline.

> A simple implementation of this is:
> >
> > class SimpleMapping:
> >   def __init__(self, items):
> >     self._items = items
> >
> >   def __getitems__(self, subitem):
> >     print('*' * 20)
> >     for item in self._items:
> >       if subitem in item:
> >         return True
> >     return False
>
> This is broken in two ways
> 1. The magic method is __getitem__ (not __getitems__). I'm assuming it's
>    a typo otherwise, the rest of your code will not work even as you've
>    mentioned.
>
Yes. Indeed it should be __getitem__. It was typo while manually typing in
gmail.


> 2. A mapping object is similar to a dictionary. It should
>    return the actual object if you try to access it. Not a boolean True
>    or False.
>
Agree with you. Ideally it should have returned object and I can test by
`if obj` or `if not obj`. Lets assume for now I want to continue with this.

>
> The mapping objects given are specifications of the locals and globals
> in which you will evaluate your string. Consult help("eval") for more
> information.
>
>     eval(source[, globals[, locals]]) -> value
>
Makes sense. So essentially I'm trying to evaluate and expression ('foo' or
'foo and bar') against a local mapping. Not fully confident but I can see
the use of locals & globals here.


> Look at my comment above. Your Mapping object is flawed and all these
> responses are quite wrong.
>

Yes. Sorry for the typo. Without typo these will be the expected answers.

>
> >
> >>>>  eval('5.6', {}, mapping)
> >         5.6
>
> I'm not sure how this will work. In my case, I see this.
>
> >>> mapping = SimpleMapping(set(['aaa', 'bbb', 'ccc']))
> >>> eval('5.6', {}, mapping)
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: locals must be a mapping
> >>>
>
> That is strange, I'm still getting 5.6 as result. I tried on different
python versions as well.

>
> >>>>  eval('aa.a', {}, mapping)
> >         AttributeError: 'bool' object has no attribute 'a'
> >
>
> > My doubts are:
> > a. Why it didn't run for numeric 5.6? Also why is dot separated '5.6' any
> > different to 'aa.a'? I looked around on eval documentation and examples
> but
> > couldn't find use of eval with a mapping.
>
> 5.6 is not an attribute access. 5.6 is is a floating point number. aa.a
> is an attribute access. It will first return False for `aa` since there
> is no such key and your code returns False and then try to access `a` in
> the Boolean which, predictably, fails.
>
> Big thanks for this. Let me explain what, I understood here.
Lets say statement we want to evaluate is `eval(expression)`. Python starts
checking 'expression' against each datatype starting from int -> float ->
str. For all native datatypes, it will return corresponding result. Finally
if it doesn't matches any of native types, tries to access against
mapping(or any other object in local or global scope).

Which is the reason,
>> type(eval('5.6'))   is float
>> type(eval("'5.6'") is str  (expression is under double quotes).

For the case of eval('aa.a'), it first tries to match 'aa'. This is not any
native type so goes to check in mapping and finds it True. Then tries to
find attribute 'a' of returned type (a boolean). Hence boolean object has
no attribute 'a'.


> > b. I see many blogs suggesting to refrain from using eval unless
> > absolutely needed. Is this one use case we must use it? Do we have any
> > better way to evaluate this?
>
> I'm not sure what exactly you want to do. Can you explain again?
>
I came across blog posts suggesting not to use eval mostly as it can lead
to un-secure code. So wanted to check should I put efforts in solving this
with 'eval' or not. Not an option now. I will explain my problem in next
question.

>
> > c. If indeed, I have to evaluate a string containing dots, how to do
> > in the above scenario?
>
> The object whose attribute you're trying to access should be there in
> the locals or globals and then should have an attribute with the given
> name. It will work then.
> [...]
>
yes. If my above observation is correct, this is clear to me. I will try to
explain my problem:

I have a list of strings ( say ['aaa', 'bbb', '4.5-'] ) and I want to check
if an expression (say 'aa') is a substring of any of this item.

Simply:   all([ item for item in ['aaa', 'bbb', '4.5-'] if 'aa' in item])
  ==> gives True as 'aa' is a substring of 'aaa'.

But my expression can be a logical expression like 'aa or bb',  'aa and
bbb', 'aaa and 4.5' etc.

This works in my above implementation. If we assume mapping object from
above. Writing it again here for ease.

> class SimpleMapping:
>   def __init__(self, items):
>     self._items = items
>
>   def __getitem__(self, subitem):
>     for item in self._items:
>       if subitem in item:
>         return True
>     return False

>>> mapping = SimpleMapping(set(['aaa', 'bbb', '4.5-']))

>>>   eval('aa and bb', {}, mapping)
True                                                          # since both
are substring
>>>  eval('aa and bbb', {}, mapping)
True                                                          # again since
both are substring
>>>  eval('aa and bbbb', {}, mapping)
False                                                         # 'bbbb'
fails.

So till now it works fine for my case. But in case if I want to check for
'4.5' in this.

>>> eval('aaaa or bbbb or 4.5')
4.5                                                           # Fails for
'aaaa', fails for 'bbbb' but evaluates '4.5' as 4.5. Something I don't
want. It should be
             checking 4.5 against mapping and I want a boolean True here.

If I am unclear, I will repeat my problem again.

Problem:   How can I evaluate a logical expression, in python where
expression contains a dot ('.').

Analysis:    First thing strike in my mind is to use eval but that fails on
expression with a dot. My tries are:

*Try A:*  I was thinking something like, transform '.' to '#' (or anything
else). For eg.

'aaaa or bbbb or 4.5' becomes 'aaaa or bbbb or 4#5'.
Now if eval('4#5') goes to check inside our mapping we are good. We can
transform back and evaluate. But sadly eval('4#5') gives 4. Tried with many
characters, any hits here?

*Try B:* Other option is extracting operators out of string ( by list
comprehension or using regular expression) and operator on these string
components.
Is there any direct way to get logical operators from string. I found (c for
c in s if c in '+-/*()_') to get mathematical operators.

I will try this option tonight.

Sorry for long reply and redundant answer but I wanted to be clear. If I
was able to explain properly can you suggest anything here?

Thanks & Regard
Alok


More information about the BangPypers mailing list