53

JavaScript has object literals, e.g.

var p = {
  name: "John Smith",
  age:  23
}

and .NET has anonymous types, e.g.

var p = new { Name = "John Smith", Age = 23}; // C#

Something similar can be emulated in Python by (ab)using named arguments:

class literal(object):
    def __init__(self, **kwargs):
        for (k,v) in kwargs.iteritems():
            self.__setattr__(k, v)
    def __repr__(self):
        return 'literal(%s)' % ', '.join('%s = %r' % i for i in sorted(self.__dict__.iteritems()))
    def __str__(self):
        return repr(self)

Usage:

p = literal(name = "John Smith", age = 23)
print p       # prints: literal(age = 23, name = 'John Smith')
print p.name  # prints: John Smith

But is this kind of code considered to be Pythonic?

Eli Courtwright
  • 186,300
  • 67
  • 213
  • 256
ShinNoNoir
  • 2,274
  • 1
  • 18
  • 24

8 Answers8

74

Why not just use a dictionary?

p = {'name': 'John Smith', 'age': 23}

print p
print p['name']
print p['age']
Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
  • 9
    You can also use `dict` function: `p = dict(name='John Smith', age=23)` – Tomasz Wysocki Jul 26 '10 at 14:13
  • Well, yes, dicts are more or less equivalent. Also, Tomasz' suggestion is a nice alternative way to construct them. Still, accessing the 'fields' of a dict is not as nice as in my code example, although it's just a small syntactic inconvenience. – ShinNoNoir Jul 26 '10 at 17:05
  • 2
    While I concur, I think there's a philosophical difference at work. The prevailing philosophy in Python is that if you need an object with `.name` and `.age` members, you should explicitly create that object, with all its constructors, etc. – Wayne Werner Jul 26 '10 at 17:24
  • 3
    yes, of course - but doing all that in interactive interpreter while testing/prototyping/having fun is a bit of overkill. You know you'll explicitly define it later, but *right now* you want to get to the point... – cji Jul 26 '10 at 17:28
  • 3
    True story. If that's the case, I'd go ahead and put that function in some "tools" file and `from tools import literals` or some such whenever I was playing around in the interpreter - because when that's all you're doing, it doesn't matter terribly if it's very pythonic (many people use 1 space for indentation - decidedly un-pythonic [in real code], for example). Plus it's a good idea anyway to have a "toolbox" of python tweaks that you use fairly often. – Wayne Werner Jul 26 '10 at 17:35
  • A dictionary doesn't provide attribute references, you have to use look-up keys. If I have a lot of code using attributes (for no good reason) and I need to transition the code supporting those references I may want to create an object literal (not a `dict`). – NeilG Mar 20 '23 at 23:10
43

Have you considered using a named tuple?

Using your dict notation

>>> from collections import namedtuple
>>> L = namedtuple('literal', 'name age')(**{'name': 'John Smith', 'age': 23})

or keyword arguments

>>> L = namedtuple('literal', 'name age')(name='John Smith', age=23)
>>> L
literal(name='John Smith', age=23)
>>> L.name
'John Smith'
>>> L.age
23

It is possible to wrap this behaviour into a function easily enough

def literal(**kw):
    return namedtuple('literal', kw)(**kw)

the lambda equivalent would be

literal = lambda **kw: namedtuple('literal', kw)(**kw)

but personally I think it's silly giving names to "anonymous" functions

Bob Stein
  • 16,271
  • 10
  • 88
  • 101
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • How about PyRec? (http://www.valuedlessons.com/2009/10/introducing-pyrec-cure-to-bane-of-init.html) – Plumenator Jul 26 '10 at 15:44
  • Yes, I have looked at the named tuple, but doesn't it violate the DRY principle? Constructing an object requires you to specify the names of each field twice. Of course, this can be circumvented: literal = lambda **kwargs: namedtuple('literal', ' '.join(kwargs.keys()))(**kwargs); p = literal(name='John Smith', age=23) (Code not tested, I don't have Py2.6 on my machine) – ShinNoNoir Jul 26 '10 at 16:34
  • 1
    @ShinNoNoir, creating a factory function is a little easier than your suggestion as `namedtuple` is quite flexible about it's second parameter – John La Rooy Jul 27 '10 at 00:43
  • @gnibbler, re: lambdas, I used them in my comment because of formatting constraints. Also, giving names to lambda expressions isn't that silly if you consider the lambda calculus. Re: namedtuple, I wasn't aware about its flexibility. – ShinNoNoir Jul 27 '10 at 05:01
  • Is there a reason why you couldn't just do `namedtuple('MyTypeName', [*someDict])(**someDict)`? e.g., with someDict = `{'a':10, 'b':20, 'c':30}` – jrh May 30 '19 at 14:35
15

From ActiveState:

class Bunch:
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

# that's it!  Now, you can create a Bunch
# whenever you want to group a few variables:

point = Bunch(datum=y, squared=y*y, coord=x)

# and of course you can read/write the named
# attributes you just created, add others, del
# some of them, etc, etc:
if point.squared > threshold:
    point.isok = 1
Don Kirkby
  • 53,582
  • 27
  • 205
  • 286
pillmuncher
  • 10,094
  • 2
  • 35
  • 33
  • 1
    Excellent. exactly what I need to create object literals when mocking objects with attributes for unit testing. Best answer IMHO :) Added this technique to my Pythonic repertoire. Thanks! – pink spikyhairman Mar 19 '20 at 11:57
  • @AndrewBullock, I'd say this is the most "pythonic" solution. The named tuple is probably the canonical knee-jerk way to do it for most, but I like the simplicity of this one. – NeilG Mar 21 '23 at 01:19
3

I don't see anything wrong with creating "anonymous" classes/instances. It's often very convienient to create one with simple function call in one line of code. I personally use something like this:

def make_class( *args, **attributes ):
    """With fixed inability of using 'name' and 'bases' attributes ;)"""
    if len(args) == 2:
        name, bases = args
    elif len(args) == 1:
        name, bases = args[0], (object, )
    elif not args:
        name, bases = "AnonymousClass", (object, )
    return type( name, bases, attributes )

obj = make_class( something = "some value" )()
print obj.something

For creating dummy objects it works just fine. Namedtuple is ok, but is immutable, which can be inconvenient at times. And dictionary is... well, a dictionary, but there are situations when you have to pass something with __getattr__ defined, instead of __getitem__.

I don't know whether it's pythonic or not, but it sometimes speeds things up and for me it's good enough reason to use it (sometimes).

cji
  • 6,635
  • 2
  • 20
  • 16
  • Interesting approach, except `make_class` is actually constructing an object. There's also the problem that you cannot have an attribute called `name` with your approach. (But I notice I have a similar problem in my `literal` class... I can't have an attribute called `self`). – ShinNoNoir Jul 26 '10 at 17:36
  • 2
    Actually it's 'class object' - it's instantiated here: `make_class(...)()`. I think? And as for name clashes - True. – cji Jul 26 '10 at 19:22
3

I'd say that the solution you implemented looks pretty Pythonic; that being said, types.SimpleNamespace (documented here) already wraps this functionality:

from types import SimpleNamespace
p = SimpleNamespace(name = "John Smith", age = 23)
print(p)
Maldus
  • 10,548
  • 4
  • 24
  • 36
  • This is a less well known solution, but seems ideal. This is probably the right "pythonic" solution. The documentation (as linked) says: "A simple object subclass that provides attribute access to its namespace, as well as a meaningful repr. Unlike object, with SimpleNamespace you can add and remove attributes. If a SimpleNamespace object is initialized with keyword arguments, those are directly added to the underlying namespace." But I can't help feeling reluctant to import this for some unknown reason when @pillmuncher 's solution seems so light. – NeilG Mar 21 '23 at 01:29
1

From the Python IAQ:

As of Python 2.3 you can use the syntax

dict(a=1, b=2, c=3, dee=4)

which is good enough as far as I'm concerned. Before Python 2.3 I used the one-line function

def Dict(**dict): return dict
Ken
  • 756
  • 4
  • 7
1

I think object literals make sense in JavaScript for two reasons:

  1. In JavaScript, objects are only way to create a “thing” with string-index properties. In Python, as noted in another answer, the dictionary type does that.

  2. JavaScript‘s object system is prototype-based. There’s no such thing as a class in JavaScript (although it‘s coming in a future version) — objects have prototype objects instead of classes. Thus it’s natural to create an object “from nothing”, via a literal, because all objects only require the built-in root object as a prototype. In Python, every object has a class — you’re sort of expected to use objects for things where you’d have multiple instances, rather than just for one-offs.

Thus no, object literals aren’t Pythonic, but they are JavaScripthonic.

Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
  • 4
    No, that’s a soda often mixed with DOM Gin. – Paul D. Waite Jul 26 '10 at 16:46
  • 2
    I disagree - in JavaScript there is indeed only one way to create dict-like object, but it can be accessed in more than one way - through `obj.prop` and `obj["prop"]` syntaxes - which is quite nice idea, that can be implemented in Python if one wants so (Unode answer below). Also - classes are objects too (in Python) so using them as one-time containers for attributes could be useful from time to time (when dealing with APIs that want to have `__getattr__` implemented on passed something). – cji Jul 26 '10 at 17:21
  • 1
    @cji: good points on potential usefulness. I took “Pythonic” to mean “using built-in features of the language before implementing custom constructs inspired by other languages” though, so without criticising it, I’ll stand by the idea that a class like `literal` isn’t particularly Pythonic. Out of interest, have you got any examples of APIs wanting to be passed something with `__getattr__` implemented, where you’d pass them a one-off thing? I’m not very experienced in Python, so I haven’t come across anything like that yet. – Paul D. Waite Jul 26 '10 at 18:40
  • 3
    One particular use case I'm familiar with is testing Django views. There's often need to _fake_ some object for one or two particular views in test environment. I often see classes like DummyObject or similar there - and it's all right, of course, but sometimes you're tired, or in a hurry and appreciate every LOC less. You know that tomorrow you'll have to rewrite it, but *tonight* you just want this damn tests to pass and go to sleep :) – cji Jul 26 '10 at 19:12
  • Aha, yes, I've done a bit of Django. I'll keep a look out for that. – Paul D. Waite Jul 26 '10 at 22:42
  • Another thing to add, Python considers a user-defined `class` and `dict` to be different types. There's no direct support for accessing a a value using `dict.key`. However, Javascript supports plain `a.b` dot notation access to find key `b` on dict `a`, and in fact [as far as I can tell, Javascript's `class` is just syntactic sugar over a dictionary](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). It makes me think that Python is aware of the type of a class, and it should be predefined, whereas in Javascript there are no "types" at all (just dynamic objects)... – jrh May 30 '19 at 14:08
  • ... where a dynamic object is something that is implemented by a dictionary, and has no metadata; even functions of a class in Javascript can be just entries in a dictionary because a function can be a variable (though to be fair I can't explain how `this` is implemented in JS's dictionary system; perhaps there's a hidden, generated first `self`-like parameter added to each function when you define a `class`). Does that make sense or am I way off here? – jrh May 30 '19 at 14:12
  • 1
    @jrh: JavaScript does have types, although they're probably not super-relevant in this context. I don't know enough about the nitty-gritty of JavaScript or Python to evaluate how accurate your statements are. – Paul D. Waite May 30 '19 at 14:34
1

A simple dictionary should be enough for most cases.

If you are looking for a similar API to the one you indicated for the literal case, you can still use dictionaries and simply override the special __getattr__ function:

class CustomDict(dict):
    def __getattr__(self, name):
        return self[name]

p = CustomDict(user='James', location='Earth')
print p.user
print p.location

Note: Keep in mind though that contrary to namedtuples, fields are not validated and you are in charge of making sure your arguments are sane. Arguments such as p['def'] = 'something' are tolerated inside a dictionary but you will not be able to access them via p.def.

unode
  • 9,321
  • 4
  • 33
  • 44