tags:

views:

192

answers:

6

I'm aware of using globals(), locals() and getattr to referance things in Python by string (as in this question) but unless I'm missing something obvious I can't seem to use this with calling types.

e.g.:

In [12]: locals()['int']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)

e:\downloads_to_access\<ipython console> in <module>()

KeyError: 'int'

In [13]: globals()['int']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)

e:\downloads_to_access\<ipython console> in <module>()

KeyError: 'int'

getattr(???, 'int')...

What's the best way of doing this?

+1  A: 

The issue here is that int is part of the __builtins__ module, not just part of the global namespace. You can get a built-in type, such as int, using the following bit of code:

int_gen = getattr(globals()["__builtins__"], "int")
i = int_gen(4)
# >>> i = 4

Similarly, you can access any other (imported) module by passing the module's name as a string index to globals(), and then using getattr to extract the desired attributes.

mipadi
There is a proper way to access the `__builtin__` module, and that way is to import it. Using `__builtins__` is not recommended, as described here: docs.python.org/library/__builtin__.html
ΤΖΩΤΖΙΟΥ
+1  A: 
getattr(__builtins__,'int')
Jonathan Feinberg
`__builtins__` is an implementation detail. See ~utnubus answer.
kaizer.se
+8  A: 

There are locals,globals, and then builtins. Perhaps you are looking for the builtin:

import __builtin__
getattr(__builtin__,'int')
unutbu
No need to import: it's loaded by default.
mavnn
@mavnn: this is correct. Loaded by default is, however logical, an implementation detail. (Especially under which name used)
kaizer.se
Please see http://docs.python.org/library/__builtin__.html
kaizer.se
Ah, I see. Thank you.
mavnn
A: 

If you have a string that is the name of a thing, and you want the thing, you can also use:

thing = 'int'
eval(thing)

Keep in mind though, that this is very powerful, and you need to understand what thing might contain, and where it came from. For example, if you accept user input as thing, a malicious user could do unlimited damage to your machine with this code.

Ned Batchelder
As mentioned in a comment, this is reading values from a csv file I don't have complete control over. eval definately isn't the way to go here!
mavnn
Are the people supplying the CSV file evil, psychotic, sociopaths? Exactly how evil are they? If they cannot be trusted to provide type names, how can you trust them to do *anything*? If you can trust them to provide data, you can trust them to provide type names.
S.Lott
My, you're in a disagreeable mood today Mr. Lott. Did I offend you in some way? No, they're not any of the above but why make the code more vulnerable than it has reason to be?
mavnn
@mavnn: There's no "vulnerability" in eval unless the people supplying the data are known sociopaths. I prefer less magic, and `eval` involves the least magic. The "vulnerability issue" is a load of malarkey except in a web application where there actually are sociopaths. Otherwise, go with the simplest possible thing.
S.Lott
mavnn will have to make his own evaluation, of course. I'll just add that if you only are trying to look up type names, then looking into __builtin__ may be simpler. If you want to use eval and are queasy about it, then a simple regex to check that the string is only alphanumeric will prevent malice.
Ned Batchelder
+7  A: 

You've already gotten a solution using builtins, but another worthwhile technique to hold in your toolbag is a dispatch table. If your CSV is designed to be used by multiple applications written in multiple languages, it might look like this:

Integer,15
String,34
Float,1.0
Integer,8

In such a case you might want something like this, where csv is a list of tuples containing the data above:

mapping = {
    'Integer': int,
    'String': str,
    'Float': float,
    'Unicode': unicode
}
results = []
for row in csv:
    datatype = row[0]
    val_string = row[1]
    results.append(mapping[datatype](val_string))
return results

That gives you the flexibility of allowing arbitrary strings to map to useful types. You don't have to massage your data to give you the exact values python expects.

jcdyer
+2  A: 

Comments suggest that you are unhappy with the idea of using eval to generate data. looking for a function in __builtins__ allows you to find eval.

the most basic solution given looks like this:

import __builtin__

def parseInput(typename, value):
    return getattr(__builtins__,typename)(value)

You would use it like so:

>>> parseInput("int", "123")
123

cool. works pretty ok. how about this one though?

>>> parseInput("eval", 'eval(compile("print \'Code injection?\'","","single"))')
Code injection?

does this do what you expect? Unless you explicitly want this, you need to do something to prevent untrustworthy inputs from poking about in your namespace. I'd strongly recommend a simple whitelist, gracefully raising some sort of exception in the case of invalid input, like so:

import __builtin__

def parseInput(typename, value):
    return {"int":int, "float":float, "str":str}[typename](value)

but if you just can't bear that, you can still add just a bit of armor by verifying that the requested function is actually a type:

import __builtin__

def parseInput(typename, value):
    typector = getattr(__builtins__,typename)
    if type(typector) is type:
        return typector(value)
    else:
        return None
TokenMacGuy
I'm in the unusual circumstance that I have very 'trustworthy' data (grabbed direct from a mainframe), while the csv file with the processing information is vulnerable to modification by a large number of people. +1 for the useful advice, though. I'll bear it in mind for future projects.
mavnn