2008-10-17 6 views
9

Je voudrais créer un objet Python one-off trivial pour contenir des options de ligne de commande. Je voudrais faire quelque chose comme ceci:Qu'est-ce qu'un moyen facile de créer un objet Python one-off trivial?

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

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

Bien sûr, je pourrais utiliser un dictionnaire, mais options.VERBOSE est plus lisible et plus facile à saisir que options['VERBOSE'].

Je pensais que je devrais pouvoir faire

options = object() 

, puisque object est le type de base de tous les objets de classe et devrait donc être quelque chose comme une classe sans attributs. Mais cela ne fonctionne pas, car un objet créé à l'aide object() n'a pas membre __dict__, et donc on ne peut pas ajouter des attributs à elle:

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

Quelle est la façon la plus simple « pythonique » pour créer un objet peut être utilisé de cette façon, de préférence sans avoir à créer une classe d'assistance supplémentaire?

Répondre

8

Compte tenu de vos besoins, je dirais que la classe personnalisée est votre meilleur pari:

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

if options.VERBOSE: 
    # ... 

Pour être complète, une autre approche serait d'utiliser un module séparé, à savoir options.py pour encapsuler les options par défaut.

options.py:

VERBOSE = True 
IGNORE_WARNINGS = True 

Puis, en main.py:

import options 

if options.VERBOSE: 
    # ... 

Cela a la particularité d'enlever un peu de bruit de votre script. Les valeurs par défaut sont faciles à trouver et à modifier, car elles sont bouclées dans leur propre module. Si plus tard votre application a grandi, vous pouvez facilement accéder aux options des autres modules.

Ceci est un modèle que j'utilise fréquemment, et je le recommande chaudement si cela ne vous dérange pas que votre application devienne plus grande qu'un seul module. Ou commencez avec une classe personnalisée et développez-la ultérieurement si votre application atteint plusieurs modules.

+0

Cela semble le plus proche de ce que j'ai demandé tout en n'étant pas terriblement impitoyable. Et la classe 'options' n'a aucun autre attribut susceptible de prêter à confusion. – mhagger

+0

Je pense que mettre VERBOSE dans la définition de classe comme ça est un peu inutile car il le crée comme une variable de classe (pas une variable d'instance) AFAIK – hasen

+3

En fait, c'est le point. La classe est juste utilisée comme espace de noms, de sorte que vous n'avez pas besoin d'instancier quoi que ce soit pour obtenir les valeurs. –

14

Le collections module a développé une fonction de namedtuple 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 est immuable, de sorte que vous ne pouvez affecter des valeurs de champ lors de sa création.

Dans les versions précédentes Python versions, vous pouvez créer une classe vide:

class options(object): 
    pass 

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

Je ne savais pas de la classe 'namedtuple' donc merci pour la référence. Mais j'aimerais un objet mutable (utile, par exemple, pendant le traitement de la ligne de commande). En outre, l'initialisation de l'instance 'namedtuple' n'est pas vraiment lisible lorsqu'elle est utilisée à cette fin. – mhagger

+0

Si vous regardez dans le doc namedtuple, vous verrez qu'il existe une option d'initialisation plus claire (toujours immuable, désolé), par exemple: "opt (VERBOSE = Vrai, IGNORE_WARNINGS = False)" – gimel

+0

Les tuples nommés sont définitivement cool. Voir cet article http://www.artima.com/weblogs/viewpostP.jsp?thread=236637 et aussi cette recette si vous n'êtes pas sur Python 2.6 http://code.activestate.com/recipes/500261/ –

5

J'utilise attrdict:

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

En fonction de votre point de vue, vous pensez probablement qu'il est soit un grand kludge ou plutôt intelligent. Mais tout ce que vous pensez, il ne fait pour bien le code à la recherche, et est compatible avec un 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 
+0

Essayez ceci : ad ['foo-bar'] = 'oopsy' –

+1

@Jeremy - je ne sais pas quel est le problème: >>> ad ['foo-bar'] = 'oopsy' >>> ad {'baz' : 'bonjour', 'foo': 100, 'bar': 200, 'foo-bar': 'oopsy'} – davidavr

2

Simplifier davraamides's suggestion, on pourrait utiliser les éléments suivants:

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

qui

  1. N'est pas si kludgy. Ne pas contaminer l'espace de nom de chaque objet avec les méthodes standard dict; par exemple, ad.has_key n'est pas défini pour les objets de type attrdict2.

Par ailleurs, il est encore plus facile d'initialiser les instances de attrdict ou attrdict2:

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

Certes, attrdict2 n'est pas compatible avec un dict.

Si vous n'avez pas besoin le comportement d'initialisation magique, vous pouvez même utiliser

class attrdict3(object): 
    pass 

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

Mais j'espérais toujours une solution qui ne nécessite pas une classe auxiliaire.

1

On peut utiliser

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

options.VERBOSE = False 

if options.VERBOSE: 
    ... 

, en utilisant l'objet de classe elle-même (pas une instance de la classe!) Comme l'endroit pour stocker les options individuelles. C'est laconique et satisfait toutes les exigences, mais cela semble être une mauvaise utilisation du concept de classe. Cela conduirait également à la confusion si un utilisateur instancié la classe options.

(Si plusieurs instances des objets options détention étaient nécessaires, ce serait une solution très agréable -. L'approvisionnement en définition classe les valeurs par défaut, qui peut être annulé dans les cas individuels)

1

La classe tout à fait simple pour faire le travail est:

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

Il peut être utilisé plus tard comme:

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

namedtuple (comme un autre commenté suggèrent ed) est une classe plus avancée qui vous donne plus de fonctionnalités. Si vous utilisez toujours Python 2.5, l'implémentation de namedtuple 2.6 est basé sur peut être trouvé à http://code.activestate.com/recipes/500261/

6

Si vous insistez pour ne pas avoir à définir une classe, vous pouvez abuser de certaines classes existantes. La plupart des objets appartiennent à des classes « nouveau style », qui ne disposent pas d'un dict, mais les fonctions peuvent avoir des attributs arbitraires:

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

Un problème est que les fonctions ont déjà certains attributs, donc dir (x) est un peu désordre:

>>> 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'] 
+1

Cela ressemble exactement à ce que le PO demandait. –

3

Il suffit de créer un module appelé Options.py, et l'importer. Mettez vos valeurs d'options par défaut ici en tant que variables globales.

7

Pourquoi ne pas utiliser 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: 
    [...] 
4

Comme les meilleures pratiques vont, vous êtes vraiment mieux avec l'une des options David Eyk's answer.

Cependant, pour répondre à votre question, vous pouvez créer une classe unique en utilisant la fonction type:

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

Notez que vous pouvez fournir un dictionnaire initial, ou tout simplement le laisser vide.

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

Pas très pythonique.

-2

objet simple et tuples nommés sont le chemin à parcourir

+0

Pourriez-vous avoir élaboré ou donné un exemple? – ArtB

Questions connexes