"হিমশীতল ডিক্ট" কী হবে?


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আপনার চূড়ান্ত মান হওয়ার পরে কেবল সেট করুন । প্রথম গণনা করার সময় অন্য একটি থ্রেড একটি হ্যাশ পেয়ে একটি ভুল মান পাওয়ার চেয়ে কেবল অনর্থক গণনা করবে।
জেফ ডিকিউ

22
@ জেফ একটি নিয়ম হিসাবে, সর্বত্র সমস্ত কোড থ্রেড-নিরাপদ নয় এবং সেই কোডটি নিরাপদে ব্যবহার করার জন্য আপনার এটি কয়েকটি সিঙ্ক্রোনাইজেশন কাঠামোর চারপাশে মোড়ানো উচিত। এছাড়াও, থ্রেড সুরক্ষার জন্য আপনার বিশেষ ধারণাটি অবজেক্ট অ্যাট্রিবিউট অ্যাসাইনমেন্টের পারমাণবিকতার উপর নির্ভর করে, যা গ্যারান্টিযুক্ত far
ডেভিন জিনপিয়ের

9
@ অ্যান্ট্রোপিক, এটি মোটেও সত্য নয়।
মাইক গ্রাহাম

17
সতর্কতা অবলম্বন করুন: এই "ফ্রোজেনডিক্ট" অগত্যা হিমায়িত নয়। মান হিসাবে একটি পরিবর্তনীয় তালিকা স্থাপন থেকে আপনাকে বাধা দেওয়ার কিছুই নেই, সেক্ষেত্রে হ্যাশিং একটি ত্রুটি ফেলবে। এটিতে অগত্যা কোনও ভুল নেই, তবে ব্যবহারকারীদের সচেতন হওয়া উচিত। আরেকটি জিনিস: এই হ্যাশিং অ্যালগরিদমটি খুব ভালভাবে বেছে নেওয়া হয়েছে, হ্যাশের সংঘর্ষের খুব ঝুঁকিপূর্ণ। উদাহরণস্বরূপ {'এ': 'বি'} হ্যাশগুলি {'বি': হ'ল হ্যাশ করে: 'এ'} এবং {'এ': 1, 'বি': 2} হ্যাশ a 'এ': 2, 'এর মতো b ': 1}। ভাল পছন্দ হবে স্ব। Be হ্যাশ sh = হ্যাশ ((কী, মান))
স্টিভ বায়ার্নস

6
যদি আপনি কোনও পরিবর্তনযোগ্য অবজেক্টে একটি পরিবর্তনীয় এন্ট্রি যুক্ত করেন তবে দুটি সম্ভাব্য আচরণ হ'ল অবজেক্টটি তৈরি করার সময় একটি ত্রুটি ছুঁড়ে ফেলা বা অবধি হ্যাশ করার সময় একটি ত্রুটি নিক্ষেপ করা। টুপলস পরবর্তীকালে করে, হিমশীতল প্রাক্তনটি করে। আমি স্পষ্টতই মনে করি আপনি পরবর্তী বিষয়গুলি গ্রহণ করার জন্য একটি ভাল সিদ্ধান্ত নিয়েছিলেন, সমস্ত বিষয় বিবেচিত। তবুও, আমি মনে করি যে লোকেরা দেখতে পাবে যে ফ্রোজেনডিক্ট এবং হিমশীতলগুলির একই নাম রয়েছে এবং তারা এই সিদ্ধান্তে উপনীত হতে পারে যে তাদের একইরকম আচরণ করা উচিত। সুতরাং আমি এই পার্থক্য সম্পর্কে মানুষকে সতর্ক করা মূল্যবান বলে মনে করি। :-)
স্টিভ বায়ার্নস

62

কৌতূহলজনকভাবে, যদিও অজগরটিতে আমাদের খুব কমই উপকারী frozenset, তবে এখনও কোনও হিমশীতল ম্যাপিং নেই। ধারণাটি পিইপি 416 এ প্রত্যাখ্যান করা হয়েছিল - একটি হিমায়িত বিল্টিন টাইপ যুক্ত করুন । পাইথন ৩.৯-এ এই ধারণাটি পুনর্বিবেচনা করা যেতে পারে, পিইপি 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):
    ...

এখন ডিফল্ট কনফিগারেশনটি গতিশীলভাবে আপডেট করা যেতে পারে, তবে পরিবর্তে প্রক্সিটি অতিক্রম করে আপনি যেখানে অপরিবর্তনীয় থাকতে চান তা স্থির রাখুন remain

সুতরাং প্রত্যাশার হিসাবে পরিবর্তনগুলি 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
@ দেবিন: পুরোপুরি সম্মত, তবে আমি আমার পোস্টটিকে উদাহরণ হিসাবে দাঁড়াতে দেব যে প্রায়শই আরও ভাল উপায় আছে।
এমএসডব্লিউ

14
আরও ভাল হ'ল এটি হিমায়িত মধ্যে রাখা, যা কী বা মানগুলির একটি সামঞ্জস্যপূর্ণ ক্রম সংজ্ঞায়িত করার প্রয়োজন হয় না।
asmeurer

7
এটির সাথে কেবল একটি সমস্যা: আপনার আর ম্যাপিং নেই। হিমশীতল ডিকটি প্রথম স্থানে রাখার পুরো বিষয়টি হবে।
ম্যাড পদার্থবিদ 13

2
ডিক্টে ফিরে যাওয়ার সময় এই পদ্ধতিটি সত্যিই দুর্দান্ত। কেবলdict(t)
কোডিথেক্ডার

12

নেই fronzedict, তবে আপনি MappingProxyTypeপাইথন ৩.৩ সহ স্ট্যান্ডার্ড লাইব্রেরিতে যুক্ত করেছিলেন:

>>> 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 এ এখানে পোস্ট করেছি unlike

এমআইটি-স্টাইলের লাইসেন্স

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
নোট করুন যে আপনি এটি এখানে পোস্ট করে সিসি বাই-এসএ 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
যতবারই আমি এর মতো মন্তব্য দেখি আমি নিশ্চিত যে আমি কোথাও বেঁকেছি এবং ডিফল্ট হিসাবে {put রেখেছি এবং ফিরে গিয়ে আমার সম্প্রতি লিখিত কোডটি দেখুন।
রায়ান হাইবার্ট

1
হ্যাঁ, এটি খুব বাজে গ্যাচা যা সবাই খুব তাড়াতাড়ি বা পরে চালিয়ে যায়।
দর্শকদের

8
আরও সহজ সূত্র:optional_dict_parm = optional_dict_parm or {}
ইমানুয়েল

2
এই ক্ষেত্রে আপনি যুক্তির জন্য ডিফল্ট মান হিসাবে ব্যবহার করতে পারেন । types.MappingProxyType({})
আদা প্লাসপ্লাস প্লাস

@ জিঞ্জারপ্লাসপ্লাস আপনি কি উত্তর হিসাবে এটি লিখতে পারেন?
jonrsharpe

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

Subclassing 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 __ (স্ব, ডেটা): স্ব। `
যুবাল

@ ইউভাল যে উত্তরটি সমান নয়। প্রারম্ভিকদের জন্য এপিআই পৃথক হয় কারণ এটি ইনডিকে ডেটা প্রয়োজন। এটি আরও বোঝায় যে এটি আর বিশ্বব্যাপী অ্যাক্সেসযোগ্য নয়। তদুপরি, _ডাটা যদি রূপান্তরিত হয় তবে আপনার ফেরতের মান পরিবর্তন হবে। আমি সচেতন যে এখানে উল্লেখযোগ্য ট্রেড অফস রয়েছে - যেমন আমি বলেছিলাম, বেশিরভাগ ব্যবহারের ক্ষেত্রে আমি এটি প্রস্তাব করি না।
ক্রমাগত 18

-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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.