Calling types via their name as a string in Python
Asked Answered
G

6

5

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?

Gnaw answered 30/10, 2009 at 15:2 Comment(2)
The type int is named int. You don't need to find it in locals() with a complicated lookup. Just say int. What are you trying to do? Why would you ever need to find a built-in object using a string when you know the name of the object?Dobbins
Because in my actual code, I am reading 'int' from a csv file. The value being read in might be any callable that takes a string (so any function in the global/local namespace or any of the various types that can take a string in the constructor).Gnaw
B
12

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

import __builtin__
getattr(__builtin__,'int')
Burkhart answered 30/10, 2009 at 15:12 Comment(2)
No need to import: it's loaded by default.Gnaw
@mavnn: this is correct. Loaded by default is, however logical, an implementation detail. (Especially under which name used)Priceless
L
7

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.

Lepidopteran answered 30/10, 2009 at 15:47 Comment(0)
K
3
getattr(__builtins__,'int')
Kimmi answered 30/10, 2009 at 15:9 Comment(1)
__builtins__ is an implementation detail. See ~utnubus answer.Priceless
L
2

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.

Laticialaticiferous answered 30/10, 2009 at 15:8 Comment(1)
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__.htmlMorningglory
H
2

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
Haldes answered 30/10, 2009 at 23:49 Comment(1)
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.Gnaw
U
1

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.

Umbel answered 30/10, 2009 at 15:17 Comment(5)
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!Gnaw
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.Dobbins
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?Gnaw
@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.Dobbins
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.Umbel

© 2022 - 2024 — McMap. All rights reserved.