1

In Python it is easy to create new functions programmatically. How would I assign this to programmatically determined names in the current scope?

This is what I'd like to do (in non-working code):

obj_types = ('cat', 'dog', 'donkey', 'camel')
for obj_type in obj_types:
    'create_'+obj_type = lambda id: id

In the above example, the assignment of lambda into a to-be-determined function name obviously does not work. In the real code, the function itself would be created by a function factory.

The background is lazyness and do-not-repeat-yourself: I've got a dozen and more object types for which I'd assign a generated function. So the code currently looks like:

create_cat   = make_creator('cat')
# ...
create_camel = make_creator('camel')

The functions create_cat etc are used hardcoded in a parser.

If I would create classes as a new type programmatically, types.new_class() as seen in the docs seems to be the solution. Is it my best bet to (mis)use this approach?

7
  • You can create a variable with locals()[name] = value Commented Apr 24, 2013 at 13:55
  • related: generating variable names on fly in python Commented Apr 24, 2013 at 13:56
  • @Vaughn: Oh! That is simple, indeed. Why not convert it into an answer? Commented Apr 24, 2013 at 13:56
  • @VaughnCato: you shouldn't use locals() in that way. Commented Apr 24, 2013 at 13:57
  • 1
    @Vaughn: To provide the reasoning behind J.F.'s rejection of using locals() that way, here's what the docs think about that: "Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter" Commented Apr 24, 2013 at 14:05

2 Answers 2

3

One way to accomplish what you are trying to do (but not create functions with dynamic names) is to store the lamda's in a dict using the name as the key. Instead of calling create_cat() you would call create['cat'](). That would dovetail nicely with not hardcoding names in the parser logic as well.

Sign up to request clarification or add additional context in comments.

1 Comment

This answer and the comment by J.F. Sebastian pushed me to use a class with dynamically defined methods. Will post the code in an answer.
0

Vaughn Cato points out that one could just assign into locals()[object_type] = factory(object_type). However the Python docs prohibit this: "Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter"

D. Shawley points out that it would be wiser to use a dict() object which entries would hold the functions. Access would be simple by using create['cat']() in the parser. While this is compelling I do not like the syntax overhead of the brackets and ticks required.

J.F. Sebastian points to classes. And this is what I ended up with:

# Omitting code of these classes for clarity
class Entity:
    def __init__(file_name, line_number):
        # Store location, good for debug, messages, and general indexing

# The following classes are the real objects to be generated by a parser
# Their constructors must consume whatever data is provided by the tokens
# as well as calling super() to forward the file_name,line_number info.
class Cat(Entity): pass
class Camel(Entity): pass

class Parser:
    def parse_file(self, fn):
        # ...

        # Function factory to wrap object constructor calls
        def create_factory(obj_type):
            def creator(text, line_number, token):
                try:
                    return obj_type(*token,
                                    file_name=fn, line_number=line_number)
                except Exception as e:
                    # For debug of constructor during development
                    print(e)
            return creator

        # Helper class, serving as a 'dictionary' of obj construction functions
        class create: pass
            for obj_type in (Cat, Camel):
                setattr(create,
                        obj_type.__name__.lower(),
                        create_factory(obj_type))

        # Parsing code now can use (again simplified for clarity):
        expression = Keyword('cat').setParseAction(create.cat)

This is helper code for deploying a pyparsing parser. D. Shawley is correct in that the dict would actually more easily allow to dynamically generate the parser grammar.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.