Dikte gibi davranan bir python sınıfı


114

Şöyle davranan özel bir sınıf yazmak istiyorum dict- yani, miras alıyorum dict.

Sorum şu: Yöntemimde özel bir dictüye oluşturmam gerekir __init__()mi? Bunun anlamını anlamıyorum, çünkü dictsadece miras alırsam zaten davranışa sahibim dict.

Miras parçacıklarının çoğunun neden aşağıdakine benzediğini kimse söyleyebilir mi?

class CustomDictOne(dict):
   def __init__(self):
      self._mydict = {} 

   # other methods follow

Daha basit yerine ...

class CustomDictTwo(dict):
   def __init__(self):
      # initialize my other stuff here ...

   # other methods follow

Aslında, sorunun cevabının kullanıcıların doğrudan sözlüğünüze erişememesi için (yani sizin sağladığınız erişim yöntemlerini kullanmaları gerektiğinden) şüpheleniyorum.

Ancak dizi erişim operatörü ne olacak []? Bunu nasıl uygularsınız? Şimdiye kadar, []operatörün nasıl geçersiz kılınacağını gösteren bir örnek görmedim .

Öyleyse [], özel sınıfta bir erişim işlevi sağlanmazsa, miras alınan temel yöntemler farklı bir sözlükte çalışacak mı?

Python kalıtım anlayışımı test etmek için aşağıdaki parçacığı denedim:

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(self, id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)
print md[id]

Aşağıdaki hatayı aldım:

KeyError: <yerleşik işlev kimliği>

Yukarıdaki kodda yanlış olan ne?

Böyle bir myDictkod yazabilmek için sınıfı nasıl düzeltirim ?

md = myDict()
md['id'] = 123

[Düzenle]

Masamdan ayrılmadan önce yaptığım aptalca hatadan kurtulmak için yukarıdaki kod örneğini düzenledim. Bir yazım hatasıydı (hata mesajından fark etmeliydim).

Yanıtlar:



105
class Mapping(dict):

    def __setitem__(self, key, item):
        self.__dict__[key] = item

    def __getitem__(self, key):
        return self.__dict__[key]

    def __repr__(self):
        return repr(self.__dict__)

    def __len__(self):
        return len(self.__dict__)

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

    def clear(self):
        return self.__dict__.clear()

    def copy(self):
        return self.__dict__.copy()

    def has_key(self, k):
        return k in self.__dict__

    def update(self, *args, **kwargs):
        return self.__dict__.update(*args, **kwargs)

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

    def values(self):
        return self.__dict__.values()

    def items(self):
        return self.__dict__.items()

    def pop(self, *args):
        return self.__dict__.pop(*args)

    def __cmp__(self, dict_):
        return self.__cmp__(self.__dict__, dict_)

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

    def __iter__(self):
        return iter(self.__dict__)

    def __unicode__(self):
        return unicode(repr(self.__dict__))


o = Mapping()
o.foo = "bar"
o['lumberjack'] = 'foo'
o.update({'a': 'b'}, c=44)
print 'lumberjack' in o
print o

In [187]: run mapping.py
True
{'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44}

37
Alt sınıfa gidecekseniz , basitçe örneğe yetki vermek yerine dictnesnenin kendisini (kullanarak super) kullanmalısınız __dict__- bu, esasen her örnek için iki dikt oluşturduğunuz anlamına gelir.
Aaron Hall

8
self .__ dict__, gerçek sözlük içeriği ile aynı değildir . Her python nesnesi, türüne bakılmaksızın _dict__, tüm nesne niteliklerini (yöntemler, alanlar vb.) İçeren bir nesneye sahiptir. Sen do not you ... kendini değiştirerek olduğunu yazma koduna istemedikçe bu etrafında karışıklık istiyor
Raik

86

Bunun gibi

class CustomDictOne(dict):
   def __init__(self,*arg,**kw):
      super(CustomDictOne, self).__init__(*arg, **kw)

Şimdi kullanabilirsiniz yerleşik işlevleri gibi dict.get()olarak self.get().

Bir gizli sarmanıza gerek yok self._dict. Sınıfınız zaten olan dict.


3
Bu. dictÖnce kurucusunu çağırmadan miras almanın bir anlamı yok .
sykora

1
dictKalıtımın gerçekte 2 dict-örneğini içerdiğine dikkat edin : Birincisi, miras alınan kaptır ve ikincisi, sınıf özniteliklerini tutan diktedir - yuvaları kullanarak bundan kaçınabilirsiniz .
ankostis

1
__dict__Yani sürece kullanıcıların, kullanmak 's cezası girişimi yok gibi aslında sadece ilk erişildiğinde oluşturulur olduğunu. __slots__yine de güzel olurdu.
Aaron Hall

9
Virgülden sonra boşluk kullanın, vahşi! ;-)
James Burke

10

Eksiksizlik adına, en son Python 2.x (yazım tarihi itibariyle 2.7.7) için @ björn-pollex tarafından belirtilen dokümantasyonun bağlantısını burada bulabilirsiniz:

Kapsayıcı Türlerini Taklit Etme

(Yorumlar işlevini kullanmadığım için üzgünüm, stackoverflow tarafından bunu yapmama izin verilmiyor.)


3

Bu kod yığınındaki sorun:

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)

... 'add' yönteminizin (... ve bir sınıfın üyesi olmak istediğiniz herhangi bir yöntemin) ilk argümanı olarak açık bir 'kendisinin' beyan edilmiş olması gerektiğidir, örneğin:

def add(self, 'id', 23):

Öğelere anahtara göre erişmek için operatör aşırı yüklemesini uygulamak için, sihirli yöntemlerin belgelerine bakın __getitem__ve __setitem__.

Python, Duck Typing'i kullandığından, ne yapmaya çalıştığınızı bilmeden (örneğin, bunun bir örneğini iletmeniz gerekirse sürece kıracak bazı kod bir yere mi sınıf isinstance(MyDict(), dict) == True), sadece yeterince dict benzeri ve orada durdurma senin sınıfını ortaya API uygulaması daha iyi olabilir.


3

İşte alternatif bir çözüm:

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

a = AttrDict()
a.a = 1
a.b = 2

Bu kötü çünkü herhangi bir özel yöntem tanımlamıyorsunuz ve ayrıca diğer yanıtın söylediği gibi başka sorunlar da var.
Shital Shah

bu tam olarak ihtiyacım olan şey. Teşekkür ederim!
jakebrinkmann

2

Buna gerçekten doğru cevabı hiçbir yerde göremiyorum

class MyClass(dict):
    
    def __init__(self, a_property):
        self[a_property] = a_property

Gerçekte yapmanız gereken tek şey, kendinizinkini tanımlamak __init__- gerçekten de hepsi bu.

Başka bir örnek (biraz daha karmaşık):

class MyClass(dict):

    def __init__(self, planet):
        self[planet] = planet
        info = self.do_something_that_returns_a_dict()
        if info:
            for k, v in info.items():
                self[k] = v

    def do_something_that_returns_a_dict(self):
        return {"mercury": "venus", "mars": "jupiter"}

Bu son örnek, bir tür mantık yerleştirmek istediğinizde kullanışlıdır.

Her neyse ... Kısacası class GiveYourClassAName(dict), sınıfınızın bir dikte gibi davranması için yeterlidir. Yapacağınız herhangi bir dikte operasyonu selfnormal bir dikte gibi olacaktır.


1

Bu benim en iyi çözümüm. Bunu birçok kez kullandım.

class DictLikeClass:
    ...
    def __getitem__(self, key):
        return getattr(self, key)

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

Gibi kullanabilirsiniz:

>>> d = DictLikeClass()
>>> d["key"] = "value"
>>> print(d["key"])

0

Asla Python yerleşik diktesinden miras almayın! örneğin updateyöntem kullanmaz __setitem__, optimizasyon için çok şey yaparlar. UserDict kullanın.

from collections import UserDict

class MyDict(UserDict):
    def __delitem__(self, key):
        pass
    def __setitem__(self, key, value):
        pass

1
Birisi yerleşik dikteden asla miras almamalı mı? Dokümantasyondan ( docs.python.org/3/library/collections.html#collections.UserDict ): "Bu sınıfa duyulan ihtiyacın yerini, doğrudan dict'den alt sınıfa alabilme yeteneği almıştır; ancak, bu sınıf daha kolay olabilir ile çalışın çünkü temeldeki sözlüğe bir öznitelik olarak erişilebilir. " Ayrıca aynı sayfada: Koleksiyon modülü "3.3 sürümünden beri kullanımdan kaldırıldı, sürüm 3.9'da kaldırılacak: Koleksiyonlar Özet Temel Sınıfları collections.abc modülüne taşındı." ... UserDict içermeyen.
NumesSanguis

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.