Bir dikteyi "mükemmel şekilde" nasıl geçersiz kılabilirim?


218

Mümkün olduğunca mükemmel bir diksiyon alt sınıfı nasıl yapabilirim ? Nihai hedef, anahtarların küçük harf olduğu basit bir dikte oluşturmaktır.

Bu işi yapmak için geçersiz kılabileceğim küçük bir ilkel kümesi olmalı gibi görünüyor, ancak tüm araştırmalarım ve girişimlerime göre durum böyle değil:

İşte ilk denemem, get()çalışmıyor ve şüphesiz birçok küçük sorun var:

class arbitrary_dict(dict):
    """A dictionary that applies an arbitrary key-altering function
       before accessing the keys."""

    def __keytransform__(self, key):
        return key

    # Overridden methods. List from 
    # /programming/2390827/how-to-properly-subclass-dict

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    # Note: I'm using dict directly, since super(dict, self) doesn't work.
    # I'm not sure why, perhaps dict is not a new-style class.

    def __getitem__(self, key):
        return dict.__getitem__(self, self.__keytransform__(key))

    def __setitem__(self, key, value):
        return dict.__setitem__(self, self.__keytransform__(key), value)

    def __delitem__(self, key):
        return dict.__delitem__(self, self.__keytransform__(key))

    def __contains__(self, key):
        return dict.__contains__(self, self.__keytransform__(key))


class lcdict(arbitrary_dict):
    def __keytransform__(self, key):
        return str(key).lower()

Bence __keytransform __ () statik olmalı. Güzel yaklaşım olsa. (prepending @staticmethod)
Aiyion.Prime

Yanıtlar:


229

Modülden ABC s (Soyut Temel Sınıflar) dictile oldukça kolay davranan bir nesne yazabilirsiniz . Bir yöntemi kaçırıp kaçırmamanızı bile söyler, bu yüzden aşağıda ABC'yi kapatan minimal sürüm.collections.abc

from collections.abc import MutableMapping


class TransformedDict(MutableMapping):
    """A dictionary that applies an arbitrary key-altering
       function before accessing the keys"""

    def __init__(self, *args, **kwargs):
        self.store = dict()
        self.update(dict(*args, **kwargs))  # use the free update to set keys

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

    def __setitem__(self, key, value):
        self.store[self.__keytransform__(key)] = value

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

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

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

    def __keytransform__(self, key):
        return key

ABC'den birkaç ücretsiz yöntem elde edersiniz:

class MyTransformedDict(TransformedDict):

    def __keytransform__(self, key):
        return key.lower()


s = MyTransformedDict([('Test', 'test')])

assert s.get('TEST') is s['test']   # free get
assert 'TeSt' in s                  # free __contains__
                                    # free setdefault, __eq__, and so on

import pickle
# works too since we just use a normal dict
assert pickle.loads(pickle.dumps(s)) == s

dictDoğrudan alt sınıf (ya da diğer yerleşikler) olmazdı . Çoğu zaman mantıklı değildir, çünkü aslında yapmak istediğiniz şey adict . ABC'ler tam da bunun için.


46
Tanımlayıcı: Adlandırma Stilleri bölümünün __keytransform__()sonunda "Asla bu tür adları asla icat etmeyin, yalnızca belgelendiği gibi kullanın" önerisini veren PEP 8 stil kılavuzunu ihlal ettiği için yeniden adlandırmayı öneririm .
martineau

1
Ancak soru - bu arabirimi kullanıcı tanımlı bir türle uygulamak genellikle yerleşik türü kullanan daha yavaş dikte benzeri işlemlerle sonuçlanmaz mı?
twneale

2
Bunu yapmanın bir yolu var mı? İsinstance (_, dict) == Doğru mu? Yoksa sadece alt sınıfı oluşturmak için Değişken Eşleme mi kullanıyorsunuz?
Andy Hayden

5
@AndyHayden: Yazmalısın if isinstance(t, collections.MutableMapping): print t, "can be used like a dict". Bir nesnenin türünü kontrol etmeyin, arayüzü kontrol edin.
Jochen Ritzel

2
@NeilG Maalesef python standart kütüphanesindeki JSONEncoder'ı içeriyor
Andy Smith

97

Mümkün olduğunca "mükemmel" bir diksiyon alt sınıfı nasıl yapabilirim?

Nihai hedef, anahtarların küçük harf olduğu basit bir dikte oluşturmaktır.

  • __getitem__/ ' I geçersiz kılarsam __setitem__, get / set çalışmaz. Onları nasıl çalıştırabilirim? Elbette bunları tek tek uygulamama gerek yok mu?

  • Turşunun çalışmasını engelliyor muyum ve __setstate__vb . Uygulamam gerekiyor mu?

  • Repr, update ve gerekiyor __init__mu?

  • Sadece kullanmalı mıyım mutablemapping(o kimse kullanmamalıdır görünüyor UserDict ya DictMixin)? Öyleyse nasıl? Dokümanlar tam olarak aydınlatıcı değil.

Kabul edilen cevap benim ilk yaklaşımım olurdu, ama bazı sorunları olduğu için ve hiç kimse alternatifi, aslında alt sınıf a'yı ele almadığı için dict, bunu burada yapacağım.

Kabul edilen cevapta yanlış olan ne?

Bu benim için oldukça basit bir istek gibi görünüyor:

Mümkün olduğunca "mükemmel" bir diksiyon alt sınıfı nasıl yapabilirim? Nihai hedef, anahtarların küçük harf olduğu basit bir dikte oluşturmaktır.

Kabul edilen cevap aslında alt sınıf değildir dictve bunun için yapılan bir test başarısız olur:

>>> isinstance(MyTransformedDict([('Test', 'test')]), dict)
False

İdeal olarak, herhangi bir tip kontrol kodu beklediğimiz arabirimi veya soyut bir temel sınıfı test eder, ancak veri nesnelerimiz test eden işlevlere geçirilirse dict- ve bu işlevleri "düzeltemezsek", bu kod başaramayacak.

Kişinin yapabileceği diğer tartışmalar:

  • Kabul cevabı da classmethod eksik: fromkeys.
  • Kabul edilen cevabın da fazlalığı vardır __dict__- bu nedenle bellekte daha fazla yer kaplar:

    >>> s.foo = 'bar'
    >>> s.__dict__
    {'foo': 'bar', 'store': {'test': 'test'}}

Aslında alt sınıflama dict

Diksiyon yöntemlerini kalıtım yoluyla tekrar kullanabiliriz. Tek yapmamız gereken, eğer anahtarlar dizeyse anahtarların küçük harfle geçirilmesini sağlayan bir arayüz katmanı oluşturmaktır.

__getitem__/ ' I geçersiz kılarsam __setitem__, get / set çalışmaz. Onları nasıl çalıştırabilirim? Elbette bunları tek tek uygulamama gerek yok mu?

Her birini ayrı ayrı uygulamak, bu yaklaşımın dezavantajı ve kullanmanın tersidir MutableMapping(kabul edilen cevaba bakınız), ama gerçekten çok daha fazla iş değil.

İlk olarak, Python 2 ve 3 arasındaki farkı göz önüne alalım, _RaiseKeyErrorgerçekten bir argüman alıp almadığımızı bildiğimizden emin olmak için bir singleton ( ) dict.popoluşturalım ve dize anahtarlarımızın küçük harfli olmasını sağlamak için bir işlev oluşturalım:

from itertools import chain
try:              # Python 2
    str_base = basestring
    items = 'iteritems'
except NameError: # Python 3
    str_base = str, bytes, bytearray
    items = 'items'

_RaiseKeyError = object() # singleton for no-default behavior

def ensure_lower(maybe_str):
    """dict keys can be any hashable object - only call lower if str"""
    return maybe_str.lower() if isinstance(maybe_str, str_base) else maybe_str

Şimdi uygulamak - supertam kodları ile kullanıyorum böylece bu kod Python 2 ve 3 için çalışır:

class LowerDict(dict):  # dicts take a mapping or iterable as their optional first argument
    __slots__ = () # no __dict__ - that would be redundant
    @staticmethod # because this doesn't make sense as a global function.
    def _process_args(mapping=(), **kwargs):
        if hasattr(mapping, items):
            mapping = getattr(mapping, items)()
        return ((ensure_lower(k), v) for k, v in chain(mapping, getattr(kwargs, items)()))
    def __init__(self, mapping=(), **kwargs):
        super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
    def __getitem__(self, k):
        return super(LowerDict, self).__getitem__(ensure_lower(k))
    def __setitem__(self, k, v):
        return super(LowerDict, self).__setitem__(ensure_lower(k), v)
    def __delitem__(self, k):
        return super(LowerDict, self).__delitem__(ensure_lower(k))
    def get(self, k, default=None):
        return super(LowerDict, self).get(ensure_lower(k), default)
    def setdefault(self, k, default=None):
        return super(LowerDict, self).setdefault(ensure_lower(k), default)
    def pop(self, k, v=_RaiseKeyError):
        if v is _RaiseKeyError:
            return super(LowerDict, self).pop(ensure_lower(k))
        return super(LowerDict, self).pop(ensure_lower(k), v)
    def update(self, mapping=(), **kwargs):
        super(LowerDict, self).update(self._process_args(mapping, **kwargs))
    def __contains__(self, k):
        return super(LowerDict, self).__contains__(ensure_lower(k))
    def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
        return type(self)(self)
    @classmethod
    def fromkeys(cls, keys, v=None):
        return super(LowerDict, cls).fromkeys((ensure_lower(k) for k in keys), v)
    def __repr__(self):
        return '{0}({1})'.format(type(self).__name__, super(LowerDict, self).__repr__())

Biz herhangi bir yöntemle veya özel yöntem için neredeyse kazan plakalı yaklaşımını kullanmak referanslar bu anahtar, ama aksi halde, miras yoluyla, biz yöntemleri olsun: len, clear, items, keys, popitem, ve valuesücretsiz. Bu doğru olmak için dikkatli bir düşünce gerektirse de, bunun işe yaradığını görmek önemsizdir.

( haskeyPython 2'de kaldırılmış, Python 3'te kaldırılmıştır.)

İşte bazı kullanımları:

>>> ld = LowerDict(dict(foo='bar'))
>>> ld['FOO']
'bar'
>>> ld['foo']
'bar'
>>> ld.pop('FoO')
'bar'
>>> ld.setdefault('Foo')
>>> ld
{'foo': None}
>>> ld.get('Bar')
>>> ld.setdefault('Bar')
>>> ld
{'bar': None, 'foo': None}
>>> ld.popitem()
('bar', None)

Turşunun çalışmasını engelliyor muyum ve __setstate__vb . Uygulamam gerekiyor mu?

asitle temizleme

Ve dik alt sınıf turşusu gayet iyi:

>>> import pickle
>>> pickle.dumps(ld)
b'\x80\x03c__main__\nLowerDict\nq\x00)\x81q\x01X\x03\x00\x00\x00fooq\x02Ns.'
>>> pickle.loads(pickle.dumps(ld))
{'foo': None}
>>> type(pickle.loads(pickle.dumps(ld)))
<class '__main__.LowerDict'>

__repr__

Repr, update ve gerekiyor __init__mu?

Biz tanımladık updateve varsayılan olarak __init__güzel bir şey var __repr__:

>>> ld # without __repr__ defined for the class, we get this
{'foo': None}

Ancak, __repr__kodunuzun hata ayıklamasını geliştirmek için bir a yazmak iyidir . İdeal test eval(repr(obj)) == obj. Kodunuz için yapılması kolaysa, kesinlikle tavsiye ederim:

>>> ld = LowerDict({})
>>> eval(repr(ld)) == ld
True
>>> ld = LowerDict(dict(a=1, b=2, c=3))
>>> eval(repr(ld)) == ld
True

Görüyorsunuz, tam olarak eşdeğer bir nesneyi yeniden yaratmamız gerekiyor - bu, günlüklerimizde veya geri izlemelerde görünebilecek bir şey:

>>> ld
LowerDict({'a': 1, 'c': 3, 'b': 2})

Sonuç

Sadece kullanmalı mıyım mutablemapping(o kimse kullanmamalıdır görünüyor UserDict ya DictMixin)? Öyleyse nasıl? Dokümanlar tam olarak aydınlatıcı değil.

Evet, bunlar birkaç kod satırı daha, ancak kapsamlı olmaları amaçlanıyor. İlk eğilimim kabul edilen cevabı kullanmak olacaktır ve eğer bununla ilgili sorunlar olsaydı, cevabıma bakardım - biraz daha karmaşık olduğundan ve arayüzümü doğru yapmama yardımcı olacak ABC yok.

Erken optimizasyon, performans arayışında daha fazla karmaşıklık sağlayacaktır. MutableMappingdaha basittir - bu yüzden her şey eşit olmak üzere anında bir avantaj elde eder. Bununla birlikte, tüm farklılıkları ortaya koymak için, karşılaştıralım ve kontrast oluşturalım.

collectionsModüle benzer bir sözlük koymak için bir itme olduğunu eklemeliyim , ancak reddedildi . Muhtemelen bunu sadece yapmalısınız:

my_dict[transform(key)]

Çok daha kolay hata ayıklanabilir olmalıdır.

Karşılaştır ve kıyas et

MutableMapping(Eksik olan fromkeys) 6 ve dictalt sınıfla 11 arabirim işlevi vardır . Ben uygulamak gerekmez __iter__ya __len__, ama bunun yerine uygulamak zorunda get, setdefault, pop, update, copy, __contains__, ve fromkeysben bu uygulamaların çoğu için devralma kullanabilirsiniz çünkü ancak bu oldukça önemsizdir -.

MutableMappingUygular Python bazı işler o dictC uygular - Bir beklenebilir böylece dictalt sınıf bazı durumlarda daha fazla ölçülebilir olması.

Her __eq__iki yaklaşımda da özgürleşiyoruz - her ikisi de sadece başka bir diksiyon küçük harfle eşitliği üstleniyor - ama yine de dictalt sınıfın daha hızlı karşılaştırılacağını düşünüyorum .

Özet:

  • alt sınıflandırma MutableMapping, hatalar için daha az fırsatla daha basittir, ancak daha yavaştır, daha fazla bellek alır (gereksiz diksele bakın) ve başarısız olurisinstance(x, dict)
  • alt sınıflandırma dictdaha hızlıdır, daha az bellek kullanır ve geçer isinstance(x, dict), ancak uygulanması daha karmaşıktır.

Hangisi daha mükemmel? Bu sizin mükemmel tanımınıza bağlıdır.


Kabul edilen cevap gereksiz dikmeyi nasıl kaldıracaktı?
Seanny123

1
Hemen akla gelen iki yol, ya mağaza özniteliğini ilan etmek ya __slots__da belki de __dict__mağaza olarak yeniden kullanmaktır , ancak bu, başka bir potansiyel eleştiri noktası olan anlambilimi karıştırır.
Aaron Hall

1
Bir yöntem ensure_loweralan ve ilk argümanında (her zaman anahtar olan) kullanan bir dekoratör yazmak daha kolay olmaz mıydı? Sonra aynı sayıda geçersiz kılma olurdu, ama hepsi formda olacaktı __getitem__ = ensure_lower_decorator(super(LowerDict, self).__getitem__).
Graipher

1
Bunun için teşekkürler - temel sınıf yönteminin imzasıyla eşleşmedikleri pop ve fromkeys için uyarılar alma.
Mr_and_Mrs_D

1
@Mr_and_Mrs_D Bir uygulama ekledim copy- Sanırım bunu yapmalı, değil mi? Ben arayüz için test gerektiğini düşünüyorum - örneğin pandas DataFrame nesnesi bir Mapping örneği değil (son kontrol) ama öğeleri / iteritems var.
Aaron Hall

4

Gereksinimlerim biraz daha katı:

  • Vaka bilgilerini tutmak zorunda kaldım (dizeler kullanıcıya görüntülenen dosyaların yollarıdır, ancak dahili olarak tüm işlemler büyük / küçük harf duyarsız olmalıdır)
  • Ben (mümkün olduğunca küçük olması anahtarları gerekli yaptılar 370 üzerinden 110 mb kesilmiş bellek performansında bir fark, olun). Bu, anahtarların küçük harfli sürümlerinin önbelleğe alınmasının bir seçenek olmadığı anlamına geliyordu.
  • Mümkün olduğunca hızlı olması için veri yapılarının oluşturulmasına ihtiyacım vardı (yine performansta bir fark yarattım, bu sefer hız). Yerleşik bir yere gitmek zorunda kaldım

İlk düşüncem, clunky Path sınıfımızı, büyük / küçük harf duyarsız unicode alt sınıfının yerine koymaktı - ama:

  • bunu doğru yapmak zor oldu - bkz: Python büyük / küçük harf duyarsız bir dize sınıfı
  • Açık dict tuşlarının işlenmesinin kod ayrıntılı ve dağınık hale geldiği ortaya çıkıyor - ve hataya eğilimli (yapılar buraya ve oradan geçirilir ve anahtarlar / öğeler olarak CIStr örneklerine sahip olup olmadıkları açık değildir, unutması kolay artı some_dict[CIstr(path)]çirkin)

Sonunda bu davaya duyarsız bir karar yazmak zorunda kaldım. Sayesinde kod 10 kez daha kolay hale geldi @AaronHall tarafından.

class CIstr(unicode):
    """See https://stackoverflow.com/a/43122305/281545, especially for inlines"""
    __slots__ = () # does make a difference in memory performance

    #--Hash/Compare
    def __hash__(self):
        return hash(self.lower())
    def __eq__(self, other):
        if isinstance(other, CIstr):
            return self.lower() == other.lower()
        return NotImplemented
    def __ne__(self, other):
        if isinstance(other, CIstr):
            return self.lower() != other.lower()
        return NotImplemented
    def __lt__(self, other):
        if isinstance(other, CIstr):
            return self.lower() < other.lower()
        return NotImplemented
    def __ge__(self, other):
        if isinstance(other, CIstr):
            return self.lower() >= other.lower()
        return NotImplemented
    def __gt__(self, other):
        if isinstance(other, CIstr):
            return self.lower() > other.lower()
        return NotImplemented
    def __le__(self, other):
        if isinstance(other, CIstr):
            return self.lower() <= other.lower()
        return NotImplemented
    #--repr
    def __repr__(self):
        return '{0}({1})'.format(type(self).__name__,
                                 super(CIstr, self).__repr__())

def _ci_str(maybe_str):
    """dict keys can be any hashable object - only call CIstr if str"""
    return CIstr(maybe_str) if isinstance(maybe_str, basestring) else maybe_str

class LowerDict(dict):
    """Dictionary that transforms its keys to CIstr instances.
    Adapted from: https://stackoverflow.com/a/39375731/281545
    """
    __slots__ = () # no __dict__ - that would be redundant

    @staticmethod # because this doesn't make sense as a global function.
    def _process_args(mapping=(), **kwargs):
        if hasattr(mapping, 'iteritems'):
            mapping = getattr(mapping, 'iteritems')()
        return ((_ci_str(k), v) for k, v in
                chain(mapping, getattr(kwargs, 'iteritems')()))
    def __init__(self, mapping=(), **kwargs):
        # dicts take a mapping or iterable as their optional first argument
        super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
    def __getitem__(self, k):
        return super(LowerDict, self).__getitem__(_ci_str(k))
    def __setitem__(self, k, v):
        return super(LowerDict, self).__setitem__(_ci_str(k), v)
    def __delitem__(self, k):
        return super(LowerDict, self).__delitem__(_ci_str(k))
    def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
        return type(self)(self)
    def get(self, k, default=None):
        return super(LowerDict, self).get(_ci_str(k), default)
    def setdefault(self, k, default=None):
        return super(LowerDict, self).setdefault(_ci_str(k), default)
    __no_default = object()
    def pop(self, k, v=__no_default):
        if v is LowerDict.__no_default:
            # super will raise KeyError if no default and key does not exist
            return super(LowerDict, self).pop(_ci_str(k))
        return super(LowerDict, self).pop(_ci_str(k), v)
    def update(self, mapping=(), **kwargs):
        super(LowerDict, self).update(self._process_args(mapping, **kwargs))
    def __contains__(self, k):
        return super(LowerDict, self).__contains__(_ci_str(k))
    @classmethod
    def fromkeys(cls, keys, v=None):
        return super(LowerDict, cls).fromkeys((_ci_str(k) for k in keys), v)
    def __repr__(self):
        return '{0}({1})'.format(type(self).__name__,
                                 super(LowerDict, self).__repr__())

Örtük vs açık hala bir sorun, ama toz yerleştiğinde, ci ile başlamak için niteliklerin / değişkenlerin yeniden adlandırılması (ve ci'nin büyük / küçük harf duyarsız olduğunu açıklayan büyük bir yağ dokümanı yorumu) mükemmel bir çözüm olduğunu düşünüyorum - kod okuyucuları olarak büyük / küçük harf duyarlı olmayan temel veri yapıları ile uğraştığımızın tamamen farkında olun. Bu, umarım büyük / küçük harfe duyarlılıktan kaynaklandığından şüphelendiğim bazı hataları yeniden üretmemizi düzeltir.

Yorumlar / düzeltmeler hoş geldiniz :)


CIstr en eval (repr (obj)) == obj testi (şu anda sanmıyorum) geçmek ve güvenmemek __repr__için üst sınıf kullanmalıdır . __repr____str__
Aaron Hall

Ayrıca unicode alt sınıfınızdan 4 yöntemi ortadan kaldıracak total_orderingsınıf dekoratörüne göz atın. Ancak dikt alt sınıfı çok zekice uygulanmış görünüyor. : P
Aaron Hall

Teşekkürler @AaronHall - bunu uygulayan sizsiniz: P Re: toplam sipariş - Kasten Raymond Hettinger tarafından önerildiği gibi satır içi yöntemleri yazdım: stackoverflow.com/a/43122305/281545 . Re: repr: Bir yorum okuduğumu hatırlıyorum (bazı çekirdek IIRC tarafından), o testi geçmek için repr yapmak için uğraşmaya değmez (bu bir güçlük) - mümkün olduğunca bilgilendirici olmaya daha iyi odaklanın ( but more more)
Mr_and_Mrs_D

Ben senin gereksiz karşılaştırma yöntemleri (Sorununuza bununla ilgili not etmeleri) size izin vereceğiz, ama CIstr.__repr__, içinde senin durumda, çok az güçlük ile repr testini geçebilir ve bunu çok daha güzel ayıklama yapmalıdır. __repr__Senin diktene bir de eklerdim. Bunu göstermek için cevabımda yapacağım.
Aaron Hall

@AaronHall: __slots__CIstr'a ekledim - performansta bir fark yaratıyor (CIstr, alt sınıfta olması veya gerçekten LowerDict dışında kullanılması değil, statik iç içe bir son sınıf olmalıdır). Yine de repr sorununun zarif bir şekilde nasıl çözüleceğinden emin değilsiniz (acı, bir 've "tırnak kombinasyonunu içerebilir )
Mr_and_Mrs_D

4

Tek yapman gereken

class BatchCollection(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(*args, **kwargs)

VEYA

class BatchCollection(dict):
    def __init__(self, inpt={}):
        super(BatchCollection, self).__init__(inpt)

Kişisel kullanımım için örnek kullanım

### EXAMPLE
class BatchCollection(dict):
    def __init__(self, inpt={}):
        dict.__init__(*args, **kwargs)

    def __setitem__(self, key, item):
        if (isinstance(key, tuple) and len(key) == 2
                and isinstance(item, collections.Iterable)):
            # self.__dict__[key] = item
            super(BatchCollection, self).__setitem__(key, item)
        else:
            raise Exception(
                "Valid key should be a tuple (database_name, table_name) "
                "and value should be iterable")

Not : sadece python3'te test edilmiştir


3

En iyi iki öneriyi denedikten sonra , Python 2.7 için gölgeli görünen bir orta rotaya yerleştim. Belki 3 daha akılcı, ama benim için:

class MyDict(MutableMapping):
   # ... the few __methods__ that mutablemapping requires
   # and then this monstrosity
   @property
   def __class__(self):
       return dict

ki bu gerçekten nefret ediyorum, ama ihtiyaçlarımı karşılayacak gibi görünüyor:

  • geçersiz kılabilir **my_dict
    • Eğer miras alırsanız dict, bu kodunuzu atlar . denemek.
    • bu # 2 benim için her zaman kabul edilemez yapar , çünkü bu python kodunda oldukça yaygındır
  • gibi maskeli balolar isinstance(my_dict, dict)
    • MutableMapping'i tek başına dışlıyor, bu yüzden # 1 yeterli değil
    • İhtiyacınız yoksa # 1 yürekten tavsiye ederim , basit ve tahmin edilebilir
  • tamamen kontrol edilebilir davranış
    • bu yüzden miras alamıyorum dict

Kendinizi başkalarından ayırmanız gerekiyorsa, şahsen ben böyle bir şey kullanıyorum (daha iyi isimler önermeme rağmen):

def __am_i_me(self):
  return True

@classmethod
def __is_it_me(cls, other):
  try:
    return other.__am_i_me()
  except Exception:
    return False

Kendinizi yalnızca dahili olarak tanımanız gerektiği sürece, bu şekilde __am_i_mepython'un ad-munging'i nedeniyle yanlışlıkla aramak daha zordur (bu,_MyDict__am_i_me bu sınıfın dışında kalan herhangi bir şeyden ). _methodHem pratikte hem de kültürel olarak s'den biraz daha özel .

Şimdiye kadar, ciddi gölgeli görünümlü __class__geçersiz kılma dışında hiçbir şikayetim yok . Ben olurdum heyecanYine de başkalarının bununla karşılaştığı herhangi bir problemi duymak , sonuçlarını tam olarak anlamıyorum. Ama şu ana kadar hiçbir sorunum olmadı ve bu, herhangi bir değişikliğe ihtiyaç duymadan birçok yerde çok fazla orta kalitede kod taşımamı sağladı.


Kanıt olarak: https://repl.it/repls/TraumaticToughCockatoo

Temel olarak: geçerli # 2 seçeneğini kopyalayın ,print 'method_name' her yönteme satır ekleyin ve ardından bunu deneyin ve çıktıyı izleyin:

d = LowerDict()  # prints "init", or whatever your print statement said
print '------'
splatted = dict(**d)  # note that there are no prints here

Diğer senaryolar için benzer davranışlar göreceksiniz. Sahte dictolduğunuzu söyleyin - diğer bazı veri türlerinin etrafını saran bir pakettir , bu nedenle verileri destek aşamasında saklamanın makul bir yolu yoktur;**your_dictdiğer yöntemlerin ne yaptığından bağımsız olarak boş olacaktır.

Bu doğru çalışır MutableMapping, ancak dictondan miras alır almaz kontrol edilemez hale gelir.


Düzenleme: bir güncelleme olarak, bu neredeyse iki yıldır, birkaç yüz bin (eh, birkaç milyon olabilir) karmaşık, eski basmış python satırlarında tek bir sorun olmadan çalışıyor. Bu yüzden çok mutluyum :)

Edit 2: görünüşe göre bu ya da uzun bir süre önce yanlış kopyaladım. çekler @classmethod __class__için çalışmaz isinstance- @property __class__yapar: https://repl.it/repls/UnitedScientificSequence


" **your_dictBoş olacak " ile tam olarak ne demek istersiniz (alt sınıftan alırsanız dict)? Ben ... dict veri açma herhangi bir sorun görmedim
Matt P

Verileri üst diksiyona (LowerDict'ın yaptığı gibi) koyarsanız, çalışır - dikte saklanan verileri alırsınız. Eğer varsa yok , bunu fark edeceksiniz: (Her zaman 's okuma içinde doldurur demek sen { "erişimin yığın izleme" access_count} gibi sinek, verileri üretmek istedik) **your_dicto yüzden kodunuzu yürütmüyor "özel" bir şey çıktı olamaz. Örneğin, "okumaları" sayamazsınız çünkü okuma sayımı kodunuzu yürütmez. MutableMapping yapar bunun için çalışmalarını (eğer yapabilirsen kullanın!), Ancak başarısız isinstance(..., dict)bunu kullanamadı yüzden. eski yazılım.
Groxx

Tamam, şimdi ne demek istediğini anlıyorum. Ben kod yürütme beklemiyordum sanırım **your_dict, ama bunu MutableMappingyapacak çok ilginç buluyorum .
Matt P

Evet. Bir dizi şey için gerekli (örneğin RPC çağrılarını yerel olarak okunan bir şeye dönüştürüyordum ve Reasons ™ için talep üzerine yapmak zorunda kaldım) ve çok az insan bunun farkında bile görünüyor, **some_dictoldukça yaygındır. En azından o varsa, yani dekoratörler çok sık olur herhangi bunun için hesap yoksa, görünüşte imkansız aksaklığı riski hemen konum.
Groxx

Belki bir şey eksik, ama def __class__()hile Python 2 veya 3 ile çalışmıyor gibi görünüyor, en azından abc.MutableMapping uygulama bir dict alt sınıf olarak nasıl kaydedilir? (aksi takdirde iki versiyonda çalışacak şekilde değiştirilmiştir). isinstance(SpreadSheet(), dict)Geri dönmek istiyorum True.
martineau
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.