Как использовать точечную нотацию для 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
Обратите внимание, что это не поддерживает точечные обозначения для диктовок, которые находятся где-то внутри, например списки.
Ответ 7
Добавьте к __repr__()
метод __repr__()
чтобы вы могли настроить текст, который будет отображаться на
print text
Подробнее читайте здесь: http://www.diveintopython.net/object_oriented_framework/special_class_methods2.html