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 dict
ve 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, _RaiseKeyError
gerçekten bir argüman alıp almadığımızı bildiğimizden emin olmak için bir singleton ( ) dict.pop
oluş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 - super
tam 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.
( haskey
Python 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 update
ve 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.
MutableMapping
daha 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.
collections
Modü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 dict
alt 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 fromkeys
ben bu uygulamaların çoğu için devralma kullanabilirsiniz çünkü ancak bu oldukça önemsizdir -.
MutableMapping
Uygular Python bazı işler o dict
C uygular - Bir beklenebilir böylece dict
alt 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 dict
alt 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
dict
daha 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.