views:

386

answers:

7

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?

+28  A: 

Why not just use a dictionary?

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

print p
print p['name']
print p['age']
Wayne Werner
You can also use `dict` function: `p = dict(name='John Smith', age=23)`
Tomasz Wysocki
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
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
@Wayne - 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
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
+13  A: 

Have you considered using a named tuple?

Using your dict notation

>>> 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 I think it's silly giving names to "anonymous" functions

gnibbler
How about PyRec? (http://www.valuedlessons.com/2009/10/introducing-pyrec-cure-to-bane-of-init.html)
Plumenator
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
@ShinNoNoir, creating a factory function is a little easier than your suggestion as `namedtuple` is quite flexible about it's second parameter
gnibbler
@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
@gnibbler, btw, there's an error in your literal function. You forgot to unpack the kw dict: `literal = lambda **kw:namedtuple('literal', kw)(**kw)`
ShinNoNoir
@ShinNoNoir, ah yes, thanks. Fixed it :)
gnibbler
+3  A: 

Look here.

pillmuncher
+1  A: 

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
A: 

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
Javascriptonic, maybe? :-)
Plumenator
No, that’s a soda often mixed with DOM Gin.
Paul D. Waite
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
@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
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
Aha, yes, I've done a bit of Django. I'll keep a look out for that.
Paul D. Waite
+2  A: 

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
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
Actually it's 'class object' - it's instantiated here: `make_class(...)()`. I think? And as for name clashes - True.
cji
+1  A: 

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