Доступ к ключам диктовки как к атрибуту? - PullRequest
252 голосов
/ 13 февраля 2011

Я нахожу более удобным доступ к ключам dict как obj.foo вместо obj['foo'], поэтому я написал следующий фрагмент:

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

Однако я предполагаю, что должна быть какая-то причина, по которой Python не 'Предоставить эту функциональность из коробки.Каковы будут предостережения и недостатки доступа к ключам диктовки таким образом?

Ответы [ 24 ]

4 голосов
/ 26 января 2018

Как насчет Prodict , маленького класса Python, который я написал для управления ими всеми:)

Плюс, вы получаете автоматическое завершение кода , рекурсивные экземпляры объектов и автоматическое преобразование типов !

Вы можете сделать именно то, что просили:

p = Prodict()
p.foo = 1
p.bar = "baz"

Пример 1: Типподсказка

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

auto code complete

Пример 2. Автоматическое преобразование типов

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>
4 голосов
/ 05 февраля 2015

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

Addict этобольшая библиотека для этого: https://github.com/mewwts/addict она решает многие проблемы, упомянутые в предыдущих ответах.

Пример из документации:

body = {
    'query': {
        'filtered': {
            'query': {
                'match': {'description': 'addictive'}
            },
            'filter': {
                'term': {'created_by': 'Mats'}
            }
        }
    }
}

С наркоманом:

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'
3 голосов
/ 15 августа 2017

Просто чтобы добавить к ответу несколько вариантов, sci-kit learn реализовал это как Bunch:

class Bunch(dict):                                                              
    """ Scikit Learn's container object                                         

    Dictionary-like object that exposes its keys as attributes.                 
    >>> b = Bunch(a=1, b=2)                                                     
    >>> b['b']                                                                  
    2                                                                           
    >>> b.b                                                                     
    2                                                                           
    >>> b.c = 6                                                                 
    >>> b['c']                                                                  
    6                                                                           
    """                                                                         

    def __init__(self, **kwargs):                                               
        super(Bunch, self).__init__(kwargs)                                     

    def __setattr__(self, key, value):                                          
        self[key] = value                                                       

    def __dir__(self):                                                          
        return self.keys()                                                      

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

    def __setstate__(self, state):                                              
        pass                       

Все, что вам нужно, это получить setattrи getattr методы - getattr проверяет наличие ключей dict и переходит к проверке фактических атрибутов.setstaet - это исправление для исправления / расслоения "сгустков" - если незаинтересованный чек https://github.com/scikit-learn/scikit-learn/issues/6196

3 голосов
/ 13 февраля 2011

Не нужно писать свой как setattr () и getattr () уже существуют.

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

3 голосов
/ 12 апреля 2013

Я создал это на основе ввода из этой темы. Мне нужно использовать odict, поэтому мне пришлось переопределить get и установить attr. Я думаю, что это должно работать для большинства специальных целей.

Использование выглядит следующим образом:

# Create an ordered dict normally...
>>> od = OrderedAttrDict()
>>> od["a"] = 1
>>> od["b"] = 2
>>> od
OrderedAttrDict([('a', 1), ('b', 2)])

# Get and set data using attribute access...
>>> od.a
1
>>> od.b = 20
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])

# Setting a NEW attribute only creates it on the instance, not the dict...
>>> od.c = 8
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])
>>> od.c
8

Класс:

class OrderedAttrDict(odict.OrderedDict):
    """
    Constructs an odict.OrderedDict with attribute access to data.

    Setting a NEW attribute only creates it on the instance, not the dict.
    Setting an attribute that is a key in the data will set the dict data but 
    will not create a new instance attribute
    """
    def __getattr__(self, attr):
        """
        Try to get the data. If attr is not a key, fall-back and get the attr
        """
        if self.has_key(attr):
            return super(OrderedAttrDict, self).__getitem__(attr)
        else:
            return super(OrderedAttrDict, self).__getattr__(attr)


    def __setattr__(self, attr, value):
        """
        Try to set the data. If attr is not a key, fall-back and set the attr
        """
        if self.has_key(attr):
            super(OrderedAttrDict, self).__setitem__(attr, value)
        else:
            super(OrderedAttrDict, self).__setattr__(attr, value)

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

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d
3 голосов
/ 15 августа 2014

По-видимому, теперь есть библиотека для этого - https://pypi.python.org/pypi/attrdict - которая реализует эту точную функциональность плюс рекурсивное слияние и загрузка json. Может стоит посмотреть.

2 голосов
/ 22 марта 2017

Позвольте мне опубликовать другую реализацию, которая основана на ответе Kinvais, но объединяет идеи из AttributeDict, предложенного в http://databio.org/posts/python_AttributeDict.html.

Преимущество этой версии в том, что она также работает для вложенных словарей:

class AttrDict(dict):
    """
    A class to convert a nested Dictionary into an object with key-values
    that are accessible using attribute notation (AttrDict.attribute) instead of
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
    """

    # Inspired by:
    # http://stackoverflow.com/a/14620633/1551810
    # http://databio.org/posts/python_AttributeDict.html

    def __init__(self, iterable, **kwargs):
        super(AttrDict, self).__init__(iterable, **kwargs)
        for key, value in iterable.items():
            if isinstance(value, dict):
                self.__dict__[key] = AttrDict(value)
            else:
                self.__dict__[key] = value
1 голос
/ 28 февраля 2017

Как отметил Дуг, есть пакет Bunch, который вы можете использовать для достижения функциональности obj.key.На самом деле есть более новая версия под названием

NeoBunch

Она имеет отличную функцию, которая преобразует ваш диктант в объект NeoBunch через neobunchify функция.Я часто использую шаблоны Mako, и передача данных в виде объектов NeoBunch делает их намного более читабельными, поэтому, если вам случится так, что в вашей программе на Python вам пришлось использовать обычный dict, но вы хотите использовать точечную запись в шаблоне Mako, вы можете использовать ее следующим образом:1012 *

from mako.template import Template
from neobunch import neobunchify

mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
    out_file.write(mako_template.render(**neobunchify(data)))

И шаблон Мако может выглядеть так:

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor
1 голос
/ 07 января 2017

Решение:

DICT_RESERVED_KEYS = vars(dict).keys()


class SmartDict(dict):
    """
    A Dict which is accessible via attribute dot notation
    """
    def __init__(self, *args, **kwargs):
        """
        :param args: multiple dicts ({}, {}, ..)
        :param kwargs: arbitrary keys='value'

        If ``keyerror=False`` is passed then not found attributes will
        always return None.
        """
        super(SmartDict, self).__init__()
        self['__keyerror'] = kwargs.pop('keyerror', True)
        [self.update(arg) for arg in args if isinstance(arg, dict)]
        self.update(kwargs)

    def __getattr__(self, attr):
        if attr not in DICT_RESERVED_KEYS:
            if self['__keyerror']:
                return self[attr]
            else:
                return self.get(attr)
        return getattr(self, attr)

    def __setattr__(self, key, value):
        if key in DICT_RESERVED_KEYS:
            raise AttributeError("You cannot set a reserved name as attribute")
        self.__setitem__(key, value)

    def __copy__(self):
        return self.__class__(self)

    def copy(self):
        return self.__copy__()
1 голос
/ 06 августа 2016
class AttrDict(dict):

     def __init__(self):
           self.__dict__ = self

if __name__ == '____main__':

     d = AttrDict()
     d['ray'] = 'hope'
     d.sun = 'shine'  >>> Now we can use this . notation
     print d['ray']
     print d.sun
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...