Exec inside a class method to call other class methods?

Discussion in 'Python' started by Matthew Dubins, Dec 25, 2008.

  1. 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)
     
    Matthew Dubins, Dec 25, 2008
    #1
    1. Advertising

  2. Matthew Dubins

    Guest

    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
     
    , Dec 25, 2008
    #2
    1. Advertising

  3. Matthew Dubins

    Carl Banks Guest

    On Dec 25, 12:22 pm, Matthew Dubins <> wrote:
    > 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:
    > ----------
    > def parse(self, data, data_type)
    >     exec "self.__parse_%s(data)" % data_type
    > ----------


    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
     
    Carl Banks, Dec 25, 2008
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David Dorward
    Replies:
    1
    Views:
    2,128
    Isofarro
    Jun 28, 2003
  2. Pawel_Iks
    Replies:
    3
    Views:
    536
    terminator
    Jul 31, 2007
  3. Kenneth McDonald
    Replies:
    5
    Views:
    322
    Kenneth McDonald
    Sep 26, 2008
  4. Guillermo Riojas
    Replies:
    0
    Views:
    172
    Guillermo Riojas
    Nov 26, 2010
  5. Random Task
    Replies:
    12
    Views:
    652
    Joe Smith
    Dec 4, 2005
Loading...

Share This Page