কীভাবে পাইথনের অন্তর্নির্মিত শব্দকোষ প্রয়োগ করা হয়?


294

অজগরটির জন্য বিল্ট ইন ডিকশনারি কীভাবে প্রয়োগ করা হয় তা কি কেউ জানেন? আমার বোধগম্যতা এটি হ্যাশ টেবিলের এক প্রকারের, তবে আমি কোনও ধরণের যথাযথ উত্তর খুঁজে পাচ্ছি না।


4
এখানে পাইথন অভিধানগুলি সম্পর্কে 2.7 থেকে 3.6 পর্যন্ত একটি অন্তর্দৃষ্টিপূর্ণ আলোচনা রয়েছে। লিঙ্ক
সেরেন

উত্তর:


494

পাইথন ডিসটিক্স সম্পর্কে এখানে যা আমি একসাথে রাখতে সক্ষম হয়েছি (সম্ভবত যে কেউ জানতে চাইবে তার চেয়ে বেশি; তবে উত্তরটি বিস্তৃত)।

  • পাইথন অভিধানগুলি হ্যাশ টেবিল হিসাবে প্রয়োগ করা হয় ।
  • হ্যাশ টেবিলগুলির অবশ্যই হ্যাশ সংঘর্ষের জন্য অনুমতি দিতে হবে, এমনকি যদি দুটি স্বতন্ত্র কীগুলির একই হ্যাশ মান থাকে, সারণীর বাস্তবায়নে অবশ্যই কী এবং মান জোড়গুলি নিস্পষ্টভাবে সন্নিবেশ করানো এবং পুনরুদ্ধার করার কৌশল থাকতে হবে।
  • পাইথন হ্যাশের সংঘর্ষগুলি সমাধান করার জন্য উন্মুক্ত ঠিকানাdict ব্যবহার করে (নীচে ব্যাখ্যা করা হয়েছে) ( ডিক্টোবজেক্ট.২: ২৯6-২97 দেখুন )।
  • পাইথন হ্যাশ টেবিলটি কেবল মেমরির একটি সংলগ্ন ব্লক (একটি অ্যারের মতো সাজানো, যাতে আপনি O(1)সূচক অনুসারে একটি অনুসন্ধান করতে পারেন )।
  • টেবিলের প্রতিটি স্লট একটি এবং কেবলমাত্র একটি প্রবেশিকা সঞ্চয় করতে পারে। এটা গুরুত্বপূর্ণ.
  • সারণীতে প্রতিটি এন্ট্রি আসলে তিনটি মানের সমন্বয় করে: <হ্যাশ, কী, মান> । এটি সি স্ট্রাক্ট হিসাবে প্রয়োগ করা হয়েছে ( ডিক্টোবজেক্ট h: 51-56 দেখুন )।
  • নীচের চিত্রটি একটি পাইথন হ্যাশ টেবিলের যৌক্তিক উপস্থাপনা। নীচের চিত্রটিতে, 0, 1, ..., i, ...বামদিকে হ্যাশ টেবিলের স্লটগুলির সূচক রয়েছে (এগুলি কেবল উদাহরণস্বরূপ উদ্দেশ্যে রয়েছে এবং টেবিলের সাথে স্পষ্টত সংরক্ষণ করা হয় না!)।

    # Logical model of Python Hash table
    -+-----------------+
    0| <hash|key|value>|
    -+-----------------+
    1|      ...        |
    -+-----------------+
    .|      ...        |
    -+-----------------+
    i|      ...        |
    -+-----------------+
    .|      ...        |
    -+-----------------+
    n|      ...        |
    -+-----------------+
    
  • নতুন ডিক শুরু হলে এটি 8 টি স্লট দিয়ে শুরু হয় । ( ডিক্টোবজেক্ট দেখুন: 49 )

  • টেবিলে এন্ট্রি যুক্ত করার সময়, আমরা কিছু স্লট দিয়ে শুরু করি i, এটি কী এর হ্যাশের উপর ভিত্তি করে। সিপিথন প্রাথমিকভাবে ব্যবহার করে i = hash(key) & mask(যেখানে mask = PyDictMINSIZE - 1, তবে এটি আসলে গুরুত্বপূর্ণ নয়)। কেবলমাত্র লক্ষ্য করুন যে প্রাথমিক স্লটটি, iএটি যাচাই করা হয়েছে কীটির হ্যাশের উপর নির্ভর করে ।
  • যদি সেই স্লটটি খালি থাকে, স্লটে এন্ট্রি যুক্ত করা হবে (এন্ট্রি দ্বারা, মানে <hash|key|value>)। তবে কী যদি সেই স্লটটি দখল করা হয় !? সম্ভবত সম্ভবত অন্য এন্ট্রিতে একই হ্যাশ রয়েছে (হ্যাশ সংঘর্ষ!)
  • স্লটটি যদি দখল করা থাকে তবে সিপিথন (এবং পিপিপি) হ্যাশ এবং কী (তুলনা করে আমি ==তুলনা নয় মানে তুলনা করে is) সন্নিবেশ করানোর জন্য বর্তমান প্রবেশের হ্যাশ এবং কীটির বিপরীতে স্লটে প্রবেশের ( ডিক্টোবজেক্ট। : যথাক্রমে 337,344-345 )। যদি উভয়ই মিলে যায়, তবে এটি মনে করে যে এন্ট্রিটি ইতিমধ্যে বিদ্যমান রয়েছে, ছেড়ে দেয় এবং entryোকানোর জন্য পরবর্তী প্রবেশপথে এগিয়ে যায়। যদি হ্যাশ বা কীটি মেলে না, এটি অনুসন্ধান শুরু করে ।
  • অনুসন্ধানের অর্থ হ'ল এটি খালি স্লট স্লট দ্বারা স্লটগুলি সন্ধান করে। প্রযুক্তিগতভাবে আমরা কেবল একের পর এক যেতে পারি, i+1, i+2, ...এবং প্রথম উপলব্ধ একটি ব্যবহার করতে পারি (এটি লিনিয়ার প্রোবিং)। তবে কারণগুলিতে মন্তব্যগুলিতে সুন্দরভাবে ব্যাখ্যা করা হয়েছে (দেখুন ডিক্টোবজেক্ট.সি: ৩৩-১২6 ), সিপিথন এলোমেলো প্রোবিং ব্যবহার করে । এলোমেলো অনুসন্ধানে, পরবর্তী স্লটটি সিউডো এলোমেলো ক্রমে বেছে নেওয়া হয়েছে। প্রথম খালি স্লটে এন্ট্রি যুক্ত করা হয়। এই আলোচনার জন্য, পরবর্তী স্লটটি বাছাইয়ের জন্য ব্যবহৃত প্রকৃত অ্যালগরিদমটি সত্যই গুরুত্বপূর্ণ নয় ( অনুসন্ধানের জন্য অ্যালগরিদমের জন্য ডিক্টোবজেক্ট.সি: ৩৩-১২6 দেখুন )। কী গুরুত্বপূর্ণ তা হল প্রথম খালি স্লট না পাওয়া পর্যন্ত স্লটগুলি তদন্ত করা হবে।
  • অনুসন্ধানের ক্ষেত্রে একই জিনিস ঘটে, কেবল প্রাথমিক স্লট আই (যেখানে আমি কীটির হ্যাশের উপর নির্ভরশীল) দিয়ে শুরু হয়। যদি হ্যাশ এবং কী উভয় স্লটে প্রবেশের সাথে মেলে না, তবে এটি অনুসন্ধান করা শুরু করবে, যতক্ষণ না এটি কোনও ম্যাচ সহ একটি স্লট খুঁজে পায়। সমস্ত স্লট ক্লান্ত হয়ে গেলে, এটি একটি ব্যর্থতার রিপোর্ট করে।
  • বিটিডাব্লু, dictদুই-তৃতীয়াংশ পূর্ণ হলে পুনরায় আকার দেওয়া হবে। এটি অনুসন্ধানগুলিকে আস্তে আস্তে এড়িয়ে যায়। ( ডিক্টোবজেক্ট দেখুন: 64৪-65৫ )

দ্রষ্টব্য: ডিকটিতে একাধিক এন্ট্রি কীভাবে একই হ্যাশ মান রাখতে পারে সে সম্পর্কে আমার নিজের প্রশ্নের জবাবে পাইথন ডিক্ট প্রয়োগের উপর গবেষণাটি করেছি। আমি এখানে প্রতিক্রিয়াটির একটি সামান্য সম্পাদিত সংস্করণ পোস্ট করেছি কারণ সমস্ত গবেষণাও এই প্রশ্নের জন্য খুব প্রাসঙ্গিক।


8
আপনি বলেছিলেন, যখন হ্যাশ এবং কী উভয় মিলে যায়, এটি (opোকান অপশন) ছেড়ে দেয় এবং এগিয়ে যায়। এই ক্ষেত্রে বিদ্যমান প্রবেশটি ওভাররাইট সন্নিবেশ করে না?
0xc0de

65

কীভাবে পাইথনের অন্তর্নির্মিত শব্দকোষ প্রয়োগ করা হয়?

এখানে সংক্ষিপ্ত কোর্স:

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

অর্জিত দিকটি পাইথন ৩.6 (অন্যান্য বাস্তবায়নকে অব্যাহত রাখার সুযোগ দেওয়ার মতো) হিসাবে আনুষ্ঠানিক , তবে পাইথন ৩.7-তে অফিসিয়াল

পাইথনের ডিকোচারগুলি হ্যাশ টেবিল

দীর্ঘ সময় ধরে এটি ঠিক এভাবে কাজ করেছিল। পাইথন 8 টি খালি সারি পূর্বনির্ধারিত করে এবং কী-মানটির জোড়টি কোথায় আটকে থাকবে তা নির্ধারণ করতে হ্যাশ ব্যবহার করবে। উদাহরণস্বরূপ, কীটির জন্য হ্যাশটি যদি 001 এ শেষ হয়, তবে এটি এটি 1 (অর্থাৎ ২ য়) সূচীতে আটকে থাকবে (নীচের উদাহরণের মতো))

   <hash>       <key>    <value>
     null        null    null
...010001    ffeb678c    633241c4 # addresses of the keys and values
     null        null    null
      ...         ...    ...

প্রতিটি সারি একটি 64 বিট আর্কিটেকচারে 24 বাইট নেয়, 32 বিটটিতে 12 করে। (নোট করুন যে কলামের শিরোনামগুলি এখানে আমাদের উদ্দেশ্যগুলির জন্য কেবলমাত্র লেবেল - এগুলি আসলে মেমরিতে বিদ্যমান নেই))

যদি হ্যাশটি প্রিসিস্টিং কী এর হ্যাশ হিসাবে একই সমাপ্ত হয়, এটি একটি সংঘর্ষ, এবং তারপরে এটি মূল-মান জুটিটিকে অন্য কোনও স্থানে আটকে রাখবে।

5 টি মূল-মান সঞ্চিত হওয়ার পরে, অন্য কী-মান জোড় যুক্ত করার পরে, হ্যাশের সংঘর্ষের সম্ভাবনা খুব বড়, সুতরাং অভিধানটি আকারে দ্বিগুণ হয়। একটি bit৪ বিটের প্রক্রিয়াতে, আকার পরিবর্তন করার আগে, আমাদের 72 টি বাইট খালি রয়েছে এবং পরে, 10 টি খালি সারিগুলির কারণে আমরা 240 বাইট নষ্ট করছি।

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

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

উপরে বর্ণিত নষ্ট স্থান আমাদের অভিধানের বাস্তবায়ন পরিবর্তন করতে পরিচালিত করেছে, একটি আকর্ষণীয় নতুন বৈশিষ্ট্য সহ অভিধানগুলি এখন সন্নিবেশ দ্বারা আদেশ করা হয়েছে।

নতুন কমপ্যাক্ট হ্যাশ টেবিল

পরিবর্তে, সন্নিবেশ সূচকের জন্য একটি অ্যারের পূর্বনির্ধারণ করে শুরু করি।

যেহেতু আমাদের প্রথম কী-মান জুটি দ্বিতীয় স্লটে যায় তাই আমরা এই জাতীয় সূচী:

[null, 0, null, null, null, null, null, null]

এবং আমাদের সারণী সন্নিবেশ ক্রমের দ্বারা সজ্জিত হয়ে উঠেছে:

   <hash>       <key>    <value>
...010001    ffeb678c    633241c4 
      ...         ...    ...

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

আমরা অবিচ্ছিন্নভাবে অনুসন্ধানের সময়টি ধরে রাখি, কিছু ক্ষেত্রে সামান্য গতির ক্ষতি এবং অন্যদের মধ্যে লাভ সহ, আমরা পূর্ব-বিদ্যমান বাস্তবায়নের চেয়ে অনেক বেশি জায়গা সঞ্চয় করি এবং আমরা সন্নিবেশ ক্রম ধরে রাখি। কেবলমাত্র স্থান নষ্ট হয় সূচি অ্যারেতে নাল বাইট।

রেমন্ড Hettinger এই চালু পাইথন-দেব ডিসেম্বর 2012. অবশেষে মধ্যে CPython পেয়েছিলাম মধ্যে পাইথন 3.6 । পাইথনের অন্যান্য বাস্তবায়নের সুযোগটি ধরার সুযোগ দেওয়ার জন্য সন্নিবেশ দ্বারা অর্ডার দেওয়া 3.6 এর জন্য একটি বাস্তবায়ন বিশদ হিসাবে বিবেচিত হয়েছিল।

ভাগ করা কী

স্থান বাঁচানোর জন্য আরেকটি অপ্টিমাইজেশন হ'ল একটি বাস্তবায়ন যা কীগুলি ভাগ করে। সুতরাং, সেই স্থানটি সমস্ত নিয়ে যাওয়া নিরর্থক অভিধানগুলির পরিবর্তে আমাদের কাছে অভিধান রয়েছে যা ভাগ করা কী এবং কীগুলির হ্যাশগুলি পুনরায় ব্যবহার করে। আপনি এটি এরকমভাবে ভাবতে পারেন:

     hash         key    dict_0    dict_1    dict_2...
...010001    ffeb678c    633241c4  fffad420  ...
      ...         ...    ...       ...       ...

একটি 64 বিট মেশিনের জন্য, এটি অতিরিক্ত অভিধানে প্রতি কী প্রতি 16 বাইট সঞ্চয় করতে পারে।

কাস্টম অবজেক্টস এবং বিকল্পগুলির জন্য ভাগ করা কীগুলি

এই ভাগ করা-কী dicts কাস্টম অবজেক্ট 'জন্য ব্যবহার করার উদ্দেশ্যে করা হয় __dict__। এই আচরণটি পেতে, আমি বিশ্বাস করি যে আপনি __dict__আপনার পরবর্তী বস্তুটি ইনস্ট্যান্ট করার আগে আপনার জনসংখ্যা শেষ করতে হবে ( পিইপি 412 দেখুন )। এর অর্থ আপনার __init__বা আপনার সমস্ত বৈশিষ্ট্য বরাদ্দ করা উচিত __new__, অন্যথায় আপনি আপনার স্থান সঞ্চয় নাও পেতে পারেন।

যাইহোক, আপনি __init__কার্যকর হওয়ার সময় যদি আপনার সমস্ত বৈশিষ্ট্যগুলি জানেন তবে আপনি __slots__নিজের বস্তুর জন্য এবং গ্যারান্টি সরবরাহ করতে পারেন __dict__যা মোটেই তৈরি করা হয়নি (পিতামাতার কাছে উপলব্ধ না থাকলেও) বা এমনকি অনুমতি দেয় __dict__তবে গ্যারান্টি দেয় যে আপনার আগত বৈশিষ্ট্যগুলি যাইহোক স্লট সঞ্চিত। আরও জন্য __slots__, আমার উত্তর এখানে দেখুন

আরো দেখুন:


1
আপনি বলেছিলেন "আমরা", এবং "পাইথনের অন্যান্য বাস্তবায়নগুলিকে ধরার সুযোগ দেওয়ার জন্য" - এর অর্থ কি আপনি "জিনিসগুলি জানেন" এবং এটি একটি স্থায়ী বৈশিষ্ট্য হয়ে উঠতে পারে? অনুমান দ্বারা আদেশ করা dicts কোন খারাপ দিক আছে?
toonarmycaptain

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

1
আপনি কিছুটা অস্পষ্টভাবে ব্যাখ্যা করেছিলেন কীভাবে সন্নিবেশ কাজ করে ("যদি হ্যাশটি একটি প্রিসিস্টিং কী এর হ্যাশ হিসাবে সমাপ্ত হয়, ... তবে এটি কী-মানের জোড়টিকে অন্য কোনও স্থানে আটকে দেবে" - কোনও?), তবে আপনি ব্যাখ্যা করেন নি কিভাবে অনুসন্ধান এবং সদস্যপদ পরীক্ষা কাজ। এটি হ্যাশ দ্বারা কীভাবে অবস্থান নির্ধারণ করা হয় তা পুরোপুরি পরিষ্কার নয় তবে আমি মনে করি আকারটি সর্বদা 2 এর শক্তি এবং আপনি হ্যাশের শেষ কয়েকটি বিট নেন ...
আলেক্সি

@ অ্যালেক্সি আমি যে সর্বশেষ লিঙ্কটি সরবরাহ করেছি তা আপনাকে সুচিত-বর্ণিত ডিক বাস্তবায়ন দেয় - যেখানে আপনি বর্তমানে 969 লাইনে এই ফাংশনটি করতে পারেন যা বলা হয় find_empty_slot: github.com/python/cpython/blob/master/Objects/dictobject.c # L969 - এবং 134 লাইনে শুরু করে কিছু গদ্য রয়েছে যা এটি বর্ণনা করে।
অ্যারন হল

46

পাইথন ডিকোচারগুলি ওপেন অ্যাড্রেসিং ( সুন্দর কোডের অভ্যন্তরে রেফারেন্স ) ব্যবহার করে

বিশেষ দ্রষ্টব্য! ওপেন অ্যাড্রেসিং , ওরফে ক্লোজড হ্যাশিং , উইকিপিডিয়ায় উল্লিখিত হিসাবে, এর বিপরীত ওপেন হ্যাশিংয়ের সাথে বিভ্রান্ত হওয়া উচিত নয় !

ওপেন অ্যাড্রেসিং এর অর্থ হ'ল ডেক অ্যারে স্লটগুলি ব্যবহার করে এবং যখন কোনও বস্তুর প্রাথমিক অবস্থান ডিকটিতে নেওয়া হয় তখন "পার্টিউথিউশন" স্কিম ব্যবহার করে বস্তুর স্পট একই অ্যারেতে আলাদা সূচীতে অনুসন্ধান করা হয়, যেখানে বস্তুর হ্যাশ মান অংশ করে plays ।


5
"এর বিপরীত ওপেন হ্যাশিংয়ের সাথে বিভ্রান্ত হবেন না! (যা আমরা স্বীকৃত উত্তরে দেখি)।" - আপনি কখন যে উত্তরটি লিখেছিলেন বা কোন উত্তরটি সেই সময়ে কী বলেছিল তা আমি নিশ্চিত নই - তবে এই প্রথম উত্তরটি গৃহীত উত্তরের ক্ষেত্রে সত্য নয় এবং সবচেয়ে ভালভাবে সরিয়ে দেওয়া হবে।
টনি ডেলরয়
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.