පයිතන් 3 හි “1000000000000000 (1000000000000001)” එතරම් වේගවත් වන්නේ ඇයි?


2141

range()ඇත්ත වශයෙන්ම පයිතන් 3 හි වස්තු වර්ගයක් වන ශ්‍රිතය උත්පාදක යන්ත්‍රයකට සමාන මැස්සන් මත එහි අන්තර්ගතය ජනනය කරන බව මගේ වැටහීමයි .

තත්වය එසේ නම්, පහත දැක්වෙන රේඛාවට අනවශ්‍ය කාලයක් ගතවනු ඇතැයි මම අපේක්ෂා කළෙමි, මන්ද චතුරස්රාකාර 1 ක් පරාසයේ තිබේද යන්න තීරණය කිරීම සඳහා, චතුරස්රාකාර අගයන් ජනනය කළ යුතු බැවිනි:

1000000000000000 in range(1000000000000001)

තව දුරටත්: මම කොපමණ ශුන්‍ය එකතු කළත්, ගණනය කිරීම වැඩි හෝ අඩු වශයෙන් එකම කාලයක් ගතවන බව පෙනේ (මූලික වශයෙන් ක්ෂණික).

මමත් මේ වගේ දේවල් අත්හදා බැලුවා, නමුත් ගණනය කිරීම තවමත් ක්ෂණිකයි:

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

මම මගේ පරාසයේ ශ්‍රිතය ක්‍රියාත්මක කිරීමට උත්සාහ කළහොත්, ප්‍රති result ලය එතරම් යහපත් නොවේ !!

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

මොකක්ද මේ range()එය එසේ වේගයෙන් කරවන ඒ අවටනම් යටතේ කරන්නේ වස්තුව?


මාර්ටින් පීටර්ස්ගේ පිළිතුර එහි සම්පූර්ණත්වය සඳහා තෝරාගෙන ඇති අතර, පයිතන් 3 හි පූර්ණ අනුක්‍රමයක් වීම යන්නෙන් අදහස් කරන්නේ කුමක්ද යන්න පිළිබඳ හොඳ සාකච්ඡාවක් සඳහා ඇබර්නර්ට්ගේ පළමු පිළිතුර ද බලන්න , සහ පයිතන් ක්‍රියාත්මක කිරීම් හරහා ක්‍රියාකාරී ප්‍රශස්තිකරණය සඳහා ඇති විය හැකි නොගැලපීම් පිළිබඳ සමහර තොරතුරු / අනතුරු ඇඟවීම් ද බලන්න. . abarnert ගේ අනෙක් පිළිතුර තවත් සවිස්තරාත්මකව ගොස් පයිතන් 3 හි ප්‍රශස්තිකරණය පිටුපස ඇති ඉතිහාසය ගැන උනන්දුවක් දක්වන අයට සබැඳි සපයයි (සහ පයිතන් 2 හි ප්‍රශස්තිකරණය නොමැති වීම ). පිළිතුරු පැම්ණ විසින් හා wim විසින් කැමැත්තක් දක්වන අය සඳහා අදාළ සී මූල කේතය හා පැහැදිලි කිරීම් ලබා දීම.range__contains__xrange


72
මෙය සිදුවන්නේ අප පරික්ෂා කරන අයිතමය a boolහෝ longවර්ගයක් නම් පමණි, වෙනත් වස්තු වර්ග සමඟ එය පිස්සු වැටෙනු ඇත. උත්සාහ කරන්න:100000000000000.0 in range(1000000000000001)
අශ්වින් චෞද්රි

10
rangeජනක යන්ත්‍රයක් යැයි ඔබට කීවේ කවුද ?
abarnert

7
ar බාර්නර්ට් මම හිතන්නේ මම කළ සංස්කරණය ව්‍යාකූලත්වයට පත්වී ඇත.
රික් මොනිකාට සහාය දක්වයි


29
Up සුපර්බෙස්ට් xrange()වස්තූන්ට __contains__ක්‍රමයක් නැත , එබැවින් අයිතම පරීක්ෂාවට සියලු අයිතම හරහා ලූප විය යුතුය. ප්ලස් range()කැපීමට සහය දක්වයි (එය නැවත rangeවස්තුවක් ආපසු ලබා දෙයි ) සහ දැන් ඒබීසී සමඟ අනුකූල වීමට ක්‍රම countසහ indexක්‍රම වැනි තවත් වෙනස්කම් කිහිපයක් තිබේ collections.Sequence.
අශ්වින් චෞද්රි

Answers:


2194

පයිතන් 3 range()වස්තුව ක්ෂණිකව සංඛ්‍යා නිපදවන්නේ නැත; එහි අරමුණ වන්නේ ඒ ස්මාර්ට් අනුක්රමය වස්තුව අංක නිෂ්පාදනය කරන ඉල්ලීම මත . එහි අඩංගු වන්නේ ඔබේ ආරම්භය, නැවතුම සහ පියවර අගයන් පමණි, ඉන්පසු ඔබ වස්තුව හරහා නැවත ගමන් කරන විට ඊළඟ නිඛිල ගණනය කරනු ලබන්නේ එක් එක් පුනරාවර්තනයයි.

වස්තුව ද ක්රියාත්මක object.__contains__කොක්කෙන් හා ගණනය ඔබගේ අංකය එහි පරාසය කොටසක් නම්. ගණනය කිරීම යනු (ආසන්න) නියත කාල මෙහෙයුමකි * . පරාසය තුළ ඇති විය හැකි සියලු සංඛ්‍යා හරහා පරිලෝකනය කිරීමේ අවශ්‍යතාවයක් කිසි විටෙකත් නැත.

සිට range()වස්තුව ප්රලේඛනය :

නිත්‍යයකට rangeවඩා වර්ගයේ වාසිය listහෝ tupleපරාසය වස්තුවක් නිරූපණය කරන පරාසයේ ප්‍රමාණය කුමක් වුවත් සෑම විටම එකම (කුඩා) මතක ප්‍රමාණයක් ගනී (එය ගබඩා කරන්නේ start, stopසහ stepඅගයන් පමණක් වන නිසා , තනි අයිතම සහ උපසිරැසි ගණනය කිරීම) අවශ්‍ය පරිදි).

එබැවින් අවම වශයෙන්, ඔබේ 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), ගැලපීමක් තිබේදැයි බැලීමට මන්දගාමී ස්කෑන් පරීක්ෂණයක් ආරම්භ කරනු ලැබේ, ඔබ අඩංගු සියලු අගයන් ලැයිස්තුවකට එරෙහිව බහාලුම් පරීක්ෂණයක් භාවිතා කරනවා සේ. මෙය සිදු කරනු ලැබුවේ පූර්ණ සංඛ්‍යා සමඟ සමානාත්මතා පරීක්‍ෂණයට සහය දැක්වීමට පමණක් සිදු වන නමුත් සංඛ්‍යා සංඛ්‍යා ගණිතයට සහය දැක්වීමට අපේක්‍ෂා නොකරන වෙනත් සංඛ්‍යාත්මක වර්ග සඳහා අඛණ්ඩව සහාය දැක්වීම සඳහා ය. බහාලුම් පරීක්ෂණය ක්‍රියාත්මක කළ මුල් පයිතන් නිකුතුව බලන්න .


* ළඟ නිරන්තර කාලය Python නිඛිල ගොඩගසමින් හා ගණිත මෙහෙයුම් ද මේ සා (ලොග් N) මෙහෙයුම් කරමින්, එන් වර්ධනය වන විට, කාලය තුළ වර්ධනය වූ නිසා. මේ සියල්ල ප්‍රශස්තිකරණය කරන ලද සී කේතයෙන් ක්‍රියාත්මක වන අතර පයිතන් පූර්ණ සංඛ්‍යා අගයන් බිටු 30 කින් ගබඩා කර ඇති හෙයින්, මෙහි ඇති පූර්ණ සංඛ්‍යා වල විශාලත්වය හේතුවෙන් කිසියම් ක්‍රියාකාරීත්ව බලපෑමක් දැකීමට පෙර ඔබට මතක ශක්තිය නැති වී යයි.


67
විනෝද ඇත්ත: ඔබ වැඩකරන ක්රියාත්මක කිරීම නිසා __getitem__සහ __len__එම __iter__ක්රියාත්මක කිරීම ඇත්තෙන්ම අනවශ්ය වේ.
ලුක්‍රෙටියල්

2
Uc ලුක්‍රේටියල්: පයිතන් 2.3 හි විශේෂයක් xrangeiteratorවිශේෂයෙන් එකතු කරන ලද්දේ එය ප්‍රමාණවත් නොවන බැවිනි. 3.x හි කොතැනක හෝ (එය 3.0 හෝ 3.2 දැයි මට විශ්වාස නැත) එය විසි කරන ලද අතර ඔවුන් භාවිතා කරන එකම listiteratorවර්ගය listභාවිතා කරයි.
abarnert

1
මම ඉදිකිරීම්කරු ලෙස අර්ථ def __init__(self, *start_stop_step)දක්වා එය එතැනින් විග්‍රහ කරමි. දැන් තර්ක ලේබල් කර ඇති ආකාරය අවුල් සහගත ය. එසේ වුවද, +1; ඔබ තවමත් අනිවාර්යයෙන්ම හැසිරීම පැහැදිලි කර ඇත.
කෝඩි පියර්සාල්

1
Ody කෝඩිපියර්සාල්: අවාසනාවට, එය සැබෑ පන්තියේ ආරම්භකයාගේ අත්සනයි. rangeවඩා පැරණි *args( argclinicසී-ඒපීඅයි කාර්යයන් සඳහා සම්පූර්ණ පයිතන් අත්සන් තිබිය හැකි ඒපීඅයි ) වඩා අඩුය . තවත් පැරණි කාර්යයන් කිහිපයක් (සහ නව කාර්යයන් කිහිපයක්, වැනි xrange, sliceසහ itertools.islice, අනුකූලතාව සඳහා) එකම ආකාරයකින් ක්‍රියා කරයි, නමුත් බොහෝ දුරට, ගයිඩෝ සහ සෙසු ප්‍රධාන දේව්ස් ඔබ සමඟ එකඟ වන බව පෙනේ. 2.0+ ලියකියවිලි පවා විස්තර කරන rangeඅතර මිතුරන් සැබෑ ව්‍යාකූල අත්සන පෙන්වීමට වඩා C ++ විලාසිතාවේ බර පැටවීමක් සේ පෙනේ.
abarnert

2
Ody කෝඩිපියර්සාල්: ඇත්ත වශයෙන්ම, ගයිඩෝ argclinicසාකච්ඡාවෙන් උපුටා දැක්වීමක් , නික් කොග්ලන් නිසැකවම නිර්වචනය කිරීමට ඉඩ සලසන ක්‍රමයක් ඉදිරිපත් කළ විට range: "කරුණාකර මගේ නරකම නිර්මාණ තීරණය පිටපත් කිරීමට මිනිසුන්ට පහසු නොකරන්න." ඉතින්, මට විශ්වාසයි ඔහු rangeලියා ඇති පරිදි එය අවුල් සහගත බව එකඟ වේ.
abarnert

852

මෙහි මූලික වරදවා වටහා rangeගැනීම ජනක යන්ත්‍රයක් යැයි සිතීමයි. එය නෙමෙයි. ඇත්ත වශයෙන්ම, එය කිසිදු ආකාරයක අනුකාරකයක් නොවේ.

ඔබට මෙය ඉතා පහසුවෙන් පැවසිය හැකිය:

>>> 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))බව ඔබට පෙනෙනු ඇත . එය ක්‍රියා කරන්නේ කෙසේද? එය සී ක්‍රියාත්මක කිරීමක් සපයන බව හැරෙන්නට විශේෂ කිසිවක් භාවිතා නොකරයි , එබැවින් එය හොඳින් ක්‍රියාත්මක වේ ද.)rangelistiteratorlistlistiteratorlist__getitem__range


දැන්, Sequence.__contains__නියත වේලාවක් තිබිය යුතු යැයි පවසන කිසිවක් නැත fact ඇත්ත වශයෙන්ම, වැනි අනුක්‍රමයන් සඳහා පැහැදිලි උදාහරණ සඳහා list, එසේ නොවේ. නමුත් එය විය නොහැකි යැයි පවසන කිසිවක් නැත . සියලු අගයන් සැබවින්ම උත්පාදනය කර පරීක්ෂා කිරීමට වඩා range.__contains__ගණිතමය වශයෙන් ( (val - start) % stepනමුත් negative ණාත්මක පියවර සමඟ කටයුතු කිරීමට අමතර සංකීර්ණතාවයකින්) එය ක්‍රියාත්මක කිරීම පහසුය , එබැවින් එය වඩා හොඳ ආකාරයෙන් නොකළ යුත්තේ ඇයි ?

නමුත් මෙය සිදුවනු ඇති බවට සහතික වන කිසිවක් භාෂාවේ ඇති බවක් නොපෙනේ . අශ්වින් චෞද්රි පෙන්වා දෙන පරිදි, ඔබ එය අනුකලනය නොවන අගයක් ලබා දෙන්නේ නම්, එය පූර්ණ සංඛ්‍යාවක් බවට පරිවර්තනය කර ගණිතමය පරීක්‍ෂණයක් කරනවා වෙනුවට, එය සියලු අගයන් නැවත සැකසීමට හා ඒවා එකින් එක සංසන්දනය කිරීමට නැවත වැටෙනු ඇත. CPython 3.2+ සහ PyPy 3.x අනුවාදයන් මෙම ප්‍රශස්තිකරණය අඩංගු වන නිසාත්, එය පැහැදිලිවම හොඳ අදහසක් සහ පහසුවෙන් කළ හැකි නිසාත්, IronPython හෝ NewKickAssPython 3.x හට එය අතහැර දැමීමට කිසිදු හේතුවක් නැත. (ඇත්ත වශයෙන්ම CPython 3.0-3.1 එයට ඇතුළත් කර නැත.)


නම් rangeඇත්තටම ජනකය විය, වැනි my_crappy_range, එය හැඟීමක් පරීක්ෂා කිරීමට පත් නොවන __contains__මේ ආකාරයට, හෝ එය හැඟීමක් පැහැදිලි වන්නේ නැහැ කරයි අවම වශයෙන් ක්රමයක්. ඔබ දැනටමත් පළමු අගයන් 3 නැවත කියවා ඇත්නම්, 1තවමත් inඋත්පාදක යන්ත්රයද? (හෝ පළමු අගය දක්වා ) 1දක්වා සියලු අගයන් නැවත සැකසීමට හා පරිභෝජනය කිරීමට හේතුව පරීක්ෂා කිරීම කළ යුතුද ?1>= 1


11
මෙය කෙළින්ම ලබා ගැනීම සඳහා ඉතා වැදගත් දෙයකි. පයිතන් 2 සහ 3 අතර ඇති වෙනස්කම් මෙම කාරණය පිළිබඳ මගේ ව්‍යාකූලත්වයට හේතු වන්නට ඇතැයි මම සිතමි. කෙසේ වෙතත්, අනුක්‍රමික වර්ගයක් ලෙස (සමග සහ ) ලැයිස්තුගත කර ඇති බැවින්rangelisttuple මම තේරුම් ගත යුතුව තිබුණි .
රික් මොනිකා

4
IckRickTeachey: ඇත්ත වශයෙන්ම, 2.6+ දී (මම හිතන්නේ; සමහර විට 2.5+), xrangeඅනුක්‍රමයක් ද වේ. ලියකියවිලි 2.7 බලන්න . ඇත්ත වශයෙන්ම, එය සෑම විටම පාහේ අනුක්රමයක් විය.
abarnert

5
Ick රික් ටීචි: ඇත්තටම මම වැරදියි; 2.6-2.7 (සහ 3.0-3.1) හි එය අනුක්‍රමයක් යැයි කියා සිටියත් එය තවමත් අනුක්‍රමයක් පමණයි. මගේ අනෙක් පිළිතුර බලන්න.
abarnert

2
එය අනුකාරකයක් නොවේ, එය අනුක්‍රමයකි (ජාවා අනුව වෙනස් කළ හැකිය, C # හි IEnumerable) - .__iter__()එය නැවත ක්‍රියාකරවන්නෙකු ආපසු ලබා දෙන ක්‍රමයක් සහිත දෙයක් . එය අනෙක් අතට භාවිතා කළ හැක්කේ එක් වරක් පමණි.
ස්මිත් ජොන්ට්

5
HThomasAhle: මක්නිසාද යත් rangeඑය පූර්ණ සංඛ්‍යාවක් නොවන විට වර්ග පරීක්ෂා නොකිරීමයි. මන්දයත් සෑම විටම වර්ගයකට __eq__අනුකූල වන වර්ගයක් තිබිය හැකි intබැවිනි. නිසැකවම, ක්‍රියා strනොකරනු ඇත, නමුත් එහි තිබිය නොහැකි සියලු වර්ගයන් පැහැදිලිව පරික්ෂා කිරීමෙන් දේවල් මන්දගාමී කිරීමට ඔවුන්ට අවශ්‍ය නොවීය (සියල්ලට පසු, strඋප පංතියක් අභිබවා __eq__ගොස් එහි අඩංගු විය හැකිය range).
ෂැඩෝ රේන්ජර්

382

මූලාශ්රය භාවිතා කරන්න , ලූක්!

CPython හි, 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)

146

මාර්ටිජන්ගේ පිළිතුරට එකතු කිරීම සඳහා, මෙය ප්‍රභවයේ අදාළ කොටසයි (සී හි, පරාසයේ වස්තුව ස්වදේශීය කේතයෙන් ලියා ඇති බැවින්):

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_longfunction ලය තීරණය කිරීම සඳහා ශ්‍රිතය භාවිතා කරයි . එම ශ්‍රිතය obනිශ්චිතවම නිශ්චිත පරාසයක තිබේදැයි පරීක්ෂා කරයි (එය C හි තරමක් සංකීර්ණ බවක් පෙනුනද).

එය 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
H ක්‍රිස්වෙස්ලිං: මාටිජන්ගේ පිළිතුර සංස්කරණය කිරීම මෙහි සුදුසු නොවනු ඇතැයි මම සිතමි මෙය වෙනස් තරම් ප්‍රමාණවත් තොරතුරු (සහ එය ප්‍රමාණවත්). එය විනිශ්චය කැඳවීමකි, නමුත් සාමාන්‍යයෙන් මිනිසුන් වැරදි වන්නේ අනෙක් අයගේ පිළිතුරු වල විශාල වෙනස්කම් නොකිරීමයි.
abarnert

108

ඔබ සිතන්නේ නම් මෙම ප්‍රශස්තිකරණය එකතු කළේ ඇයි කියාrange.__contains__ , එය ඇයි නැත එකතු xrange.__contains__2.7 දී:

පළමුව, අශ්වින් චෞද්රි සොයාගත් පරිදි, 1766304 නිකුතුව ප්‍රශස්ත කිරීම සඳහා පැහැදිලිවම විවෘත කරන ලදී [x]range.__contains__. මේ සඳහා පැච් එකක් පිළිගෙන 3.2 සඳහා පරීක්ෂා කර ඇත , නමුත් 2.7 වෙත ආපසු යවා නැත, මන්ද "xrange මෙතරම් දීර් for කාලයක් තිස්සේ මේ ආකාරයට හැසිරී ඇති නිසා මෙම ප්‍රමාදයට පැච් එක කිරීමට අප මිලදී ගන්නේ කුමක් දැයි මට නොපෙනේ." (එම අවස්ථාවේදී 2.7 ආසන්න විය.)

මේ අතර:

මුලදී, xrangeතරමක් අනුක්‍රමික නොවන වස්තුවකි. ලෙස ද ලේඛන 3.1 කියන්න:

පරාස වස්තූන් සතුව ඇත්තේ ඉතා අල්ප හැසිරීමකි: ඒවා සහාය දක්වන්නේ සුචිගත කිරීම, පුනරාවර්තනය සහ lenක්‍රියාකාරීත්වය පමණි.

මෙය සත්‍යයක් නොවේ; ක xrangeවස්තුව ඇත්තටම සූචිගත කිරීම සහ සමග ඉබේම බව වෙනත් දේවල් කිහිපයක් සහයෝගය len, * ඇතුළු __contains__(රේඛීය සෝදිසි ඔස්සේ). නමුත් කිසිවෙකු සිතුවේ ඒවා එකල සම්පූර්ණ අනුපිළිවෙලක් බවට පත් කිරීම වටී.

පසුව, වියුක්ත මූලික පංති PEP ක්‍රියාවට නැංවීමේ කොටසක් ලෙස, කුමන “ඒබීසී” ක්‍රියාවට නංවා ඇත්දැයි සහ xrange/ rangeක්‍රියාත්මක කිරීමට යැයි කියා ගන්නා කුමන බිල්ඩින් වර්ග සලකුණු කළ යුතුද යන්න සොයා බැලීම වැදගත් විය collections.Sequence. 9213 නිකුතුව තෙක් කිසිවෙකු එම ගැටලුව දුටුවේ නැත . ඒ ප්රශ්නය සඳහා ලප එකතු පමණක් නොව indexහා count3.2 ගේ කිරීමට range, එය ද නැවත වැඩ කරන ප්රචලිත කෙරෙන __contains__(සමග එම ගණිත අනුව කටයුතු indexකිරීමෙන් හා සෘජුවම භාවිතා count). ** මෙම වෙනස 3.2 සඳහාද ගිය අතර එය 2.x වෙත ආපසු යවනු ලැබුවේ නැත, මන්ද එය "නව ක්‍රම එකතු කරන දෝෂයකි". (මෙම අවස්ථාවෙහිදී, 2.7 දැනටමත් rc තත්ත්වය පසු කර තිබුණි.)

එබැවින්, මෙම ප්‍රශස්තිකරණය 2.7 දක්වා ආපසු ලබා ගැනීමට අවස්ථා දෙකක් තිබුණද ඒවා දෙකම ප්‍රතික්ෂේප විය.


* ඇත්ත වශයෙන්ම, ඔබට සුචිගත කිරීම සමඟ පමණක් නොමිලේ නැවත ලබා ගත හැක, නමුත් වස්තු 2.3 xrange කින් අභිරුචි අනුකාරකයක් ලැබුණි.

** පළමු අනුවාදය එය නැවත ප්‍රතිනිර්මාණය කළ අතර විස්තර වැරදියි - උදා: එය ඔබට ලබා දෙනු MyIntSubclass(2) in range(5) == Falseඇත. එහෙත් ලප, දානියෙල් Stutzbach ගේ නවීකරණය, මන්දගාමී වර්ගීය කිරීමට ෆෝල්බැක් ඇතුළු පසුගිය කේතය, බොහෝ ප්රතිසංස්කරණය _PySequence_IterSearchපෙර 3.2 බව range.__contains__ද ප්රශස්තිකරණය අදාළ නොවේ විට නිසැකයෙන්ම භාවිතා කරන ලදී.


4
මෙහි ඇති අදහස් වලින්: වැඩි දියුණු කරන්නxrange.__contains__ , ඔවුන් එය පයිතන් 2 වෙත ආපසු නොයැවූ බව පෙනේ, පරිශීලකයින් පුදුමයට පත් කරවන අංගයක් ඉතිරි කිරීම සඳහා එය ප්‍රමාද වැඩියි o_O. පසුව countසහ index පැච් එකතු කරන ලදී. එම අවස්ථාවේ ගොනුව: hg.python.org/cpython/file/d599a3f2e72d/Objects/rangeobject.c
චෞද්රි

12
සමහර පයිතන් ඩෙව්ස් පයිතන් 2.x සඳහා “දැඩි ප්‍රේමයට” පක්ෂපාතී බවට මට සැකයක් තිබේ. මන්ද ඔවුන් මිනිසුන්ට වඩා උසස් පයිතන් 3 වෙත මාරුවීමට ධෛර්යමත් කිරීමට අවශ්‍ය නිසාය :)
wim

4
පැරණි අනුවාද වලට නව විශේෂාංග එකතු කිරීම විශාල බරක් බව මම විශ්වාස කරමි. සිතන්න, ඔබ ඔරකල් වෙත ගොස්, "බලන්න, මම ජාවා 1.4 හි සිටිමි. මම ලැම්බඩා ප්‍රකාශන ලැබීමට සුදුසුය! ඒවා කිසිවක් සඳහා පසුපසට යන්න."
රොබ් ග්‍රාන්ට්

2
IckRickTeachey ඔව් එය උදාහරණයක් පමණි. මම 1.7 කිව්වොත් එය තවමත් අදාළ වේ. එය ගුණාත්මක නොවන ප්‍රමාණාත්මක වෙනසක්. මූලික වශයෙන් (නොගෙවූ) devs හට 3.x හි සදහටම සිසිල් නව දේවල් සෑදිය නොහැකි අතර එය යාවත්කාලීන කිරීමට අකමැති අයට එය 2.x වෙත ආපසු ගෙන යා හැකිය. එය විශාල හාස්‍යජනක බරකි. මගේ තර්කනයේ තවමත් යම් වැරැද්දක් ඇතැයි ඔබ සිතනවාද?
රොබ් ග්‍රාන්ට්

3
IckRickTeachey: 2.7 3.3 ත් 3.2 ත් අතර විය, 3.3 පමණ නොවේ. අවසාන අර්ථය 3.2 වෙත ගිය විට 2.7 rc හි පැවතුන අතර එමඟින් දෝෂ අදහස් තේරුම් ගැනීමට පහසු වේ. කෙසේ වෙතත්, ඔවුන් නැවත සලකා බැලීමේදී වැරදි කිහිපයක් සිදු කර ඇතැයි මම සිතමි (විශේෂයෙන් 2to3පුස්තකාල වැනි පුස්තකාලවල ආධාරයෙන් ද්විත්ව අනුවාද කේතයක් වෙනුවට මිනිසුන් සංක්‍රමණය වනු ඇතැයි උපකල්පනය කරයි, ඒ නිසා කිසිවෙකු මෙතෙක් භාවිතා නොකරන sixදේවල් අපට ලැබුණි dict.viewkeys), සහ 3.2 ට වඩා ප්‍රමාද වූ වෙනස්කම් කිහිපයක්, නමුත් බොහෝ දුරට 2.7 ඉතා ආකර්ෂණීය “අන්තිම 2.x මෙතෙක්” නිකුතුව විය.
abarnert

49

අනෙක් පිළිතුරු දැනටමත් එය හොඳින් පැහැදිලි කර ඇත, නමුත් පරාසයේ වස්තූන්ගේ ස්වභාවය විදහා දක්වන තවත් අත්හදා බැලීමක් කිරීමට මම කැමතියි:

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

ඔබට පෙනෙන පරිදි, පරාස වස්තුවක් යනු එහි පරාසය සිහිපත් කරන වස්තුවක් වන අතර එය එක් වරක් උත්පාදක යන්ත්‍රයක් පමණක් නොව බොහෝ වාරයක් භාවිතා කළ හැකිය.


30

එය ගැන එච්චරයි කම්මැලි ප්රවේශය ඇගයීම සහ සමහර අමතර ප්රශස්තිකරණය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.
holdenweb

20

ටීඑල්; ඩී.ආර්

ආපසු ලබා දුන් වස්තුව range()ඇත්ත වශයෙන්ම arange වස්තුව වස්තුවකි. මෙම වස්තුව iterator අතුරුමුහුණත ක්‍රියාත්මක කරන අතර එමඟින් ඔබට ජනක යන්ත්‍රයක්, ලැයිස්තුවක් හෝ ටුපල් එකක් මෙන් අනුක්‍රමිකව එහි අගයන් නැවත ප්‍රකාශ කළ හැකිය.

නමුත් එය ක්‍රියාකරුගේ දකුණු පැත්තේ වස්තුවක් දර්ශනය වූ විට ඇත්ත වශයෙන්ම හැඳින්වෙන අතුරු මුහුණත ක්‍රියාත්මක කරයි . මෙම ක්‍රමය මඟින් වම්පස වම්පස ඇති අයිතමය වස්තුවෙහි තිබේද නැද්ද යන්න පිළිබඳ විස්තරයක් ලබා දේ. වස්තූන් ඒවායේ සීමාවන් සහ ප්‍රගතිය දන්නා බැවින් මෙය O (1) හි ක්‍රියාත්මක කිරීම ඉතා පහසුය.__contains__in__contains__()boolinrange


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__ප්‍රශස්තිකරණය නොකිරීමට .

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.