views:

586

answers:

10

I would like to create a trivial one-off Python object to hold some command-line options. I would like to do something like this:

options = ??????
options.VERBOSE = True
options.IGNORE_WARNINGS = False

# Then, elsewhere in the code...
if options.VERBOSE:
    ...

Of course I could use a dictionary, but options.VERBOSE is more readable and easier to type than options['VERBOSE'].

I thought that I should be able to do

options = object()

, since object is the base type of all class objects and therefore should be something like a class with no attributes. But it doesn't work, because an object created using object() doesn't have a __dict__ member, and so one cannot add attributes to it:

options.VERBOSE = True
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'VERBOSE'

What is the simplest "pythonic" way to create an object that can be used this way, preferably without having to create an extra helper class?

+10  A: 

The collections module has grown a namedtuple function in 2.6:

import collections
opt=collections.namedtuple('options','VERBOSE IGNORE_WARNINGS')
myoptions=opt(True, False)

>>> myoptions
options(VERBOSE=True, IGNORE_WARNINGS=False)
>>> myoptions.VERBOSE
True

A namedtuple is immutable, so you can only assign field values when you create it.

In earlier Python versions, you can create an empty class:

class options(object):
    pass

myoptions=options()
myoptions.VERBOSE=True
myoptions.IGNORE_WARNINGS=False
>>> myoptions.IGNORE_WARNINGS,myoptions.VERBOSE
(False, True)
gimel
I wasn't aware of the `namedtuple` class so thanks for the reference. But I'd like a mutable object (useful, for example, during command-line processing). Also, the initialization of the `namedtuple` instance isn't really all that readable when used for this purpose.
mhagger
If you look in the namedtuple doc, you'll see there is a clearer initialization option (still immutable, sorry), e.g:"opt(VERBOSE=True, IGNORE_WARNINGS=False)"
gimel
+3  A: 

I use attrdict:

class attrdict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

Depending on your point of view, you probably think it's either a big kludge or quite clever. But whatever you think, it does make for nice looking code, and is compatible with a dict:

>>> ad = attrdict({'foo': 100, 'bar': 200})
>>> ad.foo
100
>>> ad.bar
200
>>> ad.baz = 'hello'
>>> ad.baz
'hello'
>>> ad
{'baz': 'hello', 'foo': 100, 'bar': 200}
>>> isinstance(ad, dict)
True
davidavr
try this: ad['foo-bar'] = 'oopsy'
Jeremy Cantrell
@Jeremy - not sure what the issue is: >>> ad['foo-bar'] = 'oopsy' >>> ad {'baz': 'hello', 'foo': 100, 'bar': 200, 'foo-bar': 'oopsy'}
davidavr
+2  A: 

Simplifying davraamides's suggestion, one could use the following:

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

which

  1. Isn't so kludgy.

  2. Doesn't contaminate the namespace of each object with the standard methods of dict; for example, ad.has_key is not defined for objects of type attrdict2.

By the way, it is even easier to initialize instances of attrdict or attrdict2:

>>> ad = attrdict2(foo = 100, bar = 200)

Granted, attrdict2 is not compatible with a dict.

If you don't need the magic initialization behavior, you can even use

class attrdict3(object):
    pass

ad = attrdict3()
ad.foo = 100
ad.bar = 200

But I was still hoping for a solution that doesn't require an auxiliary class.

mhagger
+1  A: 

One can use

class options(object):
    VERBOSE = True
    IGNORE_WARNINGS = False

options.VERBOSE = False

if options.VERBOSE:
    ...

, using the class object itself (not an instance of the class!) as the place to store individual options. This is terse and satisfies all of the requirements, but it seems like a misuse of the class concept. It would also lead to confusion if a user instantiated the options class.

(If multiple instances of the options-holding objects were needed, this would be a very nice solution--the class definition supplies default values, which can be overridden in individual instances.)

mhagger
+1  A: 

The absolutely simplest class to do the job is:

class Struct:
    def __init__(self, **entries): 
        self.__dict__.update(entries)

It can be later used as:

john = Struct(name='john doe', salary=34000)
print john.salary

namedtuple (as another commented suggested) is a more advanced class that gives you more functionality. If you're still using Python 2.5, the implementation 2.6's namedtuple is based on can be found at http://code.activestate.com/recipes/500261/

Eli Bendersky
+6  A: 

If you insist on not having to define a class, you can abuse some existing classes. Most objects belong to "new-style" classes which don't have a dict, but functions can have arbitrary attributes:

>>> x = lambda: 0   # any function will do
>>> x.foo = 'bar'
>>> x.bar = 0
>>> x.xyzzy = x
>>> x.foo
'bar'
>>> x.bar
0
>>> x.xyzzy
<function <lambda> at 0x6cf30>

One problem is that functions already have some attributes, so dir(x) is a little messy:

>>> dir(x)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__',
'__get__', '__getattribute__', '__hash__', '__init__',
'__module__', '__name__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', 'foo',
'func_closure', 'func_code', 'func_defaults', 'func_dict',
'func_doc', 'func_globals', 'func_name', 'xyzzy']
Jouni K. Seppänen
This sure looks like exactly what the OP was asking for.
Robert Rossney
+2  A: 

Given your requirements, I'd say the custom class is your best bet:

class options(object):
    VERBOSE = True
    IGNORE_WARNINGS = True

if options.VERBOSE:
    # ...

To be complete, another approach would be using a separate module, i.e. options.py to encapsulate your option defaults.

options.py:

VERBOSE = True
IGNORE_WARNINGS = True

Then, in main.py:

import options

if options.VERBOSE:
    # ...

This has the feature of removing some clutter from your script. The default values are easy to find and change, as they are cordoned off in their own module. If later your application has grown, you can easily access the options from other modules.

This is a pattern that I frequently use, and would heartily recommend if you don't mind your application growing larger than a single module. Or, start with a custom class, and expand to a module later if your app grows to multiple modules.

David Eyk
This seems like the nearest to what I requested while not being terribly unpythonic. And the `options` class doesn't have any other attributes that are likely to be confusing.
mhagger
I think that putting VERBOSE in the class definition like that is kinda pointless as it creates it as a class-variable (not an instance variable) AFAIK
hasen j
Actually, that's the point. The class is just being used as a namespace, so that you don't have to instantiate anything to get at the values.
David Eyk
+2  A: 

Just make a module called Options.py, and import it. Put your default options values in there as global variables.

fivebells
+5  A: 

Why not just use optparse:

from optparse import OptionParser
[...]
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",
              help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
              action="store_false", dest="verbose", default=True,
              help="don't print status messages to stdout")

(options, args) = parser.parse_args()

file = options.filename
if options.quiet == True:
    [...]
alif
+3  A: 

As best practices go, you're really better off with one of the options in David Eyk's answer.

However, to answer your question, you can create a one-off class using the type function:

options = type('Options', (object,), { 'VERBOSE': True })()
options.IGNORE_WARNINGS = False

Note that you can provide an initial dictionary, or just leave it empty .

Options = type('Options', (object,), {})
options = Options()
options.VERBOSE = True
options.IGNORE_WARNINGS = False

Not very pythonic.

itsadok