আমি কীভাবে "নিখুঁত" ডেকের একটি সাবক্লাসকে সম্ভব হিসাবে তৈরি করতে পারি?
শেষ লক্ষ্যটি হ'ল একটি সরল ডিক থাকে যাতে কীগুলি ছোট হাতের অক্ষরে থাকে।
আমি যদি ওভাররাইড __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)
তবে এটি কার্যকর করার ক্ষেত্রে আরও জটিলতা রয়েছে।
কোনটি আরও নিখুঁত? এটি নিখুঁত আপনার সংজ্ঞা উপর নির্ভর করে।