Как использовать точечную нотацию для dict в python?

Я очень новичок в python, и я бы хотел, чтобы я мог это сделать . нотация для доступа к значениям dict.

Допустим, у меня такой test:

>>> test = dict()
>>> test['name'] = 'value'
>>> print(test['name'])
value

Но я хотел бы сделать test.name чтобы получить value. Infact Я сделал это, переопределив метод __getattr__ в моем классе следующим образом:

class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self,key): 
        try:
            return self._response[key]
        except KeyError,err:
            sys.stderr.write('Sorry no key matches')

и это работает! когда я делаю:

test.name // I get value.

Но проблема в том, что когда я просто печатаю test я получаю ошибку как:

'Sorry no key matches'

Почему это происходит?

Ответ 1

Эта функциональность уже существует в стандартных библиотеках, поэтому я рекомендую вам просто использовать их класс.

>>> from types import SimpleNamespace
>>> d = {'key1': 'value1', 'key2': 'value2'}
>>> n = SimpleNamespace(**d)
>>> print(n)
namespace(key1='value1', key2='value2')
>>> n.key2
'value2'

Добавление, изменение и удаление значений достигается с помощью обычного доступа к атрибутам, т.е. вы можете использовать такие выражения, как n.key = val и del n.key.

Чтобы снова вернуться к диктату:

>>> vars(n)
{'key1': 'value1', 'key2': 'value2'}

Ключи в вашем dict должны быть строковыми идентификаторами, чтобы доступ к атрибутам работал правильно.

Простое пространство имен было добавлено в Python 3.3. Для более старых версий языка argparse.Namespace имеет похожее поведение.

Ответ 2

Я предполагаю, что вам комфортно в Javascript и вы хотите взять такой синтаксис... Я могу сказать вам по личному опыту, что это не отличная идея.

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

Если вам нужно манипулировать полями объекта, как если бы они были словарем, вы всегда можете прибегать к использованию внутреннего атрибута __dict__ когда вам это нужно, а затем четко указывается, что вы делаете. Или используйте getattr(obj, 'key') чтобы учесть структуру наследования и атрибуты класса.

Но, читая ваш пример, кажется, что вы пытаетесь что-то другое... Поскольку оператор-точка уже будет искать в __dict__ без какого-либо дополнительного кода.

Ответ 3

Не могли бы вы использовать именованный кортеж?

from collections import namedtuple
Test = namedtuple('Test', 'name foo bar')
my_test = Test('value', 'foo_val', 'bar_val')
print(my_test)
print(my_test.name)

Ответ 4

__getattr__ используется как __getattr__ когда все другие правила поиска атрибутов не удались. Когда вы пытаетесь "распечатать" свой объект, Python ищет метод __repr__, и поскольку вы его не реализуете в своем классе, он заканчивает вызов __getattr__ (да, в методах Python тоже есть атрибуты). Вы не должны предполагать, какой ключ getattr будет вызван, и, самое главное, __getattr__ должен поднять AttributeError, если он не может разрешить key.

В качестве побочного примечания: не используйте self.__dict__ для обычного доступа к атрибутам, просто используйте стандартную нотацию атрибута:

class JuspayObject:

    def __init__(self,response):
        # don't use self.__dict__ here
        self._response = response

    def __getattr__(self,key):
        try:
            return self._response[key]
        except KeyError,err:
            raise AttributeError(key)

Теперь, если ваш класс не имеет никакой другой ответственности (и ваша версия Python равна> = 2.6, и вам не нужно поддерживать более старые версии), вы можете просто использовать namedtuple: http://docs.python.org/2/library/collections.html # collections.namedtuple

Ответ 5

Вы должны быть осторожны при использовании __getattr__, потому что он используется для множества встроенных функций Python.

Попробуй что-нибудь вроде этого...

class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self, key):
        # First, try to return from _response
        try:
            return self.__dict__['_response'][key]
        except KeyError:
            pass
        # If that fails, return default behavior so we don't break Python
        try:
            return self.__dict__[key]
        except KeyError:
            raise AttributeError, key

>>> j = JuspayObject({'foo': 'bar'})
>>> j.foo
'bar'
>>> j
<__main__.JuspayObject instance at 0x7fbdd55965f0>

Ответ 6

В дополнение к этому ответу можно также добавить поддержку вложенных диктов:

from types import SimpleNamespace

class NestedNamespace(SimpleNamespace):
    def __init__(self, dictionary, **kwargs):
        super().__init__(**kwargs)
        for key, value in dictionary.items():
            if isinstance(value, dict):
                self.__setattr__(key, NestedNamespace(value))
            else:
                self.__setattr__(key, value)

nested_namespace = NestedNamespace({
    'parent': {
        'child': {
            'grandchild': 'value'
        }
    },
    'normal_key': 'normal value',
})


print(nested_namespace.parent.child.grandchild)  # value
print(nested_namespace.normal_key)  # normal value

Обратите внимание, что это не поддерживает точечные обозначения для диктовок, которые находятся где-то внутри, например списки.