[* ক] সামগ্রিকভাবে যুক্ত করার কারণ কী?


136

আপাতদৃষ্টিতে সামগ্রিকভাবে কিছু list(a)হয় না, [x for x in a]সামগ্রিকভাবে কিছু পয়েন্টে ওভারকোলেট হয় এবং সার্বক্ষণিকভাবে[*a] সামগ্রিকভাবে থাকে ?

আকার = এন = 100

এখানে 0 থেকে 12 পর্যন্ত আকারের এন এবং ফলস্বরূপ আকারগুলি তিনটি পদ্ধতির জন্য বাইটে রয়েছে:

0 56 56 56
1 64 88 88
2 72 88 96
3 80 88 104
4 88 88 112
5 96 120 120
6 104 120 128
7 112 120 136
8 120 120 152
9 128 184 184
10 136 184 192
11 144 184 200
12 152 184 208

এরকম গণনা করা হয়েছে , repl.it এ পুনরুত্পাদনযোগ্য , পাইথন ৩ ব্যবহার করে 8 :

from sys import getsizeof

for n in range(13):
    a = [None] * n
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]))

তাহলে এটা কিভাবে কাজ করে? [*a]সামগ্রিকভাবে কীভাবে হয় ? আসলে, প্রদত্ত ইনপুট থেকে ফলাফল তালিকা তৈরি করতে এটি কোন প্রক্রিয়া ব্যবহার করে? এটি কি পুনরুক্তি ব্যবহার করে aএবং এর মতো কিছু ব্যবহার করে list.append? সোর্স কোডটি কোথায়?

( ডেটা এবং কোড সহ কোলাব যা চিত্রগুলি তৈরি করে))

ছোট এন এ জুম করা:

আকার = n = 40 পর্যন্ত

বড় এন থেকে জুম করা:

N = 1000 অবধি মাপ


1
Fww, আপনার পরীক্ষার কেসগুলি প্রসারিত করে মনে হবে যে তালিকা বোধগম্যতা একটি লুপ লেখার এবং তালিকায় প্রতিটি আইটেম সংযোজন [*a]হিসাবে আচরণ করে , যখন extendখালি তালিকায় ব্যবহার করার মতো আচরণ করে ।
jdehesa

4
এটি প্রতিটিটির জন্য তৈরি বাইট কোডটি দেখতে সহায়তা করতে পারে। list(a)সম্পূর্ণ সিতে পরিচালনা করে; এটি পুনরাবৃত্ত হওয়ার সাথে সাথে নোড দ্বারা অভ্যন্তরীণ বাফার নোড বরাদ্দ করতে পারে a[x for x in a]কেবলমাত্র LIST_APPENDপ্রচুর ব্যবহার করে , তাই এটি "সাধারণভাবে কিছুটা ওভারটলোকট, যখন প্রয়োজন তখন পুনরায় ডেকে আনে" একটি সাধারণ তালিকার প্যাটার্ন অনুসরণ করে। [*a]ব্যবহার করে BUILD_LIST_UNPACK, যা ... আমি জানি না যে এটি কি করে, সমস্ত সময় দৃশ্যত অতিরিক্ত বরাদ্দ ব্যতীত :)
চিপনার

2
এছাড়াও, পাইথন ৩.7-এ, এটি প্রদর্শিত হয় list(a)এবং [*a]অভিন্ন, এবং উভয়ের তুলনায় সামগ্রিকভাবে কাজ করা হয় [x for x in a], সুতরাং ... sys.getsizeofএখানে ব্যবহারের সঠিক সরঞ্জাম নাও হতে পারে।
চিপনার

7
@ চেপারার আমি মনে করি sys.getsizeofসঠিক সরঞ্জাম, এটি কেবল দেখায় যে সামগ্রিকভাবে list(a)ব্যবহৃত হয়। আসলে কি নতুন পাইথন 3.8 এটা উল্লেখ: "তালিকা কন্সট্রাকটর overallocate না [...] করে"
স্টিফান পোচম্যান

5
@ চেপনার: এটি একটি বাগ ছিল যা 3.8 এ ঠিক করা হয়েছিল ; কনস্ট্রাক্টর সামগ্রিকভাবে কাজ করার কথা নয়।
শ্যাডোর্যাঞ্জার

উত্তর:


81

[*a] অভ্যন্তরীণভাবে এর সি সমতুল্য করছে :

  1. একটি নতুন, খালি করুন list
  2. কল newlist.extend(a)
  3. রিটার্নস list

সুতরাং আপনি যদি নিজের পরীক্ষাটি এখানে প্রসারিত করেন:

from sys import getsizeof

for n in range(13):
    a = [None] * n
    l = []
    l.extend(a)
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]),
             getsizeof(l))

এটি অনলাইন চেষ্টা করুন!

আপনি ফলাফল দেখতে পাবেন getsizeof([*a])এবং l = []; l.extend(a); getsizeof(l)একই।

এটি সাধারণত সঠিক জিনিস; যখন extendআপনি সাধারণত আরও পরে যুক্ত হওয়ার প্রত্যাশা করেন এবং একইভাবে সাধারণ আনপ্যাকিংয়ের জন্য, ধরে নেওয়া হয় যে একের পর এক একাধিক জিনিস যুক্ত করা হবে। [*a]সাধারণ ঘটনা নয়; পাইথন ধরে নেয় যে list( [*a, b, c, *d]) টিতে একাধিক আইটেম বা পুনরাবৃত্তি যুক্ত করা হচ্ছে , সুতরাং সামগ্রিকভাবে সাধারণ ক্ষেত্রে কাজ সংরক্ষণ করে।

বিপরীতে, একটি listএকক, চাপযুক্ত পুনরাবৃত্ত (থেকে list()) থেকে নির্মিত একটি ব্যবহারের সময় বৃদ্ধি বা সঙ্কুচিত হতে পারে না, এবং সামগ্রিকভাবে অন্যথায় প্রমাণিত না হওয়া পর্যন্ত অকাল হয়; পাইথন সম্প্রতি একটি বাগ ঠিক করেছে যা পরিচিত আকারের ইনপুটগুলির জন্যও কনস্ট্রাক্টরকে সামগ্রিকভাবে পরিণত করেছিল

listবোধগম্যতার জন্য , তারা কার্যকরভাবে পুনরাবৃত্তিগুলির সমতুল্য append, সুতরাং আপনি যখন কোনও সময়ে কোনও উপাদান যুক্ত করবেন তখন সাধারণ সামগ্রিক বৃদ্ধির ধরণের চূড়ান্ত ফলাফলটি দেখছেন।

স্পষ্ট করে বলতে গেলে, এর কোনওটিই ভাষার গ্যারান্টি নয়। এটি ঠিক কীভাবে সিপিথন এটি প্রয়োগ করে। পাইথন ল্যাঙ্গুয়েজ স্পেসটি সাধারণত নির্দিষ্ট বৃদ্ধির ধরণগুলির সাথে listঅবিচলিত থাকে ( শেষ থেকে এমওরাইজড O(1) appendএস এবং popগুলি গ্যারান্টি বাদ দিয়ে )। মন্তব্যে উল্লিখিত হিসাবে, নির্দিষ্ট বাস্তবায়ন আবার পরিবর্তন হয় 3.9; যখন এটি প্রভাবিত করবে না [*a], এটা অন্যান্য ক্ষেত্রে যেখানে কি ব্যবহার করা হয় প্রভাব ফেলতে পারে "একটি অস্থায়ী গড়ে তুলতে tupleবিশেষ কোনো আইটেম এবং তারপর extendসঙ্গে tuple" এখন একাধিক অ্যাপ্লিকেশন হয়ে LIST_APPEND, যা যখন অতিবরাদ্দকরণ ঘটে এবং যা সংখ্যার হিসাব ঢোকা পরিবর্তন করতে পারেন।


4
@ স্টেফানপোকম্যান: আমি কোডটি আগে পড়েছি (এ কারণেই আমি এটি ইতিমধ্যে জানতাম)। এটি এর জন্য বাইট কোড হ্যান্ডলারBUILD_LIST_UNPACK , এটি _PyList_Extendকল করার সি সমতুল্য হিসাবে ব্যবহার করে extend(পদ্ধতিটি দেখার চেয়ে সরাসরি সরাসরি)। তারা এটিকে tupleআনপ্যাকিংয়ের সাহায্যে রাস্তাগুলির সাথে একত্রিত করে ; tupleটুকরোচাল বিল্ডিংয়ের জন্য সামগ্রিকভাবে সামগ্রিকভাবে সরব না, যাতে তারা সর্বদা একটিতে list(সামগ্রিকীকরণ থেকে উপকৃত হওয়ার জন্য) আনপ্যাক করে এবং tupleযখন অনুরোধ করা হয়েছিল তখন শেষে রূপান্তর করে ।
শ্যাডোর্যাঞ্জার

4
নোট করুন যে এটি স্পষ্টতই 3.9-এ পরিবর্তিত হয়েছে , যেখানে একক বাইট কোড নির্দেশ দিয়ে সম্পূর্ণ নির্মাণের আগে স্ট্যাকের উপর সমস্ত কিছু লোড না করে পৃথক বাইকোডগুলি ( BUILD_LIST, LIST_EXTENDপ্রতিটি জিনিস আনপ্যাক করার LIST_APPENDজন্য, একক আইটেমের জন্য) listদিয়ে করা হয় (এটি অনুমতি দেয় কম্পাইলার বাস্তবায়নের মত অপ্টিমাইজেশন সম্পাদন করতে যে সব কিছু এক নির্দেশ দিতেন না, [*a, b, *c]যেমন LIST_EXTEND, LIST_APPEND, LIST_EXTENDW / O মোড়ানো প্রয়োজন bএকটি এক- মধ্যে tupleপ্রয়োজনীয়তা দেখা BUILD_LIST_UNPACK)।
শ্যাডোএ্যাঞ্জার

18

অন্যান্য উত্তর এবং মন্তব্যে (বিশেষত শ্যাডোর্যাঙ্গারের উত্তর , এটি কেন এটি কেন এমন হয়েছে তা ব্যাখ্যা করে) তৈরি করে কী হয় তার পূর্ণ চিত্র ।

BUILD_LIST_UNPACKব্যবহৃত হয়েছে এমন শো ডিসমেন্সলিং :

>>> import dis
>>> dis.dis('[*a]')
  1           0 LOAD_NAME                0 (a)
              2 BUILD_LIST_UNPACK        1
              4 RETURN_VALUE

এটি হ্যান্ডেল করা হয়েছেceval.c , যা একটি খালি তালিকা তৈরি করে এবং এর সাথে প্রসারিত করে a:

        case TARGET(BUILD_LIST_UNPACK): {
            ...
            PyObject *sum = PyList_New(0);
              ...
                none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));

_PyList_Extend ব্যবহার list_extend :

_PyList_Extend(PyListObject *self, PyObject *iterable)
{
    return list_extend(self, iterable);
}

যা আকারগুলির যোগফলের সাথে কল list_resizeকরে :

list_extend(PyListObject *self, PyObject *iterable)
    ...
        n = PySequence_Fast_GET_SIZE(iterable);
        ...
        m = Py_SIZE(self);
        ...
        if (list_resize(self, m + n) < 0) {

এবং সামগ্রিকভাবে নিম্নলিখিত হিসাবে:

list_resize(PyListObject *self, Py_ssize_t newsize)
{
  ...
    new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

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

from sys import getsizeof
for n in range(13):
    a = [None] * n
    expected_spots = n + (n >> 3) + (3 if n < 9 else 6)
    expected_bytesize = getsizeof([]) + expected_spots * 8
    real_bytesize = getsizeof([*a])
    print(n,
          expected_bytesize,
          real_bytesize,
          real_bytesize == expected_bytesize)

আউটপুট:

0 80 56 False
1 88 88 True
2 96 96 True
3 104 104 True
4 112 112 True
5 120 120 True
6 128 128 True
7 136 136 True
8 152 152 True
9 184 184 True
10 192 192 True
11 200 200 True
12 208 208 True

ব্যতীত ম্যাচগুলি n = 0, যা list_extendআসলে শর্টকাট , তাই আসলে এটিও মেলে:

        if (n == 0) {
            ...
            Py_RETURN_NONE;
        }
        ...
        if (list_resize(self, m + n) < 0) {

8

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

এটি বলেছিল, আপনি বুঝতে পারেন যে বোঝা এবং list(a)আচরণগুলি এখানে আসে:

https://github.com/python/cpython/blob/master/Objects/listobject.c#L36

বিশেষত বোঝার জন্য:

 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
...

new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

এই লাইনের ঠিক নীচে, list_preallocate_exactকল করার সময় এটি ব্যবহৃত হয় list(a)


1
[*a]একসাথে পৃথক উপাদান যুক্ত করা হয় না। এটি নিজস্ব ডেডিকেটেড বাইটকোড পেয়েছে, এটি দিয়ে বাল্ক সন্নিবেশ করে extend
শ্যাডোএ্যাঞ্জার

গোটচা - আমার ধারণা আমি এটিতে খুব বেশি খনন করিনি। বিভাগটি সরানো হয়েছে[*a]
রেন্ডি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.