পাইথন 3 এ কেন "100000000000000000 পরিসীমা (100000000000000001)" এত দ্রুত?


2111

এটি আমার বোধগম্য যে range()ফাংশনটি আসলে পাইথন 3- তে একটি অবজেক্ট টাইপ , এটি জেনারেটরের মতোই ফ্লাইতে তার সামগ্রী তৈরি করে।

এটি হ'ল, আমি নিম্নলিখিত লাইনটি একটি প্রচুর পরিমাণে সময় নেওয়ার প্রত্যাশা করতাম, কারণ 1 কোয়াড্রিলিয়ন সীমার মধ্যে রয়েছে কিনা তা নির্ধারণ করার জন্য, এক চতুর্থাংশের মান তৈরি করতে হবে:

1000000000000000 in range(1000000000000001)

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

আমি এ জাতীয় জিনিসও চেষ্টা করেছি, তবে গণনা এখনও প্রায় তাত্ক্ষণিক:

1000000000000000000000 in range(0,1000000000000000000001,10) # count by tens

যদি আমি আমার নিজস্ব পরিসীমা ফাংশন বাস্তবায়নের চেষ্টা করি, ফলাফলটি এত সুন্দর হয় না !!

def my_crappy_range(N):
    i = 0
    while i < N:
        yield i
        i += 1
    return

range()হুডের নীচে কী করা হচ্ছে যা এত তাড়াতাড়ি করে তোলে?


Martijn Pieters 'উত্তর তার সম্পূর্ণতার জন্য নির্বাচিত করা হয়, কিন্তু তাও দেখতে abarnert প্রথম উত্তর কি এটা জন্য মানে একটি ভাল আলোচনার জন্য rangeএকটি পূর্ণাঙ্গ হতে ক্রম পাইথন 3, এবং কিছু তথ্য / জন্য সম্ভাব্য অসঙ্গতি সংক্রান্ত সতর্কীকরণ __contains__পাইথন বাস্তবায়নের জুড়ে ফাংশন অপ্টিমাইজেশান । অ্যাবারনার্টের অন্য উত্তরটি আরও বিশদে যায় এবং পাইথন 3 (এবং xrangeপাইথন 2 এ অপ্টিমাইজেশনের অভাব) এর পিছনে ইতিহাসে আগ্রহীদের জন্য লিঙ্ক সরবরাহ করে । কৌতুক এবং উইম দ্বারা উত্তর প্রাসঙ্গিক সি উত্স কোড এবং যারা আগ্রহী তাদের ব্যাখ্যা প্রদান করে।


70
মনে রাখবেন যে এটি কেবল তখনই ঘটে যদি আমরা যাচাই করি আইটেমটি একটি boolবা longটাইপ হয়, অন্য অবজেক্টের ধরণের সাথে এটি পাগল হয়ে যায়। চেষ্টা করে দেখুন:100000000000000.0 in range(1000000000000001)
অশ্বিনী চৌধুরী চৌধুরী

10
কে আপনাকে বলেছিল যে rangeজেনারেটর?
অবতারিত

7
@ বার্নার্ট আমার ধারণা আমি যে সম্পাদনাটি করেছি তা বিভ্রান্তি থেকে যায়।
রিক


28
@ সুপেরবেস্ট xrange()অবজেক্টগুলির কোনও __contains__পদ্ধতি নেই, তাই আইটেম চেকটি সমস্ত আইটেমের মধ্যে লুপ করতে হবে। এছাড়াও এর মধ্যে আরও কয়েকটি পরিবর্তন রয়েছে range()যেমন এটি স্লাইসিংকে সমর্থন করে (যা আবার কোনও rangeবস্তুকে প্রত্যাবর্তন করে ) এবং এখন এবিসির সাথে সামঞ্জস্যপূর্ণ করার পদ্ধতি countএবং indexপদ্ধতিও রয়েছে collections.Sequence
অশ্বিনী চৌধুরী 21

উত্তর:


2167

পাইথন 3 range()অবজেক্টটি সাথে সাথে সংখ্যা তৈরি করে না; এটি একটি স্মার্ট সিকোয়েন্স অবজেক্ট যা চাহিদা অনুসারে সংখ্যা তৈরি করে । এটিতে থাকা সমস্ত হ'ল আপনার সূচনা, স্টপ এবং ধাপের মানগুলি, তারপরে আপনি যখন বস্তুর উপর পুনরাবৃত্তি করবেন তখন প্রতিটি পূর্ণসংখ্যা গণনা করা হবে calc

অবজেক্টটি object.__contains__হুক প্রয়োগ করে এবং গণনা করে যে আপনার সংখ্যাটি এর ব্যাপ্তির অংশ কিনা। গণনা করা হচ্ছে (কাছাকাছি) ধ্রুবক সময় অপারেশন * । ব্যাপ্তির সমস্ত সম্ভাব্য সংখ্যার মাধ্যমে স্ক্যান করার দরকার নেই।

থেকে range()বস্তুর ডকুমেন্টেশন :

rangeএকটি নিয়মিত listবা এই জাতীয় ধরণের সুবিধাটি tupleহ'ল একটি পরিসীমা অবজেক্ট সর্বদা সমান (ছোট) পরিমাণ মেমরি গ্রহণ করবে, এটি যে আকারের প্রতিনিধিত্ব করে তার আকার নির্ধারণ না করে (যেমন এটি কেবলমাত্র এবং মানগুলি সঞ্চয় করে start, পৃথক আইটেম এবং সাব্রেনজ গণনা করে যেমন দরকার).stopstep

সুতরাং সর্বনিম্ন, আপনার range()অবজেক্টটি এটি করবে:

class my_range(object):
    def __init__(self, start, stop=None, step=1):
        if stop is None:
            start, stop = 0, start
        self.start, self.stop, self.step = start, stop, step
        if step < 0:
            lo, hi, step = stop, start, -step
        else:
            lo, hi = start, stop
        self.length = 0 if lo > hi else ((hi - lo - 1) // step) + 1

    def __iter__(self):
        current = self.start
        if self.step < 0:
            while current > self.stop:
                yield current
                current += self.step
        else:
            while current < self.stop:
                yield current
                current += self.step

    def __len__(self):
        return self.length

    def __getitem__(self, i):
        if i < 0:
            i += self.length
        if 0 <= i < self.length:
            return self.start + i * self.step
        raise IndexError('Index out of range: {}'.format(i))

    def __contains__(self, num):
        if self.step < 0:
            if not (self.stop < num <= self.start):
                return False
        else:
            if not (self.start <= num < self.stop):
                return False
        return (num - self.start) % self.step == 0

এটি এখনও বেশ কয়েকটি জিনিস অনুপস্থিত যা বাস্তব range()সমর্থন করে (যেমন পদ্ধতি .index()বা .count()পদ্ধতি, হ্যাশিং, সমতা পরীক্ষা করা বা কাটা), তবে আপনাকে ধারণা দেওয়া উচিত।

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


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


58
মজার বিষয়: কারণ আপনার একটি কাজ বাস্তবায়ন আছে __getitem__এবং __len__, __iter__বাস্তবায়ন আসলে অপ্রয়োজনীয়।
লুক্রেটিয়েল 6:55

2
@ লুক্রেটিয়েল: পাইথন ২.৩-তে একটি বিশেষভাবে যুক্ত xrangeiteratorকরা হয়েছিল কারণ এটি যথেষ্ট দ্রুত ছিল না। এবং তারপরে কোথাও 3.x (আমি নিশ্চিত নই যে এটি 3.0 বা 3.2 ছিল কিনা) এটি টস করা হয়েছিল এবং তারা একই listiteratorধরণের ব্যবহার করে যা listব্যবহার করে।
অবার্নেট

1
আমি কনস্ট্রাক্টর হিসাবে সংজ্ঞায়িত করব def __init__(self, *start_stop_step)এবং সেখান থেকে পার্স করব; যুক্তিগুলি এখন যেভাবে লেবেল করা হয়েছে তা এখন বিভ্রান্তিকর। তবুও, +1; আপনি এখনও স্পষ্টভাবে আচরণ ব্যাখ্যা।
কোডি পিয়ার্সাল

1
@ কোডিপিয়ার্সাল: দুর্ভাগ্যক্রমে, এটিই আসল শ্রেণীর প্রারম্ভিকের স্বাক্ষর। rangeএর চেয়ে পুরনো *args( argclinicসিআইপি ফাংশনগুলিতে সম্পূর্ণ পাইথন স্বাক্ষর রাখতে দেয় এমন API এর চেয়ে কম )। কয়েকটি অন্যান্য পুরানো ফাংশন (এবং কয়েকটি নতুন ফাংশন, যেমন xrange, sliceএবং itertools.islice, ধারাবাহিকতার জন্য) একইভাবে কাজ করে তবে বেশিরভাগ অংশের জন্য, গুইডো এবং বাকী মূল দেবগণই আপনার সাথে একমত বলে মনে হয়। 2.0+ ডক্স এমনকি বর্ণনা করে rangeএবং বন্ধুরা যেন তারা সি ++ - স্টাইলের ওভারলোডগুলি প্রকৃত বিভ্রান্তিকর স্বাক্ষর না দেখায়।
9:30 এ অবার্নার্ট

2
@ কোডিপিয়ার্সাল: আসলে, গাইডের argclinicআলোচনার একটি উদ্ধৃতি এখানে , যখন নিক কোগলান rangeদ্ব্যর্থহীন সংজ্ঞা সংজ্ঞা দেওয়ার উপায় নিয়ে এলো : "দয়া করে লোকেরা আমার সবচেয়ে খারাপ নকশার সিদ্ধান্তটি অনুলিপি করতে সহজ করবেন না।" সুতরাং, আমি দৃ sure়ভাবে নিশ্চিত যে তিনি rangeলিখিতভাবে বিভ্রান্তিকর বিষয়ে একমত হন ।

843

এখানে মৌলিক ভুল বোঝাবুঝি rangeএটি একটি জেনারেটর thinking এটা না। আসলে এটি কোনও ধরণের পুনরুক্তিকারী নয়।

আপনি এটি খুব সহজেই বলতে পারেন:

>>> a = range(5)
>>> print(list(a))
[0, 1, 2, 3, 4]
>>> print(list(a))
[0, 1, 2, 3, 4]

যদি এটি কোনও জেনারেটর হয়, তবে এটির পুনরাবৃত্তি একবারে তা ছাড়িয়ে দেবে:

>>> b = my_crappy_range(5)
>>> print(list(b))
[0, 1, 2, 3, 4]
>>> print(list(b))
[]

rangeআসলে কি , এটি একটি তালিকার মতো একটি ক্রম। আপনি এটি পরীক্ষা করতে পারেন:

>>> import collections.abc
>>> isinstance(a, collections.abc.Sequence)
True

এর অর্থ এটি একটি ক্রম হওয়ার সমস্ত নিয়ম অনুসরণ করতে হবে:

>>> a[3]         # indexable
3
>>> len(a)       # sized
5
>>> 3 in a       # membership
True
>>> reversed(a)  # reversible
<range_iterator at 0x101cd2360>
>>> a.index(3)   # implements 'index'
3
>>> a.count(3)   # implements 'count'
1

একটি মধ্যে পার্থক্য rangeএবং listএকটি হয় rangeএকটি হল অলস বা গতিশীল ক্রম; এটা তার মূল্যবোধের সব মনে নেই, এটা ঠিক মনে তার start, stopএবং step, এবং চাহিদা মান তৈরি করে __getitem__

(পার্শ্ব নোট হিসাবে print(iter(a)), আপনি যদি লক্ষ্য করেন যে rangeসেই একই listiteratorধরণের ব্যবহার রয়েছে that listকীভাবে এটি কাজ করে? এটি কোনও সি প্রয়োগকারী সরবরাহ করে তবে এ listiteratorবিষয়ে বিশেষ কিছু ব্যবহার করা হয় না , সুতরাং এটি কার্যকর কাজ করে খুব।)list__getitem__range


এখন, এমন কিছুই নেই যা বলে যে Sequence.__contains__ধ্রুবক সময় হতে পারে - আসলে, যেমন ক্রমগুলির সুস্পষ্ট উদাহরণগুলির জন্য list, এটি নয়। তবে এমন কিছুই নেই যা বলে যে এটি হতে পারে না । এবং range.__contains__গাণিতিকভাবে পরীক্ষা করার জন্য এটি প্রয়োগ করা আরও সহজ ( (val - start) % stepতবে নেতিবাচক পদক্ষেপগুলি মোকাবেলায় কিছু অতিরিক্ত জটিলতার সাথে) আসলে সমস্ত মানগুলি উত্পন্ন এবং পরীক্ষা করা যায়, তবে কেন এটি আরও ভাল উপায়ে করা উচিত নয় ?

তবে ভাষায় এমন কিছু নেই যা গ্যারান্টি দেয় যে এটি ঘটবে। অশ্বিনী চৌধুরী যেমন উল্লেখ করেছেন, আপনি যদি এটি পূর্ণসংখ্যায় রূপান্তর ও গাণিতিক পরীক্ষা করার পরিবর্তে এটি একটি অ-অবিচ্ছেদ্য মান দেন তবে এটি সমস্ত মানকে পুনরাবৃত্তি করে এবং একে একে একে তুলনা করতে ফিরে আসবে। এবং কেবল সিপথন ৩.২+ এবং পাইপাই ৩.০ সংস্করণগুলি এই অপটিমাইজেশনটি ধারণ করে এবং এটি একটি সুস্পষ্ট ভাল ধারণা এবং সহজ কাজ, এর কোনও কারণ নেই যে আয়রন পাইথন বা নিউকিক্যাসপাইথন ৩.x এটিকে ছাড়তে পারে নি। (এবং বাস্তবে সিপিথন ৩.০-৩.১ এটিকে অন্তর্ভুক্ত করেনি ))


rangeপ্রকৃতপক্ষে যদি কোনও জেনারেটর my_crappy_rangeথাকত, তবে এটি __contains__এইভাবে পরীক্ষা করে দেখার মতো হবে না , বা কমপক্ষে এটি যেভাবে বোঝায় তা স্পষ্ট হবে না। আপনি যদি ইতিমধ্যে প্রথম 3 টি মানগুলি পুনরায় পাঠাতে চান তবে 1এখনও inজেনারেটর রয়েছে? পরীক্ষার 1কারণে এটি পুনরাবৃত্তি হয় এবং সমস্ত মান 1(বা প্রথম মান পর্যন্ত >= 1) গ্রাস করে ?


10
সোজা হয়ে উঠতে এটি বেশ গুরুত্বপূর্ণ বিষয়। আমি মনে করি পাইথন 2 এবং 3 এর মধ্যে পার্থক্যগুলি এই বিষয়টিতে আমার বিভ্রান্তির কারণ হতে পারে। যাইহোক, আমার অনুভূত হওয়া উচিত যেহেতু একটি সিকোয়েন্স টাইপ হিসাবে rangeতালিকাভুক্ত (বরাবর listএবং tuple)
রিক

4
@ রিকটিচি: আসলে, ২.6++ (আমার মনে হয়; সম্ভবত 2.5+), xrangeএটিও একটি ক্রম। 2.7 ডক্স দেখুন । আসলে, এটি সর্বদা একটি প্রায় ক্রম ছিল।
অবতারিত

5
@ রিকটিচি: আসলে, আমি ভুল ছিলাম; ২.6-২.। (এবং 3.0.০-৩.১) এ এটি একটি সিকোয়েন্স হিসাবে দাবি করেছে তবে এটি এখনও প্রায় সিক্যুয়েন্স। আমার অন্য উত্তর দেখুন।
অবতরণ

2
এটি কোনও পুনরুক্তিকারী নয়, এটি একটি সিকোয়েন্স (জাভা হিসাবে পরিগণিত, সি # এর গণ্যমান্য) - এমন একটি .__iter__()পদ্ধতি যা এমন একটি পুনরাবৃত্তিকে ফিরিয়ে দেবে। এটির পরিবর্তে এটি একবার ব্যবহার করা যেতে পারে।
স্মিথ জন্থ

4
@ থমাস অহলে: কারণ rangeযখন এটি পূর্ণসংখ্যা হয় না তখন প্রকারগুলি পরীক্ষা করা হয় না, যেহেতু এটি সর্বদা সম্ভব যে কোনও ধরণের __eq__সাথে সামঞ্জস্য হয় int। নিশ্চিত, strস্পষ্টত কাজ করবে না, কিন্তু তারা স্পষ্টভাবে সব ধরনের যে চেক করে মন্দীভূত জিনিষ চাইনি করতে পারবে না (a ও অতঃপর এখন ও নিস্কৃতির হতে strউপশ্রেণী ওভাররাইড করতে পারে __eq__এবং অন্তর্ভুক্ত করা range)।
শ্যাডোর্যাঞ্জার

377

সূত্রটি ব্যবহার করুন , লূক!

সিপিথনে, range(...).__contains__(একটি পদ্ধতির মোড়ক) অবশেষে একটি সাধারণ গণনার জন্য প্রেরণ করা হবে যা মানটি সম্ভবত সীমার মধ্যে থাকতে পারে কিনা তা পরীক্ষা করে। গতি জন্য কারণ এখানে আমরা ব্যবহার করছি সীমা বদলে পরিসীমা বস্তুর সরাসরি পুনরাবৃত্তির সম্পর্কে গাণিতিক যুক্তি । ব্যবহৃত যুক্তি ব্যাখ্যা করতে:

  1. নম্বরটি startএবং stop, এবং এর মধ্যে রয়েছে তা পরীক্ষা করুন
  2. পরীক্ষা করুন যে ধাপের মানটি আমাদের সংখ্যাটি "ধাপে ধাপে" যায় না।

উদাহরণস্বরূপ, 994হয় range(4, 1000, 2)কারণ:

  1. 4 <= 994 < 1000, এবং
  2. (994 - 4) % 2 == 0

সম্পূর্ণ সি কোডটি নীচে অন্তর্ভুক্ত করা হয়েছে, যা মেমরি পরিচালনা এবং রেফারেন্স গণনা বিশদের কারণে কিছুটা বেশি ভারবজ, তবে মূল ধারণাটি এখানে রয়েছে:

static int
range_contains_long(rangeobject *r, PyObject *ob)
{
    int cmp1, cmp2, cmp3;
    PyObject *tmp1 = NULL;
    PyObject *tmp2 = NULL;
    PyObject *zero = NULL;
    int result = -1;

    zero = PyLong_FromLong(0);
    if (zero == NULL) /* MemoryError in int(0) */
        goto end;

    /* Check if the value can possibly be in the range. */

    cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT);
    if (cmp1 == -1)
        goto end;
    if (cmp1 == 1) { /* positive steps: start <= ob < stop */
        cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE);
        cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT);
    }
    else { /* negative steps: stop < ob <= start */
        cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE);
        cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT);
    }

    if (cmp2 == -1 || cmp3 == -1) /* TypeError */
        goto end;
    if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */
        result = 0;
        goto end;
    }

    /* Check that the stride does not invalidate ob's membership. */
    tmp1 = PyNumber_Subtract(ob, r->start);
    if (tmp1 == NULL)
        goto end;
    tmp2 = PyNumber_Remainder(tmp1, r->step);
    if (tmp2 == NULL)
        goto end;
    /* result = ((int(ob) - start) % step) == 0 */
    result = PyObject_RichCompareBool(tmp2, zero, Py_EQ);
  end:
    Py_XDECREF(tmp1);
    Py_XDECREF(tmp2);
    Py_XDECREF(zero);
    return result;
}

static int
range_contains(rangeobject *r, PyObject *ob)
{
    if (PyLong_CheckExact(ob) || PyBool_Check(ob))
        return range_contains_long(r, ob);

    return (int)_PySequence_IterSearch((PyObject*)r, ob,
                                       PY_ITERSEARCH_CONTAINS);
}

ধারণাটির "মাংস" লাইনে উল্লেখ করা হয়েছে :

/* result = ((int(ob) - start) % step) == 0 */ 

চূড়ান্ত নোট হিসাবে - range_containsকোড স্নিপেটের নীচে ফাংশনটি দেখুন । যদি সঠিক ধরণের চেকটি ব্যর্থ হয় তবে আমরা বর্ণিত চৌকস অ্যালগরিদম ব্যবহার না করে পরিবর্তে ব্যাপ্তিটির বোবা পুনরাবৃত্তি অনুসন্ধানে ফিরে যাব _PySequence_IterSearch! আপনি দোভাষী এ আচরণটি পরীক্ষা করতে পারেন (আমি এখানে v3.5.0 ব্যবহার করছি):

>>> x, r = 1000000000000000, range(1000000000000001)
>>> class MyInt(int):
...     pass
... 
>>> x_ = MyInt(x)
>>> x in r  # calculates immediately :) 
True
>>> x_ in r  # iterates for ages.. :( 
^\Quit (core dumped)

144

মার্তিজানের উত্তর যুক্ত করতে, এটি উত্সের প্রাসঙ্গিক অংশ (সিতে, রেঞ্জ অবজেক্ট নেটিভ কোডে লেখা হয়):

static int
range_contains(rangeobject *r, PyObject *ob)
{
    if (PyLong_CheckExact(ob) || PyBool_Check(ob))
        return range_contains_long(r, ob);

    return (int)_PySequence_IterSearch((PyObject*)r, ob,
                                       PY_ITERSEARCH_CONTAINS);
}

সুতরাং PyLongবস্তুর জন্য (যা intপাইথন 3 এ রয়েছে), range_contains_longফলাফলটি নির্ধারণ করতে এটি ফাংশনটি ব্যবহার করবে । এবং সেই ফাংশনটি মূলত obনির্দিষ্ট রেঞ্জে রয়েছে কিনা তা পরীক্ষা করে দেখায় (যদিও এটি সিতে কিছুটা জটিল দেখাচ্ছে)।

যদি এটি কোনও intঅবজেক্ট না হয় , এটি মান না পাওয়া পর্যন্ত এটি পুনরাবৃত্তিতে ফিরে আসে (না)।

পুরো যুক্তিটি সিউডো-পাইথনে এভাবে অনুবাদ করা যেতে পারে:

def range_contains (rangeObj, obj):
    if isinstance(obj, int):
        return range_contains_long(rangeObj, obj)

    # default logic by iterating
    return any(obj == x for x in rangeObj)

def range_contains_long (r, num):
    if r.step > 0:
        # positive step: r.start <= num < r.stop
        cmp2 = r.start <= num
        cmp3 = num < r.stop
    else:
        # negative step: r.start >= num > r.stop
        cmp2 = num <= r.start
        cmp3 = r.stop < num

    # outside of the range boundaries
    if not cmp2 or not cmp3:
        return False

    # num must be on a valid step inside the boundaries
    return (num - r.start) % r.step == 0

11
@ ক্রিসওয়েসেলিং: আমি মনে করি এটি মার্টিজেনের উত্তর সম্পাদনা করা এখানে যথাযথ হবে না বলে আলাদা-পর্যাপ্ত তথ্য (এবং এটি যথেষ্ট)। এটি বিচারের ডাক, তবে লোকেরা সাধারণত অন্য ব্যক্তির জবাবগুলিতে কঠোর পরিবর্তন না করার পক্ষে ভুল করে।
abarnert

105

আপনি যদি ভাবছেন যে এই অপ্টিমাইজেশনটি কেন যুক্ত করা হয়েছিল range.__contains__এবং এটি 2.7-এ কেন যুক্ত করা হয়নিxrange.__contains__ :

প্রথমত, অশ্বিনী চৌধুরী যেমন আবিষ্কার করেছিলেন, 1766304 সংখ্যাটি অনুকূলভাবে অপ্টিমাইজ করার জন্য খোলা হয়েছিল [x]range.__contains__। এর জন্য একটি প্যাচ গ্রহণ করা হয়েছিল এবং 3.2 এ চেক ইন করা হয়েছিল , তবে এটি 2.7 এ ব্যাকপোর্ট করা হয়নি কারণ "এক্সরেঞ্জ এত দীর্ঘ সময় ধরে এরকম আচরণ করেছে যে এই দেরিতে প্যাচটি কমিট করার জন্য এটি কী আমাদের কেনা তা আমি দেখতে পাই না।" (২.7 তখন প্রায় বাইরে ছিল out)

এদিকে:

মূলত, xrangeএকটি ছিল না যথেষ্ট-সিকোয়েন্স অবজেক্ট। ৩.১ ডক্স যেমন বলে:

ব্যাপ্তি অবজেক্টগুলির খুব সামান্য আচরণ থাকে: তারা কেবল ইনডেক্সিং, পুনরাবৃত্তি এবং lenফাংশনটিকে সমর্থন করে।

এটি বেশ সত্য ছিল না; একটি xrangeবস্তুর আসলে কয়েক অন্যান্য বিষয় আছে যা ইন্ডেক্স এবং সাথে স্বয়ংক্রিয়ভাবে আসা সমর্থিত len, * সহ __contains__(রৈখিক অনুসন্ধান মাধ্যমে)। তবে কেউ সে সময় তাদের সম্পূর্ণ সিকোয়েন্সগুলি তৈরি করা উপযুক্ত বলে ভাবেনি।

তারপরে, অ্যাবস্ট্রাক্ট বেস ক্লাসগুলি পিইপি বাস্তবায়নের অংশ হিসাবে , এটি নির্ধারণ করা গুরুত্বপূর্ণ ছিল যে কোন অন্তর্নির্মিত প্রকারগুলি বাস্তবায়ন হিসাবে চিহ্নিত করা উচিত যা এবিসি, এবং xrange/ rangeবাস্তবায়নের দাবি করেছে collections.Sequence, যদিও এটি এখনও একই "খুব সামান্য আচরণ" পরিচালনা করেছিল। 9213 ইস্যু পর্যন্ত কেউই এই সমস্যাটি লক্ষ্য করেনি । এই ইস্যুটির প্যাচটি কেবল যুক্ত হয়নি indexএবং count3.2 range-তেও নয়, এটি পুনরায় অপ্টিমাইজড কাজও করেছে __contains__(যা একই গণিতের সাথে ভাগ করে index, এবং সরাসরি ব্যবহার করে count)। ** এই পরিবর্তনটি 3.2-তেও এসেছে, এবং এটি 2.x এ ব্যাকপোর্ট করা হয়নি, কারণ "এটি একটি বাগফিক্স যা নতুন পদ্ধতি যুক্ত করে"। (এই মুহুর্তে, 2.7 ইতিমধ্যে আরসি স্ট্যাটাসটি ছিল))

সুতরাং, এই অপটিমাইজেশনটি 2.7 এ ব্যাকপোর্ট করার দুটি সম্ভাবনা ছিল তবে তারা উভয়ই প্রত্যাখ্যান হয়েছিল।


* প্রকৃতপক্ষে, আপনি একা সূচক দিয়েও বিনামূল্যে পুনরাবৃত্তি পেতে পারেন তবে ২.৩ xrange অবজেক্টে একটি কাস্টম পুনরাবৃত্তি পেয়েছে।

** প্রথম সংস্করণটি আসলে এটি পুনরায় প্রয়োগ করেছিল এবং বিশদগুলি ভুল পেয়েছে — যেমন, এটি আপনাকে দেবে MyIntSubclass(2) in range(5) == False। কিন্তু ড্যানিয়েল স্টুটজবাচের প্যাচটির আপডেট হওয়া সংস্করণ জেনেরিকের ফ্যালব্যাক সহ পূর্ববর্তী কোডের বেশিরভাগটি পুনরুদ্ধার করেছিল, অপ্টিমাইজেশন প্রয়োগ না হওয়ার সময় _PySequence_IterSearchপ্রাক -3.2 স্পষ্টতই range.__contains__ব্যবহার করছিল।


4
এখানে দেওয়া মন্তব্যগুলি থেকে: উন্নত করুনxrange.__contains__ , দেখে মনে হচ্ছে তারা ব্যবহারকারীর জন্য আশ্চর্যের একটি উপাদান ছেড়ে যাওয়ার জন্য পাইথন 2 এ এটি ব্যাকপোর্ট করেনি এবং এটি খুব দেরী হয়ে গেছে _ countএবং index প্যাচ পরে যোগ করা হয়েছিল। সেই সময় ফাইলটি: hg.python.org/cpython/file/d599a3f2e72d/Objects/rangeobject.c
অশ্বিনী চৌধুরী চৌধুরী

12
আমি একটি অশুভ সন্দেহ আছে যে কিছু কোর পাইথন devs পাইথন 2.x জন্য "শক্ত প্রেম" আংশিক কারণ তারা সুদূর উচ্চতর python3 :) স্যুইচ করতে মানুষ উত্সাহিত করতে চান
Wim

4
এছাড়াও আমি বাজি ধরছি এটি পুরানো সংস্করণগুলিতে নতুন বৈশিষ্ট্য যুক্ত করতে একটি বিশাল বোঝা। আপনি যদি ওরাকলে গিয়ে বললেন, "দেখুন, আমি জাভা ১.৪ এ আছি এবং আমি ল্যাম্বডা অভিব্যক্তির প্রাপ্য! এগুলিকে কিছুতেই ব্যাকপোর্ট করুন।"
রব গ্রান্ট

2
@ রিকটিচি হ্যাঁ এটি কেবল একটি উদাহরণ। আমি যদি বলেছিলাম 1.7 এটি এখনও প্রয়োগ করা হবে। এটি একটি পরিমাণগত পার্থক্য গুণগত নয়। মূলত (অবৈতনিক) ডেভস চিরতরে 3.x এ শীতল নতুন জিনিস তৈরি করতে পারে না এবং যারা আপগ্রেড করতে চান না তাদের জন্য এটি 2.x এ ব্যাকপোর্ট করতে পারে। এটি একটি বিশাল এবং হাস্যকর বোঝা। আপনি কি মনে করেন আমার যুক্তিতে এখনও কিছু ভুল আছে?
রব গ্রান্ট

3
@ রিকটিচি: ২.7 ছিল ৩.১ থেকে ৩.২ এর মধ্যে, ৩.৩-এর কাছাকাছি ছিল না। এবং এর অর্থ 2.2 আরসিতে ছিল যখন শেষ পরিবর্তনগুলি 3.2 এ চলেছিল, যা বাগ মন্তব্যগুলি বুঝতে সহজ করে তোলে। যাইহোক, আমি মনে করি তারা পূর্ববর্তী ক্ষেত্রে কিছু ভুল করেছে (বিশেষত ধরে নেওয়া লোকেরা 2to3লাইব্রেরির সাহায্যে দ্বৈত সংস্করণ কোডের পরিবর্তে মাইগ্রেশন করবে বলে মনে হয় six, যার কারণেই আমরা dict.viewkeysএমন জিনিস পেয়েছি যে কেউ কখনও ব্যবহার করবে না), এবং সেখানে ছিল কিছু পরিবর্তন যা সবেমাত্র 3.2 এ খুব দেরিতে এসেছিল, তবে বেশিরভাগ অংশের জন্য 2.7 ছিল একটি দুর্দান্ত চিত্তাকর্ষক "সর্বশেষ 2.x সর্বদা" মুক্তি।

47

অন্যান্য উত্তরগুলি ইতিমধ্যে এটি ভালভাবে ব্যাখ্যা করেছে, তবে আমি ব্যাপ্তির অবজেক্টের প্রকৃতির চিত্রিত করে আরও একটি পরীক্ষা প্রস্তাব করতে চাই:

>>> r = range(5)
>>> for i in r:
        print(i, 2 in r, list(r))

0 True [0, 1, 2, 3, 4]
1 True [0, 1, 2, 3, 4]
2 True [0, 1, 2, 3, 4]
3 True [0, 1, 2, 3, 4]
4 True [0, 1, 2, 3, 4]

আপনি দেখতে পাচ্ছেন, একটি রেঞ্জ অবজেক্ট এমন একটি বস্তু যা তার পরিসীমা মনে রাখে এবং এটি কেবলমাত্র এক সময়ের জেনারেটর নয়, বহুবার ব্যবহার করা যেতে পারে।


27

এটা একটা সম্পর্কে সব অলস পদ্ধতির মূল্যায়ন এবং কিছু অতিরিক্ত অপ্টিমাইজেশান এর range। অতিরিক্ত ব্যবহারের জন্য, বা আরও অপ্টিমাইজেশনের কারণে আরও বাস্তবের আগ পর্যন্ত রেঞ্জের মানগুলি গণনা করা দরকার না।

যাইহোক, আপনার পূর্ণসংখ্যা এত বড় নয়, বিবেচনা করুন sys.maxsize

sys.maxsize in range(sys.maxsize) বেশ দ্রুত

অপ্টিমাইজেশনের কারণে - প্রদত্ত পূর্ণসংখ্যাকে কেবল সর্বনিম্ন এবং সর্বাধিক ব্যাপ্তির সাথে তুলনা করা সহজ।

কিন্তু:

Decimal(sys.maxsize) in range(sys.maxsize) বেশ ধীর

(এক্ষেত্রে কোনও অপ্টিমাইজেশন নেই range, তাই পাইথন যদি অপ্রত্যাশিত দশমিক প্রাপ্ত হয় তবে পাইথন সমস্ত সংখ্যার তুলনা করবে)

আপনার বাস্তবায়নের বিশদ সম্পর্কে সচেতন হওয়া উচিত তবে নির্ভর করা উচিত নয়, কারণ এটি ভবিষ্যতে পরিবর্তিত হতে পারে।


4
ভাসমান বড় পূর্ণসংখ্যক সাবধানতা অবলম্বন করুন। বেশিরভাগ মেশিনে, float(sys.maxsize) != sys.maxsize)যদিও sys.maxsize-float(sys.maxsize) == 0
হোল্ডেনওয়েব

18

টি এল; ডিআর

প্রত্যাবর্তিত বস্তু range()আসলে একটি rangeবস্তু। এই বস্তুটি পুনরুক্তি ইন্টারফেস প্রয়োগ করে যাতে আপনি এর উত্পাদক, তালিকা, বা tuple এর মতো ক্রমানুসারে এর মানগুলি পুনরাবৃত্তি করতে পারেন।

কিন্তু এটা এছাড়াও প্রয়োগ __contains__ইন্টারফেস যা আসলে যখন একটি বস্তুর ডান দিকে প্রদর্শিত কি বলা পরার inঅপারেটর। __contains__()একটি পদ্ধতি আয় boolথাকুক বা না থাকুক বাম প্রান্তের উপর আইটেমের inবস্তুর হয়। যেহেতু rangeবস্তুগুলি তাদের সীমানা এবং গতি জানে তাই ও (1) এ এটি প্রয়োগ করা খুব সহজ।


0
  1. অপ্টিমাইজেশনের কারণে, প্রদত্ত পূর্ণসংখ্যাগুলি কেবল ন্যূনতম এবং সর্বাধিক সীমার সাথে তুলনা করা খুব সহজ।
  2. কারণ পরিসীমা () ফাংশন Python3 মধ্যে এত দ্রুত যে আমরা এখানে সীমার জন্য গাণিতিক যুক্তি ব্যবহার করি বরং পরিসীমা বস্তুর সরাসরি পুনরাবৃত্তির চেয়ে।
  3. সুতরাং এখানে যুক্তি ব্যাখ্যা করার জন্য:
    • নম্বরটি শুরু এবং থামার মধ্যে রয়েছে কিনা তা পরীক্ষা করে দেখুন।
    • পদক্ষেপের যথার্থ মানটি আমাদের সংখ্যার চেয়ে বেশি না যায় কিনা তা পরীক্ষা করে দেখুন।
  4. একটি উদাহরণ বিবেচনা করুন, 997 পরিসীমা (4, 1000, 3) কারণ:

    4 <= 997 < 1000, and (997 - 4) % 3 == 0.


1
আপনি কি এর জন্য উত্স ভাগ করতে পারেন? এমনকি যদি এটি আইনী মনে হয় তবে প্রকৃত কোড দ্বারা এই দাবিগুলি সমর্থন করা ভাল হবে
নিকো হাজে

আমি মনে করি এটি বাস্তবায়িত হতে পারে এটির একটি উদাহরণ। এটি কার্যকরভাবে কার্যকর করা হয় না। যদিও কোনও রেফারেন্স সরবরাহ করা ভাল ইঙ্গিতটি যথেষ্ট ভাল তা বোঝার জন্য কেন রেঞ্জের জন্য অন্তর্ভুক্তি পরীক্ষা করা তালিকা বা টিপলের চেয়ে আরও দ্রুত হতে পারে
মোহাম্মদ শরীফ সি

0

x-1 in (i for i in range(x))বৃহত্তর xমানগুলির জন্য চেষ্টা করুন , যা range.__contains__অপ্টিমাইজেশানটি এড়ানোর জন্য একটি জেনারেটর উপলব্ধি ব্যবহার করে ।

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