কীভাবে একটি ডিককে "পুরোপুরি" ওভাররাইড করা যায়?


218

আমি কীভাবে "নিখুঁত" ডেকের একটি সাবক্লাসকে সম্ভব হিসাবে তৈরি করতে পারি ? শেষ লক্ষ্যটি হ'ল একটি সরল ডিক থাকে যাতে কীগুলি ছোট হাতের অক্ষরে থাকে।

দেখে মনে হবে যে এই কাজটি করার জন্য আমি কিছু ছোট ছোট আদিমাগুলি লিখতে পারি, তবে আমার সমস্ত গবেষণা এবং প্রচেষ্টা অনুসারে মনে হয় এটি এরকম নয়:

  • আমি যদি ওভাররাইড __getitem__/__setitem__ , তবে get/ setকাজ করি না। আমি কীভাবে তাদের কাজ করতে পারি? নিশ্চয়ই আমি তাদের আলাদাভাবে প্রয়োগ করার দরকার নেই?

  • আমি কি কাজ থেকে বাছাইয়ের প্রতিরোধ করছি এবং আমার কি এগুলি বাস্তবায়ন করা দরকার __setstate__?

  • আমার কি দরকার repr, updateএবং__init__ ?

  • আমি শুধু উচিত mutablemapping ব্যবহার (এটা মনে হয় এক ব্যবহার করা উচিত নয় UserDict বা DictMixin)? যদি তাই হয়, কিভাবে? ডক্সগুলি হুবহু আলোকিত নয় n't

এখানে এটি আমার প্রথম যেতে হবে, get()কাজ করে না এবং সন্দেহ আছে যে আরও অনেক ছোটখাটো সমস্যা রয়েছে:

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()

আমি মনে করি __keytransfor __ () স্থির হওয়া উচিত। ভাল পন্থা যদিও। (@ স্ট্যাটিকমেডোডিং প্রিপেন্ডিং)
আয়য়ন.প্রিম

উত্তর:


229

আপনি এমন একটি উপাদান লিখতে পারেন যা মডিউল থেকে এবিসি (অ্যাবস্ট্রাক্ট বেস ক্লাসেস) dictদিয়ে খুব সহজেই আচরণ করে । এমনকি এটি আপনাকে বলে যে আপনি কোনও পদ্ধতি মিস করেছেন কিনা, তাই নীচে নীচে ন্যূনতম সংস্করণ যা এবিসিটিকে বন্ধ করে দেয়।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

আপনি এবিসি থেকে কয়েকটি বিনামূল্যে পদ্ধতি পান:

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

আমি dictসরাসরি সাবক্লাস (বা অন্যান্য বিল্টিনগুলি) করতাম না। এটা প্রায়ই, কোন জ্ঞান করে তোলে কারণ আপনি আসলে কি করতে চান হয় একটি ইন্টারফেস বাস্তবায়নdict । আর এ বি সি-র জন্য ঠিক এটিই।


46
আমি নাম পরিবর্তন করার পরামর্শ দেব __keytransform__()কারণ এটি পিইপি 8 স্টাইল গাইডকে লঙ্ঘন করে যা বর্ণনামূলক: নামকরণ শৈলী বিভাগের শেষে "এই জাতীয় নাম কখনই আবিষ্কার করবেন না; কেবল তাদের ডকুমেন্টেড হিসাবে ব্যবহার করুন" পরামর্শ দেয় ।
মার্টিনো

1
যদিও প্রশ্ন - কোনও ব্যবহারকারী-সংজ্ঞায়িত প্রকারের সাথে এই ইন্টারফেসটি বাস্তবায়িত করার ফলে বিল্ট-ইন টাইপটি ব্যবহার করে ধীর ডিকের মতো ক্রিয়াকলাপ সাধিত হয় না?
twneale

2
এটি করার কোনও উপায় কি সেই বিচ্ছিন্নতা (_, ডিক) == সত্য? অথবা আপনি কেবল সাবক্লাস নির্মাণের জন্য মিউটেবল ম্যাপিং ব্যবহার করেন?
অ্যান্ডি হেডেন

5
@ অ্যান্ডি হেডেন: আপনার লেখা উচিত if isinstance(t, collections.MutableMapping): print t, "can be used like a dict"। কোনও বস্তুর প্রকারটি পরীক্ষা করে দেখুন না, ইন্টারফেসটি পরীক্ষা করুন।
জোচেন রিটজেল

2
@ নীলজি এটি দুর্ভাগ্যক্রমে পাইথন স্ট্যান্ডার্ড লাইব্রেরিতে জেএসওন এনকোডারকে অন্তর্ভুক্ত করেছে - github.com/python-git/python/blob/…
অ্যান্ডি স্মিথ

97

আমি কীভাবে "নিখুঁত" ডেকের একটি সাবক্লাসকে সম্ভব হিসাবে তৈরি করতে পারি?

শেষ লক্ষ্যটি হ'ল একটি সরল ডিক থাকে যাতে কীগুলি ছোট হাতের অক্ষরে থাকে।

  • আমি যদি ওভাররাইড __getitem__/ করি __setitem__, তবে / সেটটি কাজ করবে না। আমি কীভাবে তাদের কাজ করব? নিশ্চয়ই আমি তাদের আলাদাভাবে প্রয়োগ করার দরকার নেই?

  • আমি কি কাজ থেকে বাছাইয়ের প্রতিরোধ করছি এবং আমার কি এগুলি বাস্তবায়ন করা দরকার __setstate__?

  • আমার কি পুনরায় প্রকাশ, আপডেট এবং দরকার আছে __init__?

  • আমি শুধু ব্যবহার করা উচিত mutablemapping(এটা মনে হয় এক ব্যবহার করা উচিত নয় UserDict বা DictMixin)? যদি তাই হয়, কিভাবে? ডক্সগুলি হুবহু আলোকিত নয় n't

গৃহীত উত্তরটি আমার প্রথম পদ্ধতির হবে, তবে যেহেতু এটির কিছু সমস্যা রয়েছে এবং যেহেতু কেউ বিকল্পটির দিকে মনোনিবেশ করেনি, আসলে একটি উপশ্রেণীর কাজ dict, আমি এখানে এটি করতে যাচ্ছি।

গৃহীত উত্তরের সাথে কী ভুল?

এটি আমার কাছে একটি সহজ অনুরোধের মতো বলে মনে হচ্ছে:

আমি কীভাবে "নিখুঁত" ডেকের একটি সাবক্লাসকে সম্ভব হিসাবে তৈরি করতে পারি? শেষ লক্ষ্যটি হ'ল একটি সরল ডিক থাকে যাতে কীগুলি ছোট হাতের অক্ষরে থাকে।

গৃহীত উত্তর আসলে সাবক্লাস হয় না dictএবং এর জন্য একটি পরীক্ষা ব্যর্থ হয়:

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

আদর্শভাবে, যে কোনও টাইপ-চেকিং কোডটি আমাদের প্রত্যাশা করা ইন্টারফেস বা একটি বিমূর্ত বেস ক্লাসের জন্য পরীক্ষা করা হবে, তবে যদি আমাদের ডেটা অবজেক্টগুলি পরীক্ষা করা ফাংশনগুলিতে পাস করা হয় dict- এবং আমরা এই ফাংশনগুলিকে "ফিক্স" করতে পারি না, এই কোডটি অকৃতকার্য হবে.

অন্যান্য quibbles একটি তৈরি করতে পারে:

  • গৃহীত উত্তরটিও শ্রেণিবদ্ধ: অনুপস্থিত fromkeys
  • গৃহীত উত্তরের একটি অতিরিক্ত কাজও রয়েছে __dict__- সুতরাং স্মৃতিতে আরও স্থান নেওয়া:

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

আসলে সাবক্লাসিং dict

উত্তরাধিকারের মাধ্যমে আমরা ডিক পদ্ধতিগুলি পুনরায় ব্যবহার করতে পারি। আমাদের কেবল একটি ইন্টারফেস স্তর তৈরি করতে হবে যা নিশ্চিত করে যে কীগুলি যদি স্ট্রিং থাকে তবে লোকেস আকারে ডিকের মধ্যে চলে যায়।

আমি যদি ওভাররাইড __getitem__/ করি __setitem__, তবে / সেটটি কাজ করবে না। আমি কীভাবে তাদের কাজ করব? নিশ্চয়ই আমি তাদের আলাদাভাবে প্রয়োগ করার দরকার নেই?

ঠিক আছে, এগুলির প্রতিটি পৃথকভাবে বাস্তবায়ন হ'ল এই পদ্ধতির নেতিবাচক দিক এবং ব্যবহারের MutableMappingগ্রহণযোগ্যতা (স্বীকৃত উত্তর দেখুন) তবে এটি এত বেশি কাজ নয়।

প্রথমে পাইথন 2 এবং 3 এর মধ্যে পার্থক্যটি চিহ্নিত করা যাক, _RaiseKeyErrorআমরা আসলে একটি যুক্তি পাই কিনা তা নিশ্চিত করার জন্য একটি সিঙ্গলটন ( ) dict.popতৈরি করুন এবং আমাদের স্ট্রিং কীগুলি ছোট হাতের অক্ষর কিনা তা নিশ্চিত করার জন্য একটি ফাংশন তৈরি করুন:

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

এখন আমরা বাস্তবায়ন করি - আমি superসম্পূর্ণ যুক্তি দিয়ে ব্যবহার করছি যাতে এই কোডটি পাইথন 2 এবং 3 এর জন্য কাজ করে:

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__())

আমরা কোনো পদ্ধতি বা বিশেষ পদ্ধতির জন্য একটি প্রায় বয়লার-প্লেট পদ্ধতির ব্যবহার রেফারেন্স একটি কী, কিন্তু অন্যথায়, উত্তরাধিকার দ্বারা, আমরা পেতে পদ্ধতি: len, clear, items, keys, popitem, এবং valuesবিনামূল্যে জন্য। যদিও এটি সঠিক হওয়ার জন্য কিছু সতর্ক চিন্তাভাবনা দরকার, তবুও এটি কাজ করা তুচ্ছ বিষয়।

(দ্রষ্টব্য যে haskeyপাইথন 2 এ অবহেলা করা হয়েছিল, পাইথন 3 এ সরানো হয়েছে))

এখানে কিছু ব্যবহার রয়েছে:

>>> 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)

আমি কি কাজ থেকে বাছাইয়ের প্রতিরোধ করছি এবং আমার কি এগুলি বাস্তবায়ন করা দরকার __setstate__?

pickling

এবং ডিক সাবক্লাস আচার ঠিক সূক্ষ্ম:

>>> 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__

আমার কি পুনরায় প্রকাশ, আপডেট এবং দরকার আছে __init__?

আমরা সংজ্ঞায়িত করেছি updateএবং __init__তবে আপনার __repr__ডিফল্টরূপে একটি সুন্দর রয়েছে :

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

তবে __repr__আপনার কোডটির ডিবাগযোগ্যতা উন্নত করতে একটি লিখতে ভাল । আদর্শ পরীক্ষা হয় eval(repr(obj)) == obj। যদি আপনার কোডটির জন্য এটি করা সহজ হয় তবে আমি দৃ strongly়ভাবে এটির প্রস্তাব দিচ্ছি:

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

আপনি দেখুন, এটি একটি সমতুল্য বস্তুটি পুনরায় তৈরি করতে আমাদের ঠিক প্রয়োজন - এটি এমন কিছু যা আমাদের লগগুলিতে বা ব্যাকট্রেসগুলিতে প্রদর্শিত হতে পারে:

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

উপসংহার

আমি শুধু ব্যবহার করা উচিত mutablemapping(এটা মনে হয় এক ব্যবহার করা উচিত নয় UserDict বা DictMixin)? যদি তাই হয়, কিভাবে? ডক্সগুলি হুবহু আলোকিত নয় n't

হ্যাঁ, এগুলি কোডের আরও কয়েকটি লাইন, তবে সেগুলি বিস্তৃত হওয়ার উদ্দেশ্যে। আমার প্রথম প্রবণতাটি হ'ল গ্রহণযোগ্য উত্তরটি ব্যবহার করা হবে এবং যদি এতে কোনও সমস্যা থাকে তবে আমি আমার উত্তরটি তখনই দেখব - কারণ এটি কিছুটা জটিল, এবং আমার ইন্টারফেসটি সঠিকভাবে পেতে সহায়তা করার জন্য কোনও এবিসি নেই।

অকাল অপটিমাইজেশন কর্মক্ষমতা অনুসন্ধানে আরও জটিলতার জন্য যাচ্ছে। MutableMappingআরও সহজ - সুতরাং এটি তাত্ক্ষণিক প্রান্ত পায়, অন্য সমস্ত কিছু সমান। তবুও, সমস্ত পার্থক্য ছড়িয়ে দিতে, আসুন তুলনা এবং বিপরীতে।

আমার যুক্ত করা উচিত যে collectionsমডিউলে অনুরূপ অভিধান রাখার জন্য একটি চাপ ছিল তবে তা প্রত্যাখ্যান করা হয়েছিল । পরিবর্তে আপনার সম্ভবত এটি করা উচিত:

my_dict[transform(key)]

এটি আরও বেশি সহজে ডিবাগযোগ্য হওয়া উচিত।

তুলনা এবং প্রতিযোগিতা

এখানে MutableMapping(যা অনুপস্থিত fromkeys) এবং dictসাবক্লাসের সাথে ১১ টি ইন্টারফেস ফাংশন প্রয়োগ করা হয়েছে । আমি বাস্তবায়ন করতে হবে না __iter__বা __len__, কিন্তু এর পরিবর্তে আমি বাস্তবায়ন করতে হবে get, setdefault, pop, update, copy, __contains__, এবং fromkeysযেহেতু আমি ঐ বাস্তবায়নের অধিকাংশ জন্য উত্তরাধিকার ব্যবহার করতে পারেন, কিন্তু এই মোটামুটি তুচ্ছ আছে -।

MutableMappingকার্যকরী পাইথন মধ্যে কিছু বিষয় আছে যা dictসি কার্যকরী - তাই আমি একটি আশা dictউপশ্রেণী কিছু ক্ষেত্রে আরও performant যাবে।

আমরা __eq__উভয় পদ্ধতির মধ্যেই একটি ফ্রি পেয়েছি - উভয়ই কেবল সাম্যকে ধরে নেয় যদি অন্য ডিকটি সমস্ত ছোট হাতের হয় - তবে আবার, আমি মনে করি dictসাবক্লাস আরও দ্রুত তুলনা করবে।

সারসংক্ষেপ:

  • সাবক্লাসিং MutableMappingবাগের জন্য কম সুযোগের সাথে সহজ, তবে ধীরে ধীরে, আরও মেমরি লাগে (রিডানডেন্ট ডিক দেখুন), এবং ব্যর্থ হয়isinstance(x, dict)
  • সাবক্লাসিং dictদ্রুত হয়, কম মেমরি ব্যবহার করে এবং পাস হয় isinstance(x, dict)তবে এটি কার্যকর করার ক্ষেত্রে আরও জটিলতা রয়েছে।

কোনটি আরও নিখুঁত? এটি নিখুঁত আপনার সংজ্ঞা উপর নির্ভর করে।


গৃহীত উত্তর কীভাবে অপ্রয়োজনীয় আদেশটি সরিয়ে ফেলবে?
Seanny123

1
দুটি উপায় যা তাত্ক্ষণিকভাবে মনে আসে সেগুলি হ'ল স্টোরের বৈশিষ্ট্যটি ঘোষণা করা __slots__বা সম্ভবত দোকানটিকে পুনরায় ব্যবহার করা __dict__, তবে এটি শব্দার্থকে মিশ্রিত করে, এটি সমালোচনার আরেকটি সম্ভাব্য বিষয়।
অ্যারন হল

1
আপনি কি ensure_lowerপ্রথম কোনও যুক্তিযুক্ত পদ্ধতিতে (যা সর্বদা মূল) কীভাবে কোনও পদ্ধতি গ্রহণ করে এবং এটি ব্যবহার করে এমন কোনও শোভাকর লিখতে আরও সহজ হত? তারপরে এটি একই সংখ্যার ওভাররাইডগুলি হবে তবে তারা সমস্ত ফর্মের হবে __getitem__ = ensure_lower_decorator(super(LowerDict, self).__getitem__)
গ্রিফার

1
এর জন্য ধন্যবাদ - পপ এবং টোকি-র জন্য সতর্কতা পেয়েছে যে তারা বেস শ্রেণির পদ্ধতির স্বাক্ষরের সাথে মেলে না।
মিস্টার_আর_আমস_ডি

1
@ মিঃ_আর_আমস_ডি আমি একটি বাস্তবায়ন যুক্ত করেছি copy- আমার মনে হয় এটি করা উচিত, না? আমি মনে করি এটি ইন্টারফেসের জন্য পরীক্ষা করা উচিত - উদাহরণস্বরূপ পান্ডাস ডেটা ফ্রেম অবজেক্টটি ম্যাপিংয়ের উদাহরণ নয় (শেষ পরীক্ষায়) তবে এতে আইটেম / ইটারাইটেম রয়েছে।
অ্যারন হল

4

আমার প্রয়োজনীয়তা কিছুটা কঠোর ছিল:

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

আমার প্রাথমিক চিন্তাটি ছিল আমাদের ক্লানকি পাথ ক্লাসকে একটি সংবেদনশীল ইউনিকোড সাবক্লাসের ক্ষেত্রে প্রতিস্থাপন করা - তবে:

  • এই অধিকারটি পেতে কঠোর প্রমাণিত - দেখুন: পাইথনে একটি সংবেদনশীল স্ট্রিং ক্লাস
  • সুস্পষ্ট ডিক কীগুলি হ্যান্ডলিং কোড ভার্বোস এবং অগোছালো করে তোলে - এবং ত্রুটিযুক্ত প্রবণতা (কাঠামো এখানে এবং সেখানে চলে গেছে, এবং কীগুলি / উপাদান হিসাবে তাদের সিআইএসআরটি উদাহরণ রয়েছে তা ভুলে যাওয়া সহজ এবং some_dict[CIstr(path)]কুশ্রী কিনা তা পরিষ্কার নয় )

সুতরাং আমি অবশেষে যে মামলা সংবেদনশীল ডিক লিখতে ছিল। ধন্যবাদ কোড @AaronHall যে 10 বার সহজ করা হয়েছে।

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__())

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

মন্তব্য / সংশোধন স্বাগত :)


সিআইআরটির __repr__উচিত পিতামাত্ত শ্রেণীর ব্যবহার করা উচিত ইওল (রেপ (আপত্তি __repr__)) == আপত্তি পরীক্ষা (আমার মনে হয় না এখনই এটি করা হয়) এবং নির্ভর করে না __str__
অ্যারন হল

এছাড়াও চেক আউট total_orderingবর্গ প্রসাধক আপনার ইউনিকোড উপশ্রেণী থেকে 4 পদ্ধতি নিষ্কাশন করা হবে -। কিন্তু ডিক সাবক্লাসটি খুব চতুরতার সাথে বাস্তবায়িত দেখায়। : P: P
হারুন হলের

ধন্যবাদ @AaronHall - এটা আপনার যে বাস্তবায়িত আছে: পি লিখেছেন: মোট ক্রম - আমি ইচ্ছাকৃতভাবে পদ্ধতি যেমন রেমন্ড Hettinger এখানে দ্বারা উপদিষ্ট inlined লিখেছিলেন: stackoverflow.com/a/43122305/281545 । পুনরায়: পুনরায়: আমি একটি মন্তব্য (কিছু কোর দেব আইআইআরসি দ্বারা) পড়েছিলাম তা ভাল মনে আছে, সেই পরীক্ষাটি পাস করার জন্য পুনরায় চেষ্টা করা এবং এটি করা ঝামেলা করার মতো নয় (এটি একটি ঝামেলা) - এটি যতটা সম্ভব তথ্যবহুল হওয়াতে আরও ভাল ফোকাস ( তবে আরও নয়)
মিস্টার_আর_মিরস_ডি

আমি তোমাদের অনুমতি দেব না আপনার অপ্রয়োজনীয় তুলনা পদ্ধতি (আপনি আপনার উত্তর তা সম্পর্কে একটি নোট করা উচিত), কিন্তু CIstr.__repr__, ইন আপনার মামলা, খুব সামান্য ঝগড়া সঙ্গে repr পরীক্ষা পাস করতে পারেন, এবং এটি একটি অনেক সুন্দর ডিবাগ করা উচিত। আমি __repr__আপনার ডিক্টের জন্য একটি যুক্ত করতাম আমি আমার উত্তরে এটি প্রদর্শন করব।
অ্যারন হল

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

4

আপনাকে যা করতে হবে তা হ'ল

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

অথবা

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

আমার ব্যক্তিগত ব্যবহারের জন্য একটি নমুনা ব্যবহার

### 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")

দ্রষ্টব্য : কেবল অজগর 3 এ পরীক্ষা করা হয়েছে


3

শীর্ষস্থানীয় দুটি প্রস্তাব উভয়ই চেষ্টা করার পরে , আমি পাইথন ২.7 এর একটি ছায়াময় চেহারার মাঝারি রুটে স্থির হয়েছি। সম্ভবত 3 টি স্যানার তবে আমার জন্য:

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

যা আমি সত্যিই ঘৃণা করি, তবে আমার প্রয়োজনগুলির সাথে এটি ফিট করে বলে মনে হচ্ছে, যা হ'ল:

  • ওভাররাইড করতে পারে **my_dict
    • যদি আপনার কাছ থেকে উত্তরাধিকার dict, এই আপনার কোড রোধ করা যাবে । চেষ্টা কর.
    • এটি আমার জন্য সর্বদা # 2 অগ্রহণযোগ্য করে তোলে , কারণ পাইথন কোডে এটি বেশ সাধারণ
  • মাস্ক্রেড হিসাবে isinstance(my_dict, dict)
    • একা মিউটেবলম্যাপিংয়ের বিষয়টি বাতিল করে দেয়, সুতরাং # 1 পর্যাপ্ত নয়
    • আপনার যদি এটির প্রয়োজন না হয় তবে আমি আন্তরিকভাবে সুপারিশ করছি # 1 এটি সহজ এবং অনুমানযোগ্য
  • সম্পূর্ণ নিয়ন্ত্রণযোগ্য আচরণ
    • সুতরাং আমি উত্তরাধিকারী হতে পারি না dict

আপনার যদি নিজেকে অন্যের থেকে আলাদা করে বলতে হয় তবে ব্যক্তিগতভাবে আমি এই জাতীয় কিছু ব্যবহার করি (যদিও আমি আরও ভাল নামগুলির প্রস্তাব দিই):

def __am_i_me(self):
  return True

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

যতক্ষণ না কেবল আপনাকে কেবল অভ্যন্তরীণভাবে স্বীকৃতি দেওয়া দরকার ততক্ষণ এইভাবে __am_i_meঅজগরটির নাম-মুংয়ের কারণে দুর্ঘটনাক্রমে কল করা শক্ত হয় ( _MyDict__am_i_meএই শ্রেণীর বাইরে ফোন করে যে কোনও কিছু থেকে এটির নামকরণ করা হয়েছে)। _methodঅনুশীলনে এবং সাংস্কৃতিকভাবে উভয়ের চেয়ে সামান্য বেশি ব্যক্তিগত ।

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


প্রমাণ হিসাবে: https://repl.it/repls/TraumaticToughCockatoo

মূলত: বর্তমান # 2 বিকল্পটি অনুলিপি করুন , print 'method_name'প্রতিটি পদ্ধতিতে লাইন যুক্ত করুন এবং তারপরে এটি চেষ্টা করে দেখুন আউটপুট:

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

আপনি অন্যান্য পরিস্থিতিতে একই রকম আচরণ দেখতে পাবেন। আপনার নকলটি বলুন- dictএটি অন্য কোনও ডেটাটাইপের চারপাশে একটি মোড়ক, তাই ডেটা ব্যাকিং-ডিকটিতে সংরক্ষণ করার কোনও যুক্তিসঙ্গত উপায় নেই; **your_dictখালি খালি থাকবে, প্রতিটি অন্যান্য পদ্ধতি যা-ই করুক না কেন।

এটি এর জন্য সঠিকভাবে কাজ করে MutableMappingতবে আপনি dictএটির উত্তরাধিকার সূত্রে অনিয়ন্ত্রিত হয়ে পড়েন becomes


সম্পাদনা: আপডেট হিসাবে এটি প্রায় দুই বছর ধরে একক ইস্যু ছাড়াই চলছে, কয়েক শতাধিক (এহ, কয়েক মিলিয়ন হতে পারে) জটিল, উত্তরাধিকার-অদৃশ্য পাইথনের লাইন। সুতরাং আমি এটি দিয়ে বেশ খুশি :)

সম্পাদনা 2: স্পষ্টতই আমি এটি বা খুব আগে অনেক কিছু অনুলিপি করেছি। চেকগুলির @classmethod __class__জন্য কাজ করে না isinstance- @property __class__করায়: https://repl.it/repls/UnitedSifographicSequence


ঠিক কি আপনি বোঝাতে চাচ্ছেন " **your_dictখালি হতে হবে" (যদি আপনার কাছ থেকে উপশ্রেণী dict)? ডিক আনপ্যাকিংয়ের সাথে আমি কোনও সমস্যা দেখিনি ...
ম্যাট পি

যদি আপনি প্রকৃতপক্ষে ডেটা প্যারেন্ট ডিকের মধ্যে রাখেন (যেমন লোয়ারডিক্ট করেন), এটি কাজ করে - আপনি সেই ডিক-সঞ্চিত ডেটা পাবেন। আপনি যদি না বলেন (you অ্যাক্সেস_কাউন্ট: "অ্যাক্সেসের স্ট্যাক ট্রেস" like যা প্রতিটি বার এটি পড়বে তখন পূরণ করে) এর মতো আপনি ফ্লাইতে ডেটা তৈরি করতে চেয়েছিলেন), আপনি লক্ষ্য করবেন যে **your_dictআপনার কোডটি কার্যকর করে না, তাই এটি "বিশেষ" কিছু আউটপুট দিতে পারে না। যেমন আপনি "পঠন" গণনা করতে পারবেন না কারণ এটি আপনার পঠন-গণনা কোডটি কার্যকর করে না। মিউটেবলম্যাপিং এর জন্য কাজ করে (আপনি যদি পারেন তবে এটি ব্যবহার করুন!) তবে এটি ব্যর্থ হয় isinstance(..., dict)তাই আমি এটি ব্যবহার করতে পারিনি। ইয়া লিগ্যাসি সফ্টওয়্যার
গ্রুমিক্স

ঠিক আছে, আমি এখন আপনি কি বলতে চাইছেন তা দেখতে পাচ্ছি। আমি মনে করি আমি কোড সম্পাদন আশা করি না **your_dict, তবে আমি এটি খুব আকর্ষণীয় মনে MutableMappingকরি যা এটি করবে।
ম্যাট পি

হ্যাঁ। এটি বেশ কয়েকটি জিনিসের জন্য প্রয়োজনীয় (যেমন আমি স্থানীয় ডিক পড়তে যাচ্ছিলাম তাতে আরপিসি কলকে ঝকঝকে করছিলাম, এবং কারণগুলির চাহিদা অনুসারে এটি করতে হয়েছিল), এবং মনে হয় খুব কম লোকই এটি সম্পর্কে অবগত আছেন, এমনকি **some_dictমোটামুটি সাধারণ। খুব কমপক্ষে এটি সজ্জায় খুব ঘন ঘন ঘটে থাকে, সুতরাং আপনার যদি কোনও থাকে তবে আপনি যদি এটির জন্য অ্যাকাউন্ট না করেন তবে অবিলম্বে আপাতদৃষ্টিতে-অসম্ভব খারাপ আচরণের ঝুঁকির মধ্যে রয়েছে।
গ্রোক্সিক্স

সম্ভবত আমি কিছু মিস করছি তবে def __class__()কৌতুকটি পাইথন 2 বা 3 এর সাথে কাজ করছে বলে মনে হচ্ছে না, কমপক্ষে প্রশ্নের উদাহরণ কোডের জন্য কীভাবে ডাব সাবক্লাস হিসাবে abc.MutableMapping এর প্রয়োগটি নিবন্ধিত করবেন? (অন্যথায় দুটি সংস্করণে কাজ করতে পরিবর্তিত)। আমি চাই isinstance(SpreadSheet(), dict)ফিরে যাওয়ার True
মার্টিনিউ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.