Exec inside a class method to call other class methods?

M

Matthew Dubins

Hello all,

I have made a python script to upload contact information from an excel
worksheet to an online database. One part of the program that really
tripped me up was when I wanted to call specific class methods that I
had made to deal with specific types of contact information (Parent's
name, Child's name, Phone #, etc). My first thought was to make it easy
using the exec statement.

The code (a method within a class) looked like this:
----------
def parse(self, data, data_type)
exec "self.__parse_%s(data)" % data_type
----------
The data_type variable contains strings that exactly match the spellings
of the 2nd word in the titles of the class methods that I wanted to call
for each data_type inputted into the parse function. For some reason,
*it didn't work*. Alternately, I found the ugly code shown below to be
functional. As you can see, for each data_type, I call the
corresponding class method that I've specified. Please help me to
transform my ugly functional code into concise functional code. :)

Thanks,
Matthew
----------
def parse(self, data, data_type):
if data_type == 'nocall':
self.__parse_nocall(data)
elif data_type == 'DOB':
self.__parse_DOB(data)
elif data_type == 'gender':
self.__parse_gender(data)
elif data_type == 'Prematurity':
self.__parse_Prematurity(data)
elif data_type == 'email':
self.__parse_email(data)
elif data_type == 'languages':
self.__parse_languages(data)
elif data_type == 'phone':
self.__parse_phone(data)
elif data_type == 'cname':
self.__parse_cname(data)
elif data_type == 'pname':
self.__parse_pname(data)
elif data_type == 'address':
self.__parse_address(data)
elif data_type == 'duedate':
self.__parse_dudedate(data)
 
B

bearophileHUGS

Matthew Dubins:
def parse(self, data, data_type)
    exec "self.__parse_%s(data)" % data_type
For some reason, *it didn't work*.

You can try defining this class attribute:
data_types = set("nocall DOB gender Prematurity email languages phone
cname pname address duedate".split())

And then a possible method can be:

def parse(self, data, data_type):
if data_type in Classname.data_types:
getattr(self, "__parse_" + data_type)(data)

If performance isn't a problem you can move data_types inside the
parse method.
If performance is a problem, data_types may become a dict, whose
values are references to the methods. That you can use as:

def parse(self, data, data_type):
if data_type in data_types:
data_types[data_type](data)

Or even as (but may be a little slower):

def parse(self, data, data_type):
data_types.get(data_type, lambda x: None)(data)

Bye,
bearophile
 
C

Carl Banks

Hello all,

I have made a python script to upload contact information from an excel
worksheet to an online database.  One part of the program that really
tripped me up was when I wanted to call specific class methods that I
had made to deal with specific types of contact information (Parent's
name, Child's name, Phone #, etc).  My first thought was to make it easy
using the exec statement.

Bad idea.

You should not use exec and eval unless it's your intention to allow
the user to run arbitrary Python code.

Any other time you think you need to use exec (such as when
constructing an attribute name), you should find out whether there's
another way to do it, and there usually is. Such is the case here;
there is a function, getattr, that does what you want. See below.

The code (a method within a class) looked like this:

I presume you're aware that the double underscore is a sort of private
variable. It causes the Python compiler to mangle the name when used
inside a class definition (it converts __parse_email to
_Parser__parse_email under the covers, assuming Parser is the class
name).

Well, it turns out that the Python compiler doesn't know that the
string in the exec statement is part of the class, so it doesn't
mangle the name for you. If you want to get the method name from
within the exec statement, you have to mangle it by hand, like so:

def parse(self,data,data_ype):
exec "self._Parser__parse_%s(data)" % data_type


However, you shouldn't do that. Instead, use getattr, which is a
function that was designed exactly for that purpose, and is much less
of a security risk.

def parse(self,data,data_ype):
method = getattr(self,"_Parser__parse_%s" % data_type)
method(data)

Note that you still have to use the mangled version of the name with
getattr (getattr doesn't know it's inside the class, either). And,
although this is much safer than exec, you should still be wary of
passing user input into it.


Here is why you shouldn't use exec for this. You say this program
reads data from a spreadsheet. What would happen if, in the data type
field, a malicious user were to enter the following string (without
surrounding quotes):

"email(); import os; os.system('format c:');"

Your exec statement would blindly execute this, oops, it happened to
format your hard disk. exec and eval are prone to security holes like
this, and they usually entail a performance penalty (since the
compiler has to be invoked for every call), so please use them only
what they were intended for.


Carl Banks
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top