Какой простой способ создать тривиальный одноразовый объект Python? - PullRequest
9 голосов
/ 17 октября 2008

Я хотел бы создать тривиальный одноразовый объект Python для хранения некоторых параметров командной строки. Я хотел бы сделать что-то вроде этого:

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

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

Конечно, я мог бы использовать словарь, но options.VERBOSE более читабелен и его легче набирать, чем options['VERBOSE'].

Я думал , что я смогу сделать

options = object()

, поскольку object является базовым типом всех объектов класса и, следовательно, должен быть чем-то вроде класса без атрибутов. Но это не работает, потому что объект, созданный с использованием object(), не имеет члена __dict__, и поэтому к нему нельзя добавить атрибуты:

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

Какой самый простой «питонный» способ создания объекта, который можно использовать таким образом, желательно без необходимости создания дополнительного вспомогательного класса?

Ответы [ 11 ]

14 голосов
/ 17 октября 2008

Модуль коллекций расширил функцию 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 является неизменным, поэтому вы можете назначать значения поля только при его создании.

В более ранних Python версиях вы можете создать пустой класс:

class options(object):
    pass

myoptions=options()
myoptions.VERBOSE=True
myoptions.IGNORE_WARNINGS=False
>>> myoptions.IGNORE_WARNINGS,myoptions.VERBOSE
(False, True)
8 голосов
/ 17 октября 2008

Учитывая ваши требования, я бы сказал, что пользовательский класс - ваш лучший выбор:

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

if options.VERBOSE:
    # ...

Чтобы завершить, другой подход будет использовать отдельный модуль, т. Е. options.py для инкапсуляции параметров по умолчанию.

options.py

VERBOSE = True
IGNORE_WARNINGS = True

Затем в main.py:

import options

if options.VERBOSE:
    # ...

Это позволяет убрать помехи из вашего скрипта. Значения по умолчанию легко найти и изменить, поскольку они отключены в своем собственном модуле. Если впоследствии ваше приложение расширилось, вы можете легко получить доступ к параметрам из других модулей.

Это шаблон, который я часто использую, и от всей души рекомендую, если вы не возражаете, если ваше приложение станет больше, чем один модуль. Или начните с пользовательского класса, а затем разверните его до модуля, если ваше приложение расширяется до нескольких модулей.

7 голосов
/ 17 октября 2008

Почему бы просто не использовать 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:
    [...]
6 голосов
/ 17 октября 2008

Если вы настаиваете на том, что вам не нужно определять класс, вы можете использовать некоторые существующие классы. Большинство объектов принадлежат классам "нового стиля", которые не имеют dict, но функции могут иметь произвольные атрибуты:

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

Одна проблема состоит в том, что функции уже имеют некоторые атрибуты, поэтому dir (x) выглядит немного беспорядочно:

>>> 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']
5 голосов
/ 17 октября 2008

Я использую attrdict :

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

В зависимости от вашей точки зрения, вы, вероятно, думаете, что это либо большой клудж, либо довольно умный. Но что бы вы ни думали, это делает код красивым и совместим с требованием:

>>> 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
4 голосов
/ 19 октября 2008

По мере использования передовых методов, вам действительно лучше воспользоваться одним из вариантов в Ответ Дэвида Эйка .

Однако, чтобы ответить на ваш вопрос, вы можете создать одноразовый класс, используя функцию type:

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

Обратите внимание, что вы можете предоставить исходный словарь или просто оставить его пустым.

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

Не очень питонический.

3 голосов
/ 17 октября 2008

Просто создайте модуль с именем Options.py и импортируйте его. Поместите значения параметров по умолчанию в качестве глобальных переменных.

2 голосов
/ 17 октября 2008

Упрощение предложения Давраамида , можно использовать следующее:

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

, которая

  1. Не так глупо.

  2. Не загрязняет пространство имен каждого объекта стандартными методами dict; например, ad.has_key не определено для объектов типа attrdict2.

Кстати, еще проще инициализировать экземпляры attrdict или attrdict2:

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

Конечно, attrdict2 несовместимо с dict.

Если вам не нужно магическое поведение при инициализации, вы даже можете использовать

class attrdict3(object):
    pass

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

Но я все еще надеялся на решение, которое не требует вспомогательного класса.

1 голос
/ 17 октября 2008

Абсолютно самый простой класс для выполнения работы:

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

Позже может использоваться как:

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

namedtuple (как предложил другой комментарий) - это более продвинутый класс, который дает вам больше функциональности. Если вы все еще используете Python 2.5, на базе namedtuple реализации 2.6 можно найти http://code.activestate.com/recipes/500261/

1 голос
/ 17 октября 2008

Можно использовать

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

options.VERBOSE = False

if options.VERBOSE:
    ...

, используя сам объект класса (не экземпляр класса!) В качестве места для хранения отдельных опций. Это кратко и удовлетворяет всем требованиям, но кажется неправильным использованием концепции класса. Это также может привести к путанице, если пользователь создаст экземпляр класса options.

(Если бы потребовалось несколько экземпляров объектов, содержащих опцию, это было бы очень хорошим решением - определение класса предоставляет значения по умолчанию, которые могут быть переопределены в отдельных экземплярах.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...