19

Do I have to formally define a function before I can use it as an element of a dictionary?

def my_func():
  print 'my_func'

d = {
  'function': my_func
}

I would rather define the function inline. I just tried to type out what I want to do, but the whitespace policies of python syntax make it very hard to define an inline func within a dict. Is there any way to do this?

Matthew Taylor
  • 3,911
  • 4
  • 29
  • 33
  • 4
    Can you explain why you would want to do this? If you want a value back from a function, you can place it into the dictionary using `d['function'] = my_func()`; if you want a function you'll never really need again, you could use a `lambda`. – Makoto Jan 14 '12 at 17:59
  • 2
    A lambda doesn't work for me, because I need statements within the function. The function is not trivial. I want to avoid declaring the function if I can. I want the function declared inline. – Matthew Taylor Jan 14 '12 at 19:04
  • How do you expect to be able to use anything that is not defined at the time (not physical file position) it is to be used? Python defines a function by executing a `def` statement. Python defines your dict by executing your `d = { etc etc etc}`. – John Machin Jan 14 '12 at 23:02
  • 1
    @JohnMachin I don't know how python works under the hood yet. I just know you can do this type of thing in other languages. There must not be a way in python. – Matthew Taylor Jan 15 '12 at 02:06
  • 3
    One reason I want to do this is for networking. I want to have a dictionary of command handlers where the key is the command id with the value being the function to handle that command. I'd rather not pollute the namespace if I don't have to with those function declarations. – leetNightshade Apr 14 '15 at 01:25

7 Answers7

29

The answer seems to be that there is no way to declare a function inline a dictionary definition in python. Thanks to everyone who took the time to contribute.

Matthew Taylor
  • 3,911
  • 4
  • 29
  • 33
  • 3
    This is the most correct answer. While it's nice to know about the five or so roundabout ways to do something similar in Python, there's no way of doing what is so simple in Javascript with anonymous functions other than with lambdas, which are limited to simple functions that can fit on one line. – Michael Bylstra Jan 08 '13 at 08:21
  • I'd like to point the reason the Python does not do what JavaScript does is for readability and a consequence of choosing to use white space to indicate control blocks rather then brackets. It was only after using Python for years I was able to easily grok the "good parts" of JavaScript. – Derek Litz Aug 15 '13 at 16:34
  • 2
    Agreed that JavaScript got this part right, it is just so easy and readable to declare some functions inline with the object that contains them – Josh Petitt Apr 16 '14 at 19:00
  • "which are limited to simple functions that can fit on one line." To be pedantic: whose *logic* can fit in one *expression*. You can use as many line continuation characters, multi-line strings, and/or multi-line parenthesized expressions (including tuple/list/dict/set literals) as you want. – Karl Knechtel May 21 '22 at 19:47
8

Do you really need a dictionary, or just getitem access?

If the latter, then use a class:

>>> class Dispatch(object):
...     def funcA(self, *args):
...         print('funcA%r' % (args,))
...     def funcB(self, *args):
...         print('funcB%r' % (args,))
...     def __getitem__(self, name):
...         return getattr(self, name)
... 
>>> d = Dispatch()
>>> 
>>> d['funcA'](1, 2, 3)
funcA(1, 2, 3)
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • This is a good way if he actually wants to instantiate the objects, hopefully because they contain some useful state. The key point here is, which your example does not show, is the class should contain some useful state to be worth instantiating, otherwise he can just use the method described in my answer to access the functions by string key. – Derek Litz Jan 14 '12 at 18:45
  • @DerekLitz. Practicality beats purity. The OP's question is about inlining functions within a dictionary, which is very close to what a python class already is (i.e. a wrapper around a dict). My example provides the functionality the OP really needs in a readable and easily maintainable form. – ekhumoro Jan 14 '12 at 19:07
  • I think you missed my point. Instantiating the d object above has no meaningful purpose, which can lead to confusion. It adds extra complexity where none is needed (since you have no meaningful state). d appears to behave like a dict at first glance, but in fact does not. You are not using a Class you are using a Dispatch object to provide the functionality, and IMHO as written the Dispatch object adds no meaningful functionality nor simplification. __getitem__ should be used to simplify use of a complex class, by reusing common Python idioms, as sqlalchemy does for queries. – Derek Litz Jan 14 '12 at 20:01
  • @DerekLitz. My `Dispatch` class has the same purpose as the dict in the OP's question: it's a container (or namespace) for functions, which also provides `getitem` access. The key advantage it has over a `dict`, is that it allows functions to be defined inline - which is what the OP asked for. This is really just a simple example of the very common python idiom of duck-typing. It replaces one object (the OP's dict), with another that behaves the same where it matters (i.e. `getitem` access). – ekhumoro Jan 14 '12 at 20:35
  • Yes, this is why I said it was a good way, provided his usage is complex enough to warrant instantiation of a class. I don't see the point in doing it this way otherwise. It's my opinion still, and I haven't seen you present any argument for the additional complexity. I pointed it out for less knowledgeable users who will simply take your code and use it, not knowing there is a simpler way, for simpler use cases. Plainly, I like your way if it is justified by the necessary complexity of the class. – Derek Litz Jan 14 '12 at 20:46
7

You could use a decorator:

func_dict = {}

def register(func):
    func_dict[func.__name__] = func
    return func

@register
def a_func():
    pass

@register
def b_func():
    pass

The func_dict will end up mapping using the entire name of the function:

>>> func_dict
{'a_func': <function a_func at 0x000001F6117BC950>, 'b_func': <function b_func at 0x000001F6117BC8C8>}

You can modify the key used by register as desired. The trick is that we use the __name__ attribute of the function to get the appropriate string.

Booboo
  • 38,656
  • 3
  • 37
  • 60
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
5

Consider using lambdas, but note that lambdas can only consist of one expression and cannot contain statements (see http://docs.python.org/reference/expressions.html#lambda).

e.g.

d = { 'func': lambda x: x + 1 }
# call d['func'](2) will return 3

Also, note that in Python 2, print is not a function. So you have to do either:

from __future__ import print_function

d = {
    'function': print
}

or use sys.stdout.write instead

d = {
    'function': sys.stdout.write
}
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
qiao
  • 17,941
  • 6
  • 57
  • 46
  • I object to the phrasing "must be functions, not procedures". Regardless which of the conflicting definitions of those terms you use, none really fits. The only restriction is that the body of the lambda must be a single expression. –  Jan 14 '12 at 18:17
  • @delnan Thanks for the clarification. Answer edited. I used to take control flows (e.g. `if` `for`) as procedures, thus lead to the ambiguity. – qiao Jan 14 '12 at 18:26
  • Thanks for the note about `print`. – Matthew Taylor Jan 14 '12 at 19:07
4

The point of inlining functions is to blur the distinction between dictionaries and class instances. In javascript, for example, this techinque makes it very pleasant to write control classes that have little reusability. Also, and very helpfully the API then conforms to the well-known dictionary protocols, being self explanatory (pun intended).

You can do this in python - it just doesn't look like a dictionary! In fact, you can use the class keyword in ANY scope (i.e. a class def in a function, or a class def inside of a class def), and it's children can be the dictonary you are looking for; just inspect the attributes of a definition as if it was a javascript dictionary.

Example as if it was real:

somedict = {
    "foo":5,
    "one_function":your method here,
    "two_function":your method here,
}

Is actually accomplished as

class somedict:
    foo = 5
    @classmethod
    def one_method(self):
        print self.foo
        self.foo *= 2;

    @classmethod
    def two_method(self):
        print self.foo

So that you can then say:

somedict.foo          #(prints 5)
somedict.one_method() #(prints 5)
somedict.two_method() #(prints 10)

And in this way, you get the same logical groupings as you would with your "inlining".

3

Some functions can be easily 'inlined' anonymously with lambda expressions, e.g.:

>>> d={'function': lambda x : x**2}
>>> d['function'](5)
25

But for anything semi-complex (or using statements) you probably just should define them beforehand.

ChristopheD
  • 112,638
  • 29
  • 165
  • 179
3

There is no good reason to want to write this using a dictionary in Python. It's strange and is not a common way to namespace functions.

The the Python philosophies that apply here are:

There should be one-- and preferably only one --obvious way to do it.

Combined with

Readability counts.

Doing it this way also makes things hard to understand and read for the typical Python user.

The good things the dictionary does in this case is map strings to functions and namespace them within a dictionary, but this functionality is already provided by both modules and classes and it's much easier to understand by those familiar with Python.

Examples:

Module method:

#cool.py

def cool():
    print 'cool'

Now use the module like you would be using your dict:

import cool
#cool.__dict__['cool']()
#update - to the more correct idiom vars
vars(cool)['cool']()

Class method:

class Cool():
    def cool():
        print 'cool'

#Cool.__dict__['cool']()
#update - to the more correct idiom vars
vars(Cool)['cool']()

Edit after comment below:

argparse seems like a good fit for this problem, so you don't have to reinvent the wheel. If you do decide to implement it completely yourself though argparse source should give you some good direction. Anyways the sections below seem to apply to this use case:

15.4.4.5. Beyond sys.argv

Sometimes it may be useful to have an ArgumentParser parse arguments other than those of sys.argv. This can be accomplished by passing a list of strings to parse_args(). This is useful for testing at the interactive prompt:

15.4.5.1. Sub-commands¶

ArgumentParser.add_subparsers()

Many programs split up their functionality into a number of sub-commands, for example, the svn program can invoke sub-commands like svn checkout, svn update, and svn commit.

15.4.4.6. The Namespace object

It may also be useful to have an ArgumentParser assign attributes to an already existing object, rather than a new Namespace object. This can be achieved by specifying the namespace= keyword argument:

Update, here's an example using argparse

strategizer = argparse.ArgumentParser()
strat_subs = strategizer.add_subparsers()
math = strat_subs.add_parser('math')
math_subs = math.add_subparsers()
math_max = math_subs.add_parser('max')
math_sum = math_subs.add_parser('sum')
math_max.set_defaults(strategy=max)
math_sum.set_defaults(strategy=sum)

strategizer.parse_args('math max'.split())
Out[46]: Namespace(strategy=<built-in function max>)

strategizer.parse_args('math sum'.split())
Out[47]: Namespace(strategy=<built-in function sum>)

I would like to note the reasons I would recommend argparse

  1. Mainly the requirement to use strings that represent options and sub options to map to functions.
  2. It's dead simple (after getting past the feature filled argparse module).
  3. Uses a Python Standard Library Module. This let's others familiar with Python grok what your doing without getting into implementation details, and is very well documented for those who aren't.
  4. Many extra features could be taken advantage of out of the box (not the best reason!).

Using argparse and Strategy Pattern together

For the plain and simple implementation of the Strategy Pattern, this has already been answered very well.

How to write Strategy Pattern in Python differently than example in Wikipedia?

#continuing from the above example
class MathStudent():
    def do_math(self, numbers):
        return self.strategy(numbers)


maximus = strategizer.parse_args('math max'.split(),
                                  namespace=MathStudent())

sumera = strategizer.parse_args('math sum'.split(),
                                 namespace=MathStudent())

maximus.do_math([1, 2, 3])
Out[71]: 3

sumera.do_math([1, 2, 3])
Out[72]: 6
Community
  • 1
  • 1
Derek Litz
  • 10,529
  • 7
  • 43
  • 53
  • What I'm really trying to implement here is the Strategy Pattern. I want a dictionary that contains several levels of options and function, so I can do something like this: `d[option1][option2](args)`. But I want to do it the simplest way possible. In Groovy or JavaScript, you can inline closures within maps. It may be that I need to create a class for the functions I wish to be options within the strategy. – Matthew Taylor Jan 14 '12 at 19:09
  • 2
    There are many good reasons for wanting to do what the OP is doing. It's a very common technique known as [Dynamic Dispatch](http://en.wikipedia.org/wiki/Dynamic_dispatch). – ekhumoro Jan 14 '12 at 19:15
  • @ekhumoro The Python language implements dynamic dispatch... the OP is not implementing dynamic dispatch... – Derek Litz Jan 14 '12 at 20:17
  • @MatthewTaylor I think argparse, though typically for command line usage, will do just what you want. – Derek Litz Jan 14 '12 at 20:17
  • 1
    @DerekLitz. Yes, sorry. Of course I meant [Dispatch Table](http://en.wikipedia.org/wiki/Dispatch_table). – ekhumoro Jan 14 '12 at 20:48
  • @ekhumoro Ah, :) I just realized though, I should have been more specific in my statement (no reason to want to do this using a plain dictionary in Python). – Derek Litz Jan 14 '12 at 20:55
  • @DerekLitz I see it. But to make my strategy thing work, it needs to be general enough that it doesn't require argument definition. If I could define a sub parser without specifying any parameters, it could work. – Matthew Taylor Jan 16 '12 at 05:24
  • @MatthewTaylor Yeah, my example was a bit thrown together. I updated it to be closer to what you wanted with your example in the comments. – Derek Litz Jan 16 '12 at 06:57