1

Considering memory use, clock cycles or good pythonic style is it better to do this:

def func():
    class A:
       x = 10
       y = 20
    return A

or this

def func():
    o = object()
    o.x = 10
    o.y = 20
    return o

Or something else? I don't want to return a dictionary because I don't like having to use the square brackets.

z7sg Ѫ
  • 3,153
  • 1
  • 23
  • 35

6 Answers6

9

I like to use a special way of crafting a dict subclass which I found here. It looks like:

class Struct(dict):
    """Python Objects that act like Javascript Objects"""
    def __init__(self, *args, **kwargs):
        super(Struct, self).__init__(*args, **kwargs)
        self.__dict__ = self

This object can be used this way:

o = Struct(x=10)
o.y = 20
o['z'] = 30
print o.x, o['y'], o.z

The different types of access can be used in a exchangeable way.

martineau
  • 119,623
  • 25
  • 170
  • 301
glglgl
  • 89,107
  • 13
  • 149
  • 217
4

The usual trick for this is to use a namedtuple.

Simon Callan
  • 3,020
  • 1
  • 23
  • 34
3

Sounds like you want a namedtuple (but it's effectively read-only):

from collections import namedtuple

XYTuple = namedtuple('XYTuple', 'x y')
nt = XYTuple._make( (10, 20) )

print nt.x, nt.y
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
2

I use dict for things like this. Dics have a lot of benefits like listing all keys, etc.

Pavel Reznikov
  • 2,968
  • 1
  • 18
  • 17
1

The second solution does not work:

>>> o = object()
>>> o.x = 10
AttributeError: 'object' object has no attribute 'x'

This is because instances of object do not have a __dict__.

I agree that using square brackets to access attributes is not elegant. To return a value object, my team uses this (this code can certainly be improved):

class Struct(object):
    """
    An object whose attributes are initialized from an optional positional
    argument or from a set of keyword arguments (the constructor accepts the
    same arguments than the dict constructor).
    """

    def __init__(self, *args, **kwargs):
        self.__dict__.update(*args, **kwargs)

    def __repr__(self):
        klass = self.__class__
        attributes = ', '.join('{0}={1!r}'.format(k, v) for k, v in self.__dict__.iteritems())
        return '{0}.{1}({2})'.format(klass.__module__, klass.__name__, attributes)

Using Struct, your example can be rewritten as is:

def func():
    return Struct(x = 10, y = 20)

The advantage of Struct over namedtuple is that you do not have to define your types beforehand. It is closer of what you use in a language like JavaScript for example. namedtuple has the advantage to be more efficient and attributes are accessible by index or names at the same time.

Nicolas Grilly
  • 133
  • 1
  • 4
  • I also like the elegant implementation of @glglgl if some your attribute names are not valid identifiers in the character set [_a-zA-Z0-9]. – Nicolas Grilly Jul 20 '12 at 11:18
1

Yet another way derived from the [now current] version of jsobect that was also used in @glglgl's answer (but is very different):

class Struct(dict):
    def __getattr__(self, k):
        try:
            return self[k]
        except KeyError:
            return self.__getitem__(k)

    def __setattr__(self, k, v):
        if isinstance(v, dict):
            self[k] = self.__class__(v)
        else:
            self[k] = v


o = Struct(x=10)
o.y = 20
o['z'] = 30
print(o.x, o['y'], o.z)  # -> (10, 20, 30)
print(o['not_there'])    # -> KeyError: 'not_there'
print(o.not_there)       # -> KeyError: 'not_there'
martineau
  • 119,623
  • 25
  • 170
  • 301