Karar verme anahtarlarına bir özellik gibi mi erişiyorsunuz?


303

Dict anahtarlarına erişmek obj.fooyerine daha uygun buluyorum obj['foo'], bu yüzden bu snippet'i yazdım:

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

Ancak, Python'un bu işlevselliği kutudan çıkarmamasının bir nedeni olması gerektiğini varsayıyorum. Karar anahtarlarına bu şekilde erişmenin uyarıları ve tuzakları ne olurdu?


16
Sabit kodlu anahtarlara her yerde sabit boyutlu sınırlı bir kümeden erişiyorsanız, bunları tutan nesneler oluşturmaktan daha iyi olabilirsiniz. collections.namedtuplebunun için çok faydalıdır.

6
stackoverflow.com/questions/3031219/… benzer bir çözüme sahip, ancak bir adım daha ileri gidiyor
keflavich

1
Bunun için bir modül github.com/bcj/AttrDict adresinde bulundu . Buradaki ve ilgili sorulardaki çözümlerle nasıl karşılaştırıldığını bilmiyorum.
matt wilkie

Ben de benzer kesmek kullandım, şimdi kullanıyorumeasydict.EasyDict
muon

Sözlük üyelerine "." İle erişmenin diğer yolları : stackoverflow.com/questions/2352181/…
Soluk Mavi Nokta

Yanıtlar:


304

Bunu yapmanın en iyi yolu:

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

Bazı artıları:

  • Aslında işe yarıyor!
  • Hiçbir sözlük sınıfı yöntemi gölgelenmez (örn. İyi .keys()çalışır. Tabii ki - bunlara bir değer atamazsanız, aşağıya bakın)
  • Özellikler ve öğeler her zaman senkronizedir
  • Bir öznitelik doğru yükseltir olarak varolmayan anahtar erişmeye çalışıyor AttributeErroryerineKeyError

Eksileri:

  • Gibi yöntemler .keys()olacak değil onlar gelen verilerle üzerine yazılır alırsanız çıkmaması
  • Python <2.7.4 / Python3 <3.2.3'te bellek sızıntısına neden oluyor
  • Pylint ile muz gider E1123(unexpected-keyword-arg)veE1103(maybe-no-member)
  • Deneyimsiz olanlar için saf bir sihir gibi görünüyor.

Bunun nasıl çalıştığına dair kısa bir açıklama

  • Tüm python nesneleri dahili olarak özelliklerini adlı bir sözlükte saklar __dict__.
  • Dahili sözlüğün __dict__"sadece düz bir diksiyon" olması gerekmemektedir, bu nedenle dict()dahili sözlüğe herhangi bir alt sınıfı atayabiliriz .
  • Bizim durumumuzda basitçe AttrDict()somutlaştırdığımız örneği atarız (içinde olduğumuz gibi __init__).
  • Arayarak super()'ın __init__()metodunu biz bu fonksiyon tüm çağrıları beri (zaten), tam bir sözlük gibi davranır verdiğinden emin sözlük örnekleme kodu.

Python'un bu işlevselliği kutudan çıkarmamasının bir nedeni

"Eksileri" listesinde belirtildiği gibi, bu, saklanan anahtarların ad alanını (rasgele ve / veya güvenilmeyen verilerden gelebilir!) Yerleşik dict yöntemi özniteliklerinin ad alanı ile birleştirir. Örneğin:

d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

1
Hafıza sızıntısının şöyle basit bir nesne ile gerçekleşeceğini düşünüyor musunuz: >>> sınıf MyD (nesne): ... def init __ (self, d): ... self .__ dict = d
Rafe

2.7
pi'de

1
Bunu kullanıyorum <= 2.7.3.
pi.

1
2.7.4 sürüm notlarında sabit olduğunu belirtiyorlar (daha önce değil).
Robert Siemer

1
@viveksinghggits sadece bir şeye eriştiğiniz .için dilin kurallarını kıramazsınız :) Ve AttrDictboşluk içeren alanları otomatik olarak farklı bir şeye dönüştürmek istemem .
Yurik

125

Dizi gösterimini kullanırsanız, tüm yasal dize karakterlerini anahtarın parçası olarak kullanabilirsiniz. Örneğin,obj['!#$%^&*()_']


1
@Izkata evet. SE hakkında komik bir şey genellikle 'en iyi soru' yani yani. belki de SE, "başlık her şeyi söylüyor" ifadesini duymak istemediğinden, bir "alt soru"; "uyarılar" burada en altta.
n611x007

2
JavaScript, programlama dilinin özellikle iyi bir örneği değildir, ancak JS'deki nesneler, hem öznitelik erişimini hem de dizi gösterimini destekler, bu da genel durum için kolaylık sağlar ve yasal öznitelik adları olmayan semboller için genel bir geri dönüş sağlar.
André Caron

@Izkata Bu soruya nasıl cevap veriyor? Bu cevap sadece tuşların herhangi bir ismi olabileceğini söylüyor.
Melab

4
@Melab Soru What would be the caveats and pitfalls of accessing dict keys in this manner?(nitelikler olarak) ve cevap, burada gösterilen karakterlerin çoğunun kullanılamayacağıdır.
Izkata

83

Gönderen Bu, diğer SO soru varolan kodu basitleştiren büyük bir uygulama örneği var. Nasıl olur:

class AttributeDict(dict): 
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

Çok daha özlüdür ve gelecekte sizin __getattr__ve __setattr__işlevlerinize girebilmek için ekstra boşluk bırakmaz .


Bu yöntemi kullanarak AttributeDict.update veya AttributeDict.get'i çağırabilir misiniz?
Dor

13
Çalışma zamanında yeni özellikler eklerseniz bunların dict'in kendisine değil dict özelliğine eklendiğini unutmayın . Örn d = AttributeDict(foo=1). d.bar = 1bar niteliği, dict özelliğinin içinde saklanır ancak dict içinde saklanmaz . baskı dsadece foo öğeyi gösterir.
P3trus

7
+1, çünkü anlayabildiğim kadarıyla mükemmel çalışıyor. @GringoSuave, @Izkata, @ P3trus Başarısız olduğunu iddia eden herkesin işe yaramadığını gösteren bir örnek göstermesini istiyorum d = AttributeDict(foo=1);d.bar = 1;print d=> {'foo': 1, 'bar': 1}Benim için çalışıyor!
Dave Abrahams

4
@DaveAbrahams Tüm soruyu okuyun ve Hery, Ryan ve TheCommunistDuck'ın cevaplarına bakın. Bunun nasıl yapılacağını değil , ortaya çıkabilecek problemleri soruyor .
İzkata

6
Bir sağlamalıdır __getattr__bir yükseltir yöntemi AttributeErrorverilen nitelik yoksa, başka türlü şeyler gibi getattr(obj, attr, default_value)(yani dönmez değil işi yapmak default_valuedurumunda attrmevcut değil obj)
jcdude

83

Sorulan Soruya Nerede Cevap Vereceğim

Python neden onu kutudan çıkarmıyor?

Bunun Python Zen ile ilgisi olduğundan şüpheleniyorum : "Bunu yapmanın bir - ve tercihen tek - açık bir yolu olmalı." Bu, sözlüklerden değerlere erişmek için iki belirgin yol oluşturur: obj['key']ve obj.key.

Uyarılar ve Tuzaklar

Bunlar, kodda olası netlik ve karışıklık eksikliğini içerir. yani, kodunuzu daha sonraki bir tarihte tutacak bir başkası , hatta bir süreliğine tekrar girmiyorsanız bile sizin için kafa karıştırıcı olabilir . Yine Zen'den : "Okunabilirlik önemlidir!"

>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1

Eğer dörneği veya KEY tanımlanır veya d[KEY] uzak yerden atanır d.spamkullanılıyor bu yaygın-el deyim olmadığından, kolayca, yapılıyor neler olup karışıklığa yol açabilir. Beni şaşırtma potansiyeline sahip olacağını biliyorum.

Ayrıca, değerini KEYaşağıdaki gibi değiştirirseniz (ancak değiştirmeyi özlüyorsanız d.spam), şimdi şunu elde edersiniz:

>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'

IMO, çabaya değmez.

Diğer öğeler

Diğerlerinin de belirttiği gibi, herhangi bir yıkanabilir nesneyi (sadece bir dize değil) bir dikte anahtarı olarak kullanabilirsiniz. Örneğin,

>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>> 

yasal, ancak

>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
  File "<stdin>", line 1
  d.(2, 3)
    ^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>> 

değil. Bu, sözlük anahtarlarınız için bir nesne özelliğine erişirken sahip olmadığınız tüm yazdırılabilir karakterlere veya diğer yıkanabilir nesnelere erişmenizi sağlar. Bu, Python Yemek Tarifleri'nden (Ch.9) tarif gibi önbelleğe alınmış bir nesne metasınıfı gibi büyüyü mümkün kılar .

Nerede Editörlük Yaparım

spam.eggsÜzerinde estetiği tercih ediyorum spam['eggs'](sanırım daha temiz görünüyor) ve gerçekten tanıştığımda bu işlevselliği özlemeye başladım namedtuple. Ancak aşağıdakileri yapabilmenin rahatlığı onu gölgede bırakır.

>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>

Bu basit bir örnek, ama sık sık kendimi obj.keygösterim kullanacağımdan farklı durumlarda dikte kullanarak buluyorum (yani, bir XML dosyasından tercihleri ​​okumam gerektiğinde). Dinamik bir sınıf oluşturmaya ve estetik nedenlerle bazı özellikleri atlatmaya çalıştığım diğer durumlarda, okunabilirliği artırmak için tutarlılık için bir diksiyon kullanmaya devam ediyorum.

Eminim OP uzun zamandır bunu tatmin edici bir şekilde çözmüştür, ancak yine de bu işlevselliği istiyorsa, bunu sağlayan pypi'den paketlerden birini indirmenizi öneririm:

  • Demet daha aşina olduğum kişi. Alt sınıfdict, böylece tüm bu işlevselliğe sahip olursunuz.
  • AttrDict o da oldukça iyi, ama onunla aşina olduğu değilim ve sahip olduğu kadar ayrıntılı olarak kaynağına baktım değil gibi de görünüyor Bunch .
  • Bağımlı Aktif olarak korunur ve attr benzeri erişim ve daha fazlasını sağlar.
  • Rotareti'nin yorumlarında belirtildiği gibi, Bunch kullanımdan kaldırıldı, ancak Munch adında aktif bir çatal var .

Ancak, onun kodun okunabilirliği artırmak için şiddetle o tavsiye değil onun notasyonu stilleri karıştırın. Bu gösterimi tercih ederse, sadece dinamik bir nesneyi başlatmalı, ona istenen niteliklerini eklemeli ve bir gün olarak adlandırmalıdır:

>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}


Güncelleme Yaptığımda, Yorumlardaki Takip Sorusunu Yanıtlamak İçin

Yorumlarda (aşağıda), Elmo soruyor:

Ya daha derine inmek istersen? (tipe (...) göre)

Bu kullanım durumunu hiç kullanmadım (yine de, dicttutarlılık için iç içe kullanma eğilimindeyim ), aşağıdaki kod çalışıyor:

>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
...     setattr(d, x, C())
...     i = 1
...     for y in 'one two three'.split():
...         setattr(getattr(d, x), y, i)
...         i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}

1
Demet kullanımdan kaldırıldı, ancak aktif bir çatal var: github.com/Infinidat/munch
Rotareti

@Rotareti - Uyarı için teşekkürler! Bu kullandığım işlevsellik değil, bu yüzden farkında değildim.
Doug

Ya daha derine inmek istersen? (tipe (...) atıfta bulunur)
Ole Aldric

6
Python, şiddetli yağmurda yüksek tutulan ters bir şemsiye gibidir. Her şey başlamak için akıllı ve korkak görünüyor, bir süre sonra ağırlaşmaya başlıyor, sonra aniden SE'de bazı yerleşik guru şeyleri okuyorsunuz ve her şey omuzlarınızdaki tüm yük ile geri dönüyor. Hâlâ sırılsıklamken daha hafif hissediyorsunuz ve her şey çok net ve yenileniyor.
Ole Aldric


19

Standart kitaplıktan uygun bir kapsayıcı sınıfı çekebilirsiniz:

from argparse import Namespace

kod bitlerini kopyalamak zorunda kalmamak için. Standart sözlük erişimi yok, ancak gerçekten isterseniz bir tane almak kolaydır. Argparse içindeki kod basittir,

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

    def __contains__(self, key):
        return key in self.__dict__

2
OP tarafından ilk yorumu ele alan standart bir kütüphaneye referans için PLUS 1.
Gordon Bean

4
Python, bu durumda daha hızlı bir sınıf (C'de uygulanmaktadır) içerir: types.SimpleNamespace docs.python.org/dev/library/types.html#types.SimpleNamespace
Nuno André

18

Ya bir yöntem olan bir anahtar isteseydiniz, __eq__ya da __getattr__?

Ve bir harfle başlamayan bir girişiniz olamazdı, bu yüzden 0343853anahtar olarak kullanmak çıktı.

Ya bir dize kullanmak istemediyseniz?


Gerçekten, ya da anahtar olarak başka nesneler. Ancak hatayı bundan 'beklenen davranış' olarak sınıflandırırdım - sorumla beklenmedik olanı daha fazla hedefliyordum.
Izz ad-Din Ruhulessin

pickle.dumpkullanımları__getstate__
Cees Timmerman

12

tuples dict tuşları kullanılabilir. Yapınızdaki tuple'a nasıl erişirsiniz?

Ayrıca, namedtuple , öznitelik erişimi aracılığıyla değerler sağlayabilen kullanışlı bir yapıdır.


7
Adlandırılmış başlıkların dezavantajı, değişmez olmalarıdır.
Izz ad-Din Ruhulessin

10
Bazıları değişmez olmanın bir böcek değil, bir tuples özelliği olduğunu söyleyebilir.
ben yazar

9

Prodict , hepsine hükmetmek için yazdığım küçük Python sınıfı ne dersin :)

Ayrıca, otomatik kod tamamlama , özyinelemeli nesne örnekleme ve otomatik tip dönüştürme olsun !

Tam olarak istediğinizi yapabilirsiniz:

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

Örnek 1: Tip ipucu

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

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

otomatik kod tamamlandı

Örnek 2: Otomatik tür dönüşümü

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

2
pip üzerinden python2 üzerine kurulur, ancak python2 üzerinde çalışmaz
Ant6n

2
@ Ant6n tip ek açıklamaları nedeniyle python 3.6+ gerektirir
Ramazan Polat

8

Genel olarak çalışmaz. Tüm geçerli dikte anahtarları adreslenebilir nitelikte değildir ("anahtar"). Bu yüzden dikkatli olmalısın.

Python nesnelerinin hepsi temelde sözlüklerdir. Bu yüzden çok fazla performans veya başka bir ceza olduğundan şüpheliyim.


8

Bu orijinal soruyu ele almaz, ancak benim gibi bu işlevselliği sağlayan bir lib ararken buraya gelen insanlar için yararlı olmalıdır.

Bağımlısı bunun için büyük bir lib var: https://github.com/mewwts/addict önceki cevapları belirtilen birçok kaygıları ilgilenir.

Dokümanlardan bir örnek:

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

Bağımlısı ile:

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'

8

Kendimi python ekosisteminde "dtr olarak dict anahtarları" durumunun ne olduğunu merak ederken buldum. Birkaç yorumcunun işaret ettiği gibi, bu muhtemelen bazıları çok ince olan bazı tuzaklar ve tabancalar olduğu için kendi başınızı sıfırdan döndürmek istediğiniz bir şey değildir . Ayrıca, Namespacebir temel sınıf olarak kullanmanızı tavsiye etmem , o yolda oldum, hoş değil.

Neyse ki, bu işlevselliği sağlayan, pip kuruluma hazır birkaç açık kaynak paketi var! Ne yazık ki, birkaç paket var. İşte Aralık 2019 itibariyle bir özet.

Yarışmacılar (en son master | #commits | #contribs | kapsama% 'si):

Artık bakımlı veya az bakımlı değil:

  • treedict (2014-03-28 | 95 | 2 |?%)
  • demet (2012-03-12 | 20 | 2 |?%)
  • NeoBunch

Şu anda munch veya bağımlısı öneriyorum . Her biri için sağlıklı bir açık kaynak kod tabanı öneren en fazla taahhüt, katkıda bulunanlar ve sürümler var. En temiz görünümlü benioku.md,% 100 kapsama alanı ve iyi görünümlü test setine sahiptir.

Bu yarışta (şimdilik!) Bir köpeğim yok, kendi dict / attr kodumu yuvarlatmanın yanı sıra bir ton zaman harcadım çünkü tüm bu seçeneklerin farkında değildim :). Gelecekte bağımlılığa / munch'a katkıda bulunabilirim, çünkü bir grup parçalı paketten daha sağlam bir paket görmeyi tercih ederim. Onlardan hoşlanıyorsanız, katkıda bulunun! Özellikle, munch bir codecov rozeti kullanabilir ve bağımlı bir python sürüm rozeti kullanabilir.

profesyonelleri bağımlısı:

  • özyinelemeli başlatma (foo.abc = 'bar'), dikte benzeri argümanlar bağımlı hale gelir.

bağımlısı eksilerini:

  • typing.Dicteğer gölgelerfrom addict import Dict
  • Anahtar kontrolü yok. Özyinelemeli başlatmaya izin verdiğiniz için, bir anahtarı yanlış yazarsanız, KeyError yerine yeni bir özellik oluşturursunuz (teşekkürler AljoSt)

munch artıları:

  • benzersiz adlandırma
  • JSON ve YAML için yerleşik ser / de fonksiyonları

munch eksileri:

  • özyinelemeli başlatma yok / her seferinde yalnızca bir attr işlemi başlatabilir

Nerede Editörlük Yaparım

Birçok ay önce, sadece kendim veya başka bir geliştiriciyle projelerde metin editörleri kullandığımda, dict-attrs tarzını, sadece beyan ederek anahtar ekleme yeteneğini sevdim foo.bar.spam = eggs. Şimdi ekipler üzerinde çalışıyorum ve her şey için bir IDE kullanıyorum ve statik analiz, fonksiyonel teknikler ve tip ipuçları lehine bu tür veri yapılarından ve genel olarak dinamik yazmadan uzaklaştım. Bu tekniği denemeye başladım, Pstruct'ı kendi tasarımımdaki nesnelerle alt sınıflara ayırdım:

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))


class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

Bu size hala bir dikte gibi davranan bir nesne verir, ancak öznitelikler gibi tuşlara çok daha katı bir şekilde erişmenizi sağlar. Buradaki avantaj, ben (veya kodunuzun şanssız tüketicileri) hangi alanların var olabileceğini ve var olamayacağını tam olarak bilmektir ve IDE alanları otomatik olarak tamamlayabilir. Ayrıca vanilya alt sınıflandırma dictjson serileştirmenin kolay olduğu anlamına gelir. Bu fikirdeki bir sonraki evrimin, bu arayüzleri yayan özel bir protobuf jeneratörü olacağını düşünüyorum ve hoş bir vuruş, gRPC üzerinden çapraz dil veri yapılarını ve IPC'yi neredeyse ücretsiz olarak almanızdır.

Davetsizlerle gitmeye karar verirseniz, kendi (ve ekip arkadaşlarınızın) akıl sağlığınız için hangi alanların beklendiğini belgelemek önemlidir.

Güncel tutmak için bu yayını düzenleyebilir / güncelleyebilirsiniz!


2
büyük bir con addict, yeni bir özellik döndüreceği için bir özelliği yanlış yazdığınızda istisnalar oluşturmayacağıdır Dict(bu, foo.abc = 'bar' ın çalışması için gereklidir).
AljoSt

5

Yerleşik kullanarak değiştirilemeyen kayıtlara kısa bir örnek collections.namedtuple:

def record(name, d):
    return namedtuple(name, d.keys())(**d)

ve bir kullanım örneği:

rec = record('Model', {
    'train_op': train_op,
    'loss': loss,
})

print rec.loss(..)

5

Sadece cevaba biraz çeşitlilik katmak için, sci-kit learn bunu şöyle uyguladı 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                       

İhtiyacınız olan tek şey setattrve getattryöntemleri elde etmektir - getattrdikt tuşlarını kontrol etmek ve gerçek özellikleri kontrol etmeye devam etmek. Bu setstaet"dekapaj" dekapaj / unpickling için bir düzeltme - eğer inertested https://github.com/scikit-learn/scikit-learn/issues/6196 kontrol edin


3

Zaten setattr () ve getattr () olarak kendi yazmanıza gerek yok.

Sınıf nesnelerinin avantajı muhtemelen sınıf tanımı ve mirasta devreye girer.


3

Ben bu iş parçacığının girdisine dayalı oluşturdu. Gerçi odict kullanmam gerekiyor, bu yüzden get ve set attr'yi geçersiz kılmak zorunda kaldım. Bunun özel kullanımların çoğunda çalışması gerektiğini düşünüyorum.

Kullanım şöyle görünür:

# 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

Sınıf:

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)

Bu, iş parçacığında daha önce bahsedilen oldukça havalı bir modeldir, ancak sadece bir diksiyon almak ve IDE'de otomatik tamamlama ile çalışan bir nesneye dönüştürmek istiyorsanız, vb:

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d


3

Ben bunu kullanıyorum

args = {
        'batch_size': 32,
        'workers': 4,
        'train_dir': 'train',
        'val_dir': 'val',
        'lr': 1e-3,
        'momentum': 0.9,
        'weight_decay': 1e-4
    }
args = namedtuple('Args', ' '.join(list(args.keys())))(**args)

print (args.lr)

Bu hızlı ve kirli bir cevap. Tek gözlemim / yorumum, adlandırılmış grup kurucusunun bir dizi listesini kabul edeceğini düşünüyorum, böylece çözümünüz basitleştirilebilir (sanırım):namedtuple('Args', list(args.keys()))(**args)
Dan Nguyen

2

Yeni yaptığım bu sınıfı kullanarak yapabilirsiniz. Bu sınıfla Mapnesneyi başka bir sözlük gibi kullanabilirsiniz (json serileştirmesi dahil) veya nokta gösterimi ile. Umarım yardımcı olurum:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Kullanım örnekleri:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

1
Gölge dictyöntemleri olabileceğini unutmayın , örneğin: m=Map(); m["keys"] = 42; m.keys()verir TypeError: 'int' object is not callable.
bfontaine

@bfontaine Fikir bir tür field/attributeve bir değil olmaktır method, ancak bir yöntem yerine bir sayı atarsanız, bu yönteme erişebilirsiniz m.method().
epool

2

Kinvais'in cevabını temel alan ancak http://databio.org/posts/python_AttributeDict.html'de önerilen AttributeDict'tan fikirleri entegre eden başka bir uygulama yayınlayayım .

Bu sürümün avantajı, iç içe sözlükler için de çalışmasıdır:

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

1

Çözüm:

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

Karar anahtarlarına bu şekilde erişmenin uyarıları ve tuzakları ne olurdu?

@Henry'nin de belirttiği gibi, dikimlerde noktalı erişimin kullanılmamasının bir nedeni, dikt anahtar isimlerini python-geçerli değişkenlerle sınırlaması ve böylece tüm olası isimleri kısıtlamasıdır.

Aşağıdakiler, bir diksiyon göz önüne alındığında, noktalı erişimin neden genel olarak yardımcı olmayacağına dair örnekler şunlardır d:

Geçerlilik

Aşağıdaki özellikler Python'da geçersiz olacaktır:

d.1_foo                           # enumerated names
d./bar                            # path names
d.21.7, d.12:30                   # decimals, time
d.""                              # empty strings
d.john doe, d.denny's             # spaces, misc punctuation 
d.3 * x                           # expressions  

stil

PEP8 kuralları, özellik adlandırmada yumuşak bir kısıtlama getirecektir:

Ayrılmış anahtar kelime (veya yerleşik işlev) adları:

d.in
d.False, d.True
d.max, d.min
d.sum
d.id

Bir işlev bağımsız değişkeninin adı ayrılmış bir anahtar kelimeyle çakışırsa, genellikle tek bir alt çizgi eklemek daha iyidir ...

B. Yöntemler ve değişken isimleri için vaka kuralı :

Değişken adları işlev adlarıyla aynı kuralı izler.

d.Firstname
d.Country

İşlev adlandırma kurallarını kullanın: okunabilirliği artırmak için alt çizgilerle ayrılmış sözcüklerle küçük harf.


Bazen bu endişeler panda gibi kütüphanelerde ortaya çıkar ve bu da DataFrame sütunlarının isme göre noktalı erişimine izin verir. Adlandırma kısıtlamalarını çözmek için varsayılan mekanizma da dizi gösterimidir - parantez içindeki bir dize.

Bu kısıtlamalar kullanım durumunuz için geçerli değilse, noktalı erişimli veri yapılarında birkaç seçenek vardır .



1

Bu 'iyi' bir cevap değil, ama bunun şık olduğunu düşündüm (mevcut formda iç içe dikleri işlemez). Sadece bir fonksiyonda dikteni sar:

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

Şimdi biraz farklı sözdizimine sahipsiniz. Diksiyon öğelerine öznitelikler gibi erişmek için f.key. Dict öğelerine (ve diğer dict yöntemlerine) her zamanki gibi erişmek için yapın ve f()['key']f'yi anahtar kelime argümanları ve / veya bir sözlükle çağırarak dict'i güncelleyebiliriz

Misal

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

Ve işte burada. Herkes bu yöntemin yararları ve dezavantajları önerirse mutlu olurum.


0

Doug'un belirttiği gibi, obj.keyişlevselliği elde etmek için kullanabileceğiniz bir Bunch paketi var . Aslında şu yeni bir sürüm var:

NeoBunch

Yine de, neobunchify işlevi aracılığıyla diktenizi bir NeoBunch nesnesine dönüştüren harika bir özelliğe sahiptir . Mako şablonlarını çok kullanıyorum ve NeoBunch nesneleri olarak verileri iletmek onları daha okunabilir hale getiriyor, bu nedenle Python programınızda normal bir dikte kullanmanız gerekiyorsa, ancak bir Mako şablonunda nokta gösterimini istiyorsanız:

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

Mako şablonu şöyle görünebilir:

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor

NeoBunch bağlantısı 404
DeusXMachina

0

En kolay yol, bir sınıf tanımlamak, ona Ad Alanı diyelim. bu dict üzerindeki dup .update () nesnesini kullanır . Ardından, söz bir nesne olarak ele alınacaktır.

class Namespace(object):
    '''
    helps referencing object in a dictionary as dict.key instead of dict['key']
    '''
    def __init__(self, adict):
        self.__dict__.update(adict)



Person = Namespace({'name': 'ahmed',
                     'age': 30}) #--> added for edge_cls


print(Person.name)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.