एक "जमे हुए तानाशाह" क्या होगा?


158
  • एक जमे हुए सेट एक फ्रोज़ेनसेट है।
  • एक जमी हुई सूची टपल हो सकती है।
  • एक जमे हुए हुक्म क्या होगा? एक अपरिवर्तनीय, धोने योग्य तानाशाही।

मुझे लगता है कि यह कुछ ऐसा हो सकता है collections.namedtuple, लेकिन यह एक जमे हुए-कुंजी की तरह है (एक आधा जमे हुए तानाशाह)। है ना?

ए 'frozendict "एक जमे हुए शब्दकोश होना चाहिए, यह होना चाहिए keys, values, get, आदि, और समर्थन in, forआदि

अद्यतन:
* वहाँ यह है: https://www.python.org/dev/peps/pep-0603

जवाबों:


120

पायथन में एक बिलियन फ्रोजेंडिक प्रकार नहीं है। यह पता चला है कि यह बहुत बार उपयोगी नहीं होगा (हालांकि यह अभी भी शायद अधिक बार उपयोगी होगा frozenset)।

इस तरह के प्रकार के लिए सबसे आम कारण यह है कि जब मेमोएजिंग फ़ंक्शन अज्ञात तर्कों के साथ फ़ंक्शन के लिए कहता है। एक हैश के बराबर (जहां मानों को धोने योग्य होते हैं) के समान स्टोर करने के लिए सबसे आम समाधान कुछ ऐसा है tuple(sorted(kwargs.iteritems()))

यह छँटाई पर नहीं थोड़ा पागल होने पर निर्भर करता है। पायथन सकारात्मक वादा नहीं कर सकता छँटाई का परिणाम यहाँ कुछ उचित होगा। (लेकिन यह बहुत अधिक वादा नहीं कर सकता है, इसलिए इसे बहुत पसीना मत करो।)


आप आसानी से पर्याप्त प्रकार के रैपर बना सकते हैं जो एक तानाशाह की तरह काम करता है। यह कुछ इस तरह लग सकता है

import collections

class FrozenDict(collections.Mapping):
    """Don't forget the docstrings!!"""

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)
        self._hash = None

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

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

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

    def __hash__(self):
        # It would have been simpler and maybe more obvious to 
        # use hash(tuple(sorted(self._d.iteritems()))) from this discussion
        # so far, but this solution is O(n). I don't know what kind of 
        # n we are going to run into, but sometimes it's hard to resist the 
        # urge to optimize when it will gain improved algorithmic performance.
        if self._hash is None:
            hash_ = 0
            for pair in self.items():
                hash_ ^= hash(pair)
            self._hash = hash_
        return self._hash

यह बहुत अच्छा काम करना चाहिए:

>>> x = FrozenDict(a=1, b=2)
>>> y = FrozenDict(a=1, b=2)
>>> x is y
False
>>> x == y
True
>>> x == {'a': 1, 'b': 2}
True
>>> d = {x: 'foo'}
>>> d[y]
'foo'

7
मुझे नहीं पता कि इस तरह की चीज़ से लोगों को किस स्तर की सुरक्षा की चिंता है, लेकिन इस संबंध में आपके __hash__तरीके में थोड़ा सुधार हो सकता है। बस हैश की गणना करते समय एक अस्थायी चर का उपयोग करें, और self._hashअंतिम मूल्य होने पर ही सेट करें । इस तरह एक और धागा एक हैश हो रहा है जबकि पहली गणना कर रहा है बस एक गलत मूल्य प्राप्त करने के बजाय अनावश्यक गणना करेगा।
जेफ DQ

22
@Jeff एक नियम के रूप में, हर जगह सभी कोड थ्रेड-सुरक्षित नहीं है, और आपको उस कोड को सुरक्षित रूप से उपयोग करने के लिए इसे कुछ सिंक्रनाइज़ेशन संरचनाओं के चारों ओर लपेटना चाहिए। साथ ही, थ्रेड सुरक्षा की आपकी विशेष धारणा ऑब्जेक्ट विशेषता असाइनमेंट की परमाणुता पर निर्भर करती है, जो कि गारंटी से दूर है।
डेविन जीनपिएरे

9
@Anentropic, यह बिल्कुल सच नहीं है।
माइक ग्राहम

17
सावधान रहें: यह "फ्रोजनडिक्ट" आवश्यक रूप से जमे हुए नहीं है। एक मूल्य के रूप में एक परिवर्तनशील सूची डालने से आपको रोकने के लिए कुछ भी नहीं है, जिस स्थिति में हैशिंग एक त्रुटि फेंक देंगे। इसमें कुछ भी गलत नहीं है, लेकिन उपयोगकर्ताओं को जागरूक होना चाहिए। एक और बात: यह हैशिंग एल्गोरिथ्म खराब तरीके से चुना गया है, हैश टक्करों के लिए बहुत खतरा है। उदाहरण के लिए {'a': 'b'} हैश के समान {'b': 'a'} और {'a': 1, 'b': 2} हैश के समान {'a': 2, '' बी ': 1}। बेहतर विकल्प स्वयं होगा ।_शश ^ = हैश ((कुंजी, मूल्य))
स्टीव बायरन

6
यदि आप एक अपरिवर्तनीय वस्तु में एक परिवर्तनशील प्रविष्टि जोड़ते हैं, तो दो संभावित व्यवहार ऑब्जेक्ट बनाने में त्रुटि को फेंकने के लिए हैं, या ऑब्जेक्ट को हैशिंग पर एक त्रुटि फेंकने के लिए। टुपल्स बाद में करते हैं, फ्रेज़ेनसेट पूर्व करते हैं। मुझे निश्चित रूप से लगता है कि आपने बाद के दृष्टिकोण को लेने के लिए एक अच्छा निर्णय लिया, सभी बातों पर विचार किया। फिर भी, मुझे लगता है कि लोग देख सकते हैं कि फ्रोजनडिक्ट और फ्रोज़ेन्सेट के समान नाम हैं, और एक निष्कर्ष पर पहुंचे कि उन्हें भी इसी तरह का व्यवहार करना चाहिए। इसलिए मुझे लगता है कि यह इस अंतर के बारे में लोगों को चेतावनी देने के लायक है। :-)
स्टीव बायरेंस 19

63

उत्सुकता से, हालांकि हमारे पास frozensetअजगर में उपयोगी है, फिर भी कोई जमे हुए मानचित्रण नहीं है। पीईपी 416 में इस विचार को खारिज कर दिया गया - एक फ्रेज़ेंडिक्ट बिलिन प्रकार जोड़ें । पायथन 3.9 में विचार फिर से हो सकता है, PEP 603 देखें - संग्रह में एक जमे हुए प्रकार को जोड़ना

तो इस के लिए अजगर 2 समाधान:

def foo(config={'a': 1}):
    ...

अभी भी कुछ हद तक लंगड़ा लगता है:

def foo(config=None):
    if config is None:
        config = default_config = {'a': 1}
    ...

Python3 में आप का विकल्प होता है यह :

from types import MappingProxyType

default_config = {'a': 1}
DEFAULTS = MappingProxyType(default_config)

def foo(config=DEFAULTS):
    ...

अब डिफ़ॉल्ट कॉन्फ़िगरेशन को गतिशील रूप से अपडेट किया जा सकता है, लेकिन अपरिवर्तनीय रहें जहां आप चाहते हैं कि इसके बजाय प्रॉक्सी के आसपास से गुजरकर अपरिवर्तनीय होना चाहिए।

इसलिए इच्छानुसार परिवर्तन अपेक्षित रूप default_configसे अपडेट हो जाएगा DEFAULTS, लेकिन आप स्वयं मैपिंग प्रॉक्सी ऑब्जेक्ट को नहीं लिख सकते।

माना जाता है कि यह एक "अपरिवर्तनीय, धोने योग्य तानाशाही" के रूप में एक ही बात नहीं है - लेकिन यह एक सभ्य विकल्प है जिसे उसी तरह के उपयोग के मामलों को दिया जाता है जिसके लिए हम एक फ्रिज़ेंडिक्ट चाहते हैं।


2
क्या मॉड्यूल चर में प्रॉक्सी को संग्रहीत करने का कोई विशेष कारण है? सिर्फ क्यों नहीं def foo(config=MappingProxyType({'a': 1})):? आपका उदाहरण अभी भी के माध्यम से वैश्विक संशोधन की अनुमति देता default_configहै।
jpmc26

इसके अलावा, मुझे संदेह है कि डबल असाइनमेंट config = default_config = {'a': 1}एक टाइपो है।
jpmc26

21

शब्दकोश की कुंजियों और मूल्यों को मानते हुए स्वयं अपरिवर्तनीय हैं (उदाहरण के लिए तार):

>>> d
{'forever': 'atones', 'minks': 'cards', 'overhands': 'warranted', 
 'hardhearted': 'tartly', 'gradations': 'snorkeled'}
>>> t = tuple((k, d[k]) for k in sorted(d.keys()))
>>> hash(t)
1524953596

यह एक तानाशाही का एक अच्छा, विहित, अपरिवर्तनीय प्रतिनिधित्व है (बैरिंग पागल तुलनात्मक व्यवहार को गड़बड़ कर रहा है)।
माइक ग्राहम

6
@ देविन: पूर्ण रूप से सहमत है, लेकिन मैं अपने पोस्ट को एक उदाहरण के रूप में खड़ा होने दूंगा जो अक्सर एक बेहतर तरीका होता है।
msw

14
इससे भी बेहतर यह होगा कि इसे एक फ्रेज़ेनसेट में रखा जाए, जिसके लिए चाबियों या मूल्यों की आवश्यकता नहीं होती है ताकि एक सुसंगत क्रम निर्धारित हो।
asmeurer

7
इसके साथ केवल एक समस्या है: अब आपके पास मैपिंग नहीं है। यह पहली जगह में जमे हुए तानाशाह होने की पूरी बात होगी।
मैड फिजिसिस्ट

2
यह विधि वास्तव में अच्छा है जब एक तानाशाह को वापस जाना। सीधे dict(t)
शब्दों में

12

कोई नहीं है fronzedict, लेकिन आप उपयोग कर सकते हैं MappingProxyTypeकि पायथन 3.3 के साथ मानक पुस्तकालय में जोड़ा गया था:

>>> from types import MappingProxyType
>>> foo = MappingProxyType({'a': 1})
>>> foo
mappingproxy({'a': 1})
>>> foo['a'] = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> foo
mappingproxy({'a': 1})

कैविएट के साथ:TypeError: can't pickle mappingproxy objects
रादु

मुझे इसका विचार पसंद है। मैं इसके लिए एक कोशिश करने जा रहा हूं।
डग

10

यहां वह कोड है जिसका मैं उपयोग कर रहा हूं। मैंने फ्रोज़ेन्सेट को उपवर्गित किया। इसके फायदे निम्नलिखित हैं।

  1. यह वास्तव में अपरिवर्तनीय वस्तु है। भविष्य के उपयोगकर्ताओं और डेवलपर्स के अच्छे व्यवहार पर कोई भरोसा नहीं करता है।
  2. एक नियमित शब्दकोश और जमे हुए शब्दकोश के बीच आगे और पीछे बदलना आसान है। जमे हुए निर्णय (मूल_चित्र) -> जमे हुए शब्दकोश। तानाशाही (जमे हुए_ निर्णय) -> नियमित रूप से तानाशाही।

अपडेट 21 जनवरी 2015: मेरे द्वारा 2014 में पोस्ट किए गए कोड का मूल टुकड़ा एक कुंजी को खोजने के लिए फॉर-लूप का उपयोग करता था। वह अविश्वसनीय रूप से धीमा था। अब मैंने एक कार्यान्वयन लागू किया है जो फ्रोज़ेनसेट की हैशिंग विशेषताओं का लाभ उठाता है। कुंजी-मूल्य जोड़े विशेष कंटेनरों में संग्रहीत किए जाते हैं जहां __hash__और__eq__ फ़ंक्शन केवल कुंजी पर आधारित होते हैं। इस कोड को औपचारिक रूप से इकाई-परीक्षण भी किया गया है, इसके विपरीत जो मैंने अगस्त 2014 में यहां पोस्ट किया था।

एमआईटी-शैली लाइसेंस।

if 3 / 2 == 1:
    version = 2
elif 3 / 2 == 1.5:
    version = 3

def col(i):
    ''' For binding named attributes to spots inside subclasses of tuple.'''
    g = tuple.__getitem__
    @property
    def _col(self):
        return g(self,i)
    return _col

class Item(tuple):
    ''' Designed for storing key-value pairs inside
        a FrozenDict, which itself is a subclass of frozenset.
        The __hash__ is overloaded to return the hash of only the key.
        __eq__ is overloaded so that normally it only checks whether the Item's
        key is equal to the other object, HOWEVER, if the other object itself
        is an instance of Item, it checks BOTH the key and value for equality.

        WARNING: Do not use this class for any purpose other than to contain
        key value pairs inside FrozenDict!!!!

        The __eq__ operator is overloaded in such a way that it violates a
        fundamental property of mathematics. That property, which says that
        a == b and b == c implies a == c, does not hold for this object.
        Here's a demonstration:
            [in]  >>> x = Item(('a',4))
            [in]  >>> y = Item(('a',5))
            [in]  >>> hash('a')
            [out] >>> 194817700
            [in]  >>> hash(x)
            [out] >>> 194817700
            [in]  >>> hash(y)
            [out] >>> 194817700
            [in]  >>> 'a' == x
            [out] >>> True
            [in]  >>> 'a' == y
            [out] >>> True
            [in]  >>> x == y
            [out] >>> False
    '''

    __slots__ = ()
    key, value = col(0), col(1)
    def __hash__(self):
        return hash(self.key)
    def __eq__(self, other):
        if isinstance(other, Item):
            return tuple.__eq__(self, other)
        return self.key == other
    def __ne__(self, other):
        return not self.__eq__(other)
    def __str__(self):
        return '%r: %r' % self
    def __repr__(self):
        return 'Item((%r, %r))' % self

class FrozenDict(frozenset):
    ''' Behaves in most ways like a regular dictionary, except that it's immutable.
        It differs from other implementations because it doesn't subclass "dict".
        Instead it subclasses "frozenset" which guarantees immutability.
        FrozenDict instances are created with the same arguments used to initialize
        regular dictionaries, and has all the same methods.
            [in]  >>> f = FrozenDict(x=3,y=4,z=5)
            [in]  >>> f['x']
            [out] >>> 3
            [in]  >>> f['a'] = 0
            [out] >>> TypeError: 'FrozenDict' object does not support item assignment

        FrozenDict can accept un-hashable values, but FrozenDict is only hashable if its values are hashable.
            [in]  >>> f = FrozenDict(x=3,y=4,z=5)
            [in]  >>> hash(f)
            [out] >>> 646626455
            [in]  >>> g = FrozenDict(x=3,y=4,z=[])
            [in]  >>> hash(g)
            [out] >>> TypeError: unhashable type: 'list'

        FrozenDict interacts with dictionary objects as though it were a dict itself.
            [in]  >>> original = dict(x=3,y=4,z=5)
            [in]  >>> frozen = FrozenDict(x=3,y=4,z=5)
            [in]  >>> original == frozen
            [out] >>> True

        FrozenDict supports bi-directional conversions with regular dictionaries.
            [in]  >>> original = {'x': 3, 'y': 4, 'z': 5}
            [in]  >>> FrozenDict(original)
            [out] >>> FrozenDict({'x': 3, 'y': 4, 'z': 5})
            [in]  >>> dict(FrozenDict(original))
            [out] >>> {'x': 3, 'y': 4, 'z': 5}   '''

    __slots__ = ()
    def __new__(cls, orig={}, **kw):
        if kw:
            d = dict(orig, **kw)
            items = map(Item, d.items())
        else:
            try:
                items = map(Item, orig.items())
            except AttributeError:
                items = map(Item, orig)
        return frozenset.__new__(cls, items)

    def __repr__(self):
        cls = self.__class__.__name__
        items = frozenset.__iter__(self)
        _repr = ', '.join(map(str,items))
        return '%s({%s})' % (cls, _repr)

    def __getitem__(self, key):
        if key not in self:
            raise KeyError(key)
        diff = self.difference
        item = diff(diff({key}))
        key, value = set(item).pop()
        return value

    def get(self, key, default=None):
        if key not in self:
            return default
        return self[key]

    def __iter__(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.key, items)

    def keys(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.key, items)

    def values(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.value, items)

    def items(self):
        items = frozenset.__iter__(self)
        return map(tuple, items)

    def copy(self):
        cls = self.__class__
        items = frozenset.copy(self)
        dupl = frozenset.__new__(cls, items)
        return dupl

    @classmethod
    def fromkeys(cls, keys, value):
        d = dict.fromkeys(keys,value)
        return cls(d)

    def __hash__(self):
        kv = tuple.__hash__
        items = frozenset.__iter__(self)
        return hash(frozenset(map(kv, items)))

    def __eq__(self, other):
        if not isinstance(other, FrozenDict):
            try:
                other = FrozenDict(other)
            except Exception:
                return False
        return frozenset.__eq__(self, other)

    def __ne__(self, other):
        return not self.__eq__(other)


if version == 2:
    #Here are the Python2 modifications
    class Python2(FrozenDict):
        def __iter__(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.key

        def iterkeys(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.key

        def itervalues(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.value

        def iteritems(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield (i.key, i.value)

        def has_key(self, key):
            return key in self

        def viewkeys(self):
            return dict(self).viewkeys()

        def viewvalues(self):
            return dict(self).viewvalues()

        def viewitems(self):
            return dict(self).viewitems()

    #If this is Python2, rebuild the class
    #from scratch rather than use a subclass
    py3 = FrozenDict.__dict__
    py3 = {k: py3[k] for k in py3}
    py2 = {}
    py2.update(py3)
    dct = Python2.__dict__
    py2.update({k: dct[k] for k in dct})

    FrozenDict = type('FrozenDict', (frozenset,), py2)

1
ध्यान दें कि आपने इसे यहाँ पर पोस्ट करके CC BY-SA 3.0 के तहत लाइसेंस प्राप्त किया है। कम से कम यह प्रचलित दृश्य है । जब आप पहली बार साइन अप करते हैं तो मुझे लगता है कि कुछ टीएंडसी के लिए सहमत होने का कानूनी आधार है।
इवगेनी सर्गेव

1
मैंने बिना किसी तानाशाही के प्रमुख हैश को देखने के तरीके के बारे में सोचने की कोशिश में अपना दिमाग तोड़ दिया। के हैश पुनर्परिभाषित Itemहोने के लिए कुंजी के हैश एक साफ हैक है!
क्लैक

दुर्भाग्य से, diff(diff({key}))फ्रोजनडिक्ट के आकार का रन टाइम अभी भी रैखिक है, जबकि नियमित रूप से औसत पहुंच का समय औसत मामले में स्थिर है।
डेनिस

6

मुझे लगता है कि मैं इस तरह से एक समारोह लिखने के लिए हर बार सोचता हूं:

def do_something(blah, optional_dict_parm=None):
    if optional_dict_parm is None:
        optional_dict_parm = {}

6
हर बार जब मैं इस तरह की टिप्पणी देखता हूं तो मुझे यकीन होता है कि मैंने कहीं गड़बड़ कर दी और {} डिफ़ॉल्ट के रूप में डाल दिया, और वापस जाओ और मेरे हाल ही में लिखे गए कोड को देखो।
रयान हाईबर्ट

1
हाँ, यह एक बुरा गोटा है जिसे हर कोई जल्दी या बाद में चलाता है।
मार्क विज़सर

8
आसान सूत्रीकरण:optional_dict_parm = optional_dict_parm or {}
इमैनुएल

2
इस स्थिति में आप तर्क के लिए डिफ़ॉल्ट मान के रूप में उपयोग कर सकते हैं । types.MappingProxyType({})
जिंजरप्लस प्लस

@GingerPlusPlus आप एक जवाब के रूप में लिख सकते हैं?
जॉन्सर्शपे

5

आप पैकेज frozendictसे उपयोग कर सकते हैं utilspie:

>>> from utilspie.collectionsutils import frozendict

>>> my_dict = frozendict({1: 3, 4: 5})
>>> my_dict  # object of `frozendict` type
frozendict({1: 3, 4: 5})

# Hashable
>>> {my_dict: 4}
{frozendict({1: 3, 4: 5}): 4}

# Immutable
>>> my_dict[1] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mquadri/workspace/utilspie/utilspie/collectionsutils/collections_utils.py", line 44, in __setitem__
    self.__setitem__.__name__, type(self).__name__))
AttributeError: You can not call '__setitem__()' for 'frozendict' object

दस्तावेज़ के अनुसार :

फ्रेज़ेंडिक्ट (तानाशाही) : तानाशाही के प्रकार को स्वीकार करता है और एक स्पष्ट और अपरिवर्तनीय ताना देता है



3

हां, यह मेरा दूसरा जवाब है, लेकिन यह पूरी तरह से अलग तरीका है। पहला कार्यान्वयन शुद्ध अजगर में था। यह एक साइथन में है। यदि आप जानते हैं कि साइथन मॉड्यूल का उपयोग और संकलन कैसे किया जाता है, तो यह एक नियमित शब्दकोश के रूप में तेज़ है। एकल मान प्राप्त करने के लिए मोटे तौर पर .04 से .06 माइक्रो-सेकंड।

यह फ़ाइल "जमे हुए_डिक्स्ट्रोक्स" है

import cython
from collections import Mapping

cdef class dict_wrapper:
    cdef object d
    cdef int h

    def __init__(self, *args, **kw):
        self.d = dict(*args, **kw)
        self.h = -1

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

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

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

    def __hash__(self):
        if self.h == -1:
            self.h = hash(frozenset(self.d.iteritems()))
        return self.h

class FrozenDict(dict_wrapper, Mapping):
    def __repr__(self):
        c = type(self).__name__
        r = ', '.join('%r: %r' % (k,self[k]) for k in self)
        return '%s({%s})' % (c, r)

__all__ = ['FrozenDict']

यहाँ फ़ाइल "setup.py" है

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize('frozen_dict.pyx')
)

यदि आपके पास साइथन स्थापित है, तो ऊपर दी गई दो फाइलों को एक ही डायरेक्टरी में सहेजें। कमांड लाइन में उस डायरेक्टरी में जाएँ।

python setup.py build_ext --inplace
python setup.py install

और आपको किया जाना चाहिए।


3

का मुख्य नुकसान है namedtuple यह है कि इसका उपयोग करने से पहले इसे निर्दिष्ट करने की आवश्यकता होती है, इसलिए यह एकल-उपयोग के मामलों के लिए कम सुविधाजनक है।

हालाँकि, एक व्यावहारिक समाधान है जिसका उपयोग ऐसे कई मामलों को संभालने के लिए किया जा सकता है। मान लें कि आप निम्न श्रुत के एक अपरिवर्तनीय समकक्ष चाहते हैं:

MY_CONSTANT = {
    'something': 123,
    'something_else': 456
}

इसे इस तरह अनुकरण किया जा सकता है:

from collections import namedtuple

MY_CONSTANT = namedtuple('MyConstant', 'something something_else')(123, 456)

इसे स्वचालित करने के लिए एक सहायक फ़ंक्शन लिखना भी संभव है:

def freeze_dict(data):
    from collections import namedtuple
    keys = sorted(data.keys())
    frozen_type = namedtuple(''.join(keys), keys)
    return frozen_type(**data)

a = {'foo':'bar', 'x':'y'}
fa = freeze_dict(data)
assert a['foo'] == fa.foo

बेशक यह केवल फ्लैट डाइक के लिए काम करता है, लेकिन एक पुनरावर्ती संस्करण को लागू करना बहुत मुश्किल नहीं होना चाहिए।


1
अन्य टपल जवाब के साथ भी यही समस्या है: आपको getattr(fa, x)इसके बजाय करना है fa[x], keysअपनी उंगलियों पर कोई तरीका नहीं है , और अन्य सभी कारणों से मानचित्रण वांछनीय हो सकता है।
मैड फिजिसिस्ट

1

उपवर्गीकरण dict

मैं इस पैटर्न को जंगली (गीथूब) में देखता हूं और इसका उल्लेख करना चाहता हूं:

class FrozenDict(dict):
    def __init__(self, *args, **kwargs):
        self._hash = None
        super(FrozenDict, self).__init__(*args, **kwargs)

    def __hash__(self):
        if self._hash is None:
            self._hash = hash(tuple(sorted(self.items())))  # iteritems() on py2
        return self._hash

    def _immutable(self, *args, **kws):
        raise TypeError('cannot change object - object is immutable')

    __setitem__ = _immutable
    __delitem__ = _immutable
    pop = _immutable
    popitem = _immutable
    clear = _immutable
    update = _immutable
    setdefault = _immutable

उदाहरण का उपयोग:

d1 = FrozenDict({'a': 1, 'b': 2})
d2 = FrozenDict({'a': 1, 'b': 2})
d1.keys() 
assert isinstance(d1, dict)
assert len(set([d1, d2])) == 1  # hashable

पेशेवरों

  • के लिए समर्थन get(), keys(), items()( iteritems()py2 पर) और से सभी उपहार dictबॉक्स से बाहर स्पष्ट रूप से उन्हें लागू करने के बिना
  • आंतरिक रूप से उपयोग करता है dictजिसका अर्थ है प्रदर्शन ( dictसीपीथॉन में सी में लिखा गया है)
  • सुरुचिपूर्ण सरल और कोई काला जादू नहीं
  • isinstance(my_frozen_dict, dict)रिटर्न ट्रू - हालाँकि अजगर कई पैकेजों के उपयोग के लिए डक-टाइपिंग को प्रोत्साहित करता है isinstance(), इससे कई ट्वीक्स और कस्टमाइज़ेशन को बचाया जा सकता है

विपक्ष

  • कोई भी उपवर्ग इसे ओवरराइड कर सकता है या इसे आंतरिक रूप से एक्सेस कर सकता है (आप वास्तव में 100% किसी चीज की रक्षा कर सकते हैं, आपको अपने उपयोगकर्ताओं पर भरोसा करना चाहिए और अच्छा प्रलेखन प्रदान करना चाहिए)।
  • यदि आप गति की परवाह करते हैं, तो आप __hash__थोड़ा तेज करना चाहते हैं ।

मैंने एक और धागे में गति की तुलना की और यह कई विकल्पों की तुलना में अधिक तेजी से निकला __setitem__और विरासत में मिला dictहै।
टॉर्केड


0

मुझे कुछ के लिए एक बिंदु पर कुछ के लिए निश्चित कुंजियों तक पहुंचने की आवश्यकता थी जो कि विश्व स्तर पर एक तरह की चीज थी और मैं इसके लिए कुछ पर बस गया:

class MyFrozenDict:
    def __getitem__(self, key):
        if key == 'mykey1':
            return 0
        if key == 'mykey2':
            return "another value"
        raise KeyError(key)

इसका उपयोग करें

a = MyFrozenDict()
print(a['mykey1'])

चेतावनी: मैं अधिकांश उपयोग के मामलों के लिए इसकी अनुशंसा नहीं करता क्योंकि यह कुछ बहुत ही गंभीर व्यापार बनाता है।


प्रदर्शन स्कार्फिसेस के बिना निम्नलिखित शक्ति में बराबर होगा। हालाँकि, यह स्वीकृत उत्तर का केवल एक सरलीकरण है ... `` वर्ग फ्रोजनडिक्ट: डीई __init __ (स्व, डेटा): self._data = डेटा डीप __getitem __ (स्वयं, कुंजी): वापसी self_data [कुंजी] `` `
युवल

@ यवु क यह उत्तर समतुल नहीं है। शुरुआत के लिए एपी अलग है क्योंकि इसमें डेटा इनिट की जरूरत है। इसका मतलब यह भी है कि यह अब विश्व-सुलभ नहीं है। इसके अलावा, अगर _data म्यूट किया गया है, तो आपका रिटर्न वैल्यू बदल जाता है। मुझे पता है कि महत्वपूर्ण ट्रेडऑफ हैं - जैसे मैंने कहा, मैं ज्यादातर उपयोग के मामलों के लिए इसकी सिफारिश नहीं करता हूं।
Adverbly

-1

मूल भाषा समर्थन की अनुपस्थिति में, आप या तो इसे स्वयं कर सकते हैं या मौजूदा समाधान का उपयोग कर सकते हैं। सौभाग्य से पायथन अपने आधार कार्यान्वयन के विस्तार को आसान बनाता है।

class frozen_dict(dict):
    def __setitem__(self, key, value):
        raise Exception('Frozen dictionaries cannot be mutated')

frozen_dict = frozen_dict({'foo': 'FOO' })
print(frozen['foo']) # FOO
frozen['foo'] = 'NEWFOO' # Exception: Frozen dictionaries cannot be mutated

# OR

from types import MappingProxyType

frozen_dict = MappingProxyType({'foo': 'FOO'})
print(frozen_dict['foo']) # FOO
frozen_dict['foo'] = 'NEWFOO' # TypeError: 'mappingproxy' object does not support item assignment

आपका जमे हुए_ डिक्टिक्ट वर्ग हैजेबल नहीं है
चमत्कार 173
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.