Python 3'te neden “1000000000000000 (1000000000000001) aralığında) bu kadar hızlı?


2115

Anladığım kadarıyla range(), aslında Python 3'te bir nesne türü olan fonksiyonun, bir jeneratör gibi, içeriğini anında ürettiği anlaşılıyor .

Bu durumda, aşağıdaki satırın aşırı bir zaman almasını beklerdim, çünkü 1 katrilyonun aralıkta olup olmadığını belirlemek için bir katrilyon değerinin üretilmesi gerekecekti:

1000000000000000 in range(1000000000000001)

Dahası: Kaç tane sıfır eklesem de, hesaplama az ya da çok aynı süreyi alıyor (temelde anlık).

Ben de böyle şeyler denedim, ama hesaplama neredeyse anında:

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

Kendi menzil fonksiyonumu uygulamaya çalışırsam, sonuç o kadar hoş değil !!

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

range()Kaputun altında bu kadar hızlı yapan nesne ne yapıyor?


Martijn Pieters'ın cevabı tamlığı için seçildi, ancak aynı zamanda Python 3'te tam teşekküllü bir sıra olmanın ne anlama geldiğinin iyi bir tartışması için Parhon'un ilk cevabını ve Python uygulamaları arasında fonksiyon optimizasyonu için potansiyel tutarsızlık hakkında bazı bilgileri / uyarıları da görün . abarnert'ın diğer yanıtı biraz daha ayrıntıya giriyor ve Python 3'teki optimizasyonun arkasındaki tarihle ilgilenenlere (ve Python 2'de optimizasyon eksikliğine ) bağlantı sağlıyor. Poke ve wim tarafından verilen cevaplar , ilgili C kaynak kodunu ve ilgilenenler için açıklamaları sağlar.range__contains__xrange


70
Bu durumun yalnızca kontrol ettiğimiz öğenin bir boolveya longtür olması durumunda, diğer nesne türleriyle delireceğini unutmayın. Deneyin:100000000000000.0 in range(1000000000000001)
Ashwini Chaudhary

10
Bunun rangebir jeneratör olduğunu kim söyledi ?
abarnert

7
@abarnert Yaptığım düzenleme karışıklığı olduğu gibi bıraktı.
Rick, Monica'yı

5
@AshwiniChaudhary Python2, Python3 xrangeile aynırange değil mi?
Superbest

28
@Superbest xrange()nesnelerinin __contains__yöntemi yoktur , bu nedenle öğe kontrolü tüm öğeler arasında döngü yapmalıdır . Artı içinde birkaç diğer değişiklikler vardır range()o dilimleme destekler (yine bir döndüren gibi rangevardır da artık nesne) ve countve indexyöntemler ile uyumlu hale getirmek için collections.SequenceABC.
Ashwini Chaudhary

Yanıtlar:


2170

Python 3 range()nesnesi hemen sayı üretmez; Bir olan akıllı dizi nesne sayısı verir isteğe bağlı olarak . İçerdiği tek şey başlangıç, durdurma ve adım değerlerinizdir, daha sonra nesne üzerinde yinelediğinizde her yineleme bir sonraki tamsayı hesaplanır.

Nesne aynı zamanda uygulayan object.__contains__kanca ve hesaplar numaranız yelpazesini parçası ise. Hesaplama (yakın) sabit zaman işlemidir * . Aralıktaki tüm olası tamsayıları taramaya asla gerek yoktur.

Gönderen range()nesne belgeler :

Avantajı rangedüzenli üzerinde türü listveya tuplesadece depolar gibi bir dizi nesne daima (hayır, temsil ettiği aralığın boyutu önemli, belleğin aynı (küçük) miktarını alacak olmasıdır start, stopve stepdeğerleri tek öğeleri ve subranges hesaplanması, ihyaç olduğu gibi).

Yani en azından range()nesneniz şunları yapardı:

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

Bu, gerçek bir range()desteğin ( .index()veya .count()yöntemleri, karma, eşitlik testi veya dilimleme gibi) desteklediği, ancak size bir fikir vermesi gereken bazı şeyleri hala kaçırıyor .

Ayrıca __contains__uygulamayı yalnızca tamsayı testlerine odaklanmak için basitleştirdim ; gerçek bir range()nesneye tamsayı olmayan bir değer (alt sınıfları dahil int) verirseniz, tıpkı içerilen tüm değerlerin listesine karşı bir sınırlama testi kullanıyormuş gibi bir eşleşme olup olmadığını görmek için yavaş bir tarama başlatılır. Bu, tamsayılarla eşitlik testini destekleyen ancak tamsayı aritmetiğini desteklemesi beklenmeyen diğer sayısal türleri desteklemeye devam etmek için yapıldı. Sınırlama testini uygulayan orijinal Python sorununa bakın .


* Civarında Python tamsayılar N bunun Ç hale büyüdükçe matematik işlemleri de zaman içinde büyür böylece operasyonu (N log) ve sınırsız olduğu için sürekli zaman. Her şey optimize edilmiş C kodunda yürütüldüğünden ve Python tamsayı değerlerini 30 bitlik yığınlarda sakladığından, burada yer alan tam sayıların boyutu nedeniyle herhangi bir performans etkisi görmeden önce belleğiniz tükenir.


58
Eğlenceli bir gerçek: Eğer bir çalışan uygulama var çünkü __getitem__ve __len__, __iter__uygulama aslında gereksizdir.
Lucretiel

2
@Lucretiel: Python 2.3'te , xrangeiteratoryeterince hızlı olmadığı için özel bir özellik eklendi. Ve sonra 3.x içinde bir yerde (3.0 ya da 3.2 olup olmadığından emin değilim) atıldı ve kullanan aynı listiteratortürü listkullanıyorlar.
abarnert

1
Yapıcı olarak tanımlayıp def __init__(self, *start_stop_step)oradan ayrıştırırdım; Şimdi argümanların etiketlenme şekli biraz kafa karıştırıcı. Yine de +1; yine de kesinlikle davranışı açıkladınız.
Cody Piersall

1
@CodyPiersall: Maalesef gerçek sınıfın başlatıcısının imzası bu. rangedaha eski *args( argclinicC-API işlevlerinin eksiksiz Python imzalarına sahip olmasını sağlayan API). Birkaç diğer eski fonksiyonlar (ve birkaç yeni fonksiyonlar gibi xrange, sliceve itertools.islice, tutarlılık için) çalışması aynı şekilde ancak çoğunlukla, Guido ve çekirdek devs kalanı sizinle hemfikir görünüyor. 2.0+ dokümanlar range, gerçek kafa karıştırıcı imzayı göstermek yerine, C ++ tarzı aşırı yüklermiş gibi ve arkadaşlarını bile tanımlar .
abarnert

2
@CodyPiersall: Aslında, Guido'dan Nick Coghlan'ın açık argclinicbir şekilde tanımlamaya izin vermenin bir yolunu bulduğu bir alıntı range: "Lütfen insanların en kötü tasarım kararımı kopyalamasını kolaylaştırma." Yani, eminim ki rangeyazdığı gibi kafa karıştırıcı olduğunu kabul ediyor .
abarnert

844

Buradaki temel yanlış anlama, rangebunun bir jeneratör olduğunu düşünmektir . Değil. Aslında, herhangi bir yineleyici değil.

Bunu kolayca söyleyebilirsin:

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

Bir jeneratör olsaydı, bir kez tekrarlamak onu tüketirdi:

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

Ne rangeaslında, sadece bir listesi gibi bir dizisidir. Bunu bile test edebilirsiniz:

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

Bu, bir sekans olmanın tüm kurallarına uyması gerektiği anlamına gelir:

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

Bir arasındaki fark rangeve listbir yani rangea, yavaş ya da dinamik sekansı; onun tüm değerleri hatırlamaz, sadece onun hatırlar start, stopve step, ve üzerinde talep üzerine değerlerini oluşturur __getitem__.

(Bir yan not olarak, siz print(iter(a))fark ederseniz , bununla rangeaynı listiteratortipte olduğunu fark edeceksiniz list. Bu nasıl çalışır? A listiterator, listC uygulaması sağlaması dışında özel bir şey kullanmaz __getitem__, bu nedenle rangeçok.)


Şimdi, bunun Sequence.__contains__sabit bir zaman olması gerektiğini söyleyen hiçbir şey yok - aslında, gibi dizilerin açık örnekleri için list, öyle değil. Ama olamayacağını söyleyen hiçbir şey yok . Ve bu, uygulanması daha kolay range.__contains__sadece matematiksel olarak kontrol etmek ( (val - start) % stepfakat bazı ekstra karmaşıklığı ile negatif adımlarla başa) aslında daha oluşturmak ve, neden tüm değerleri sınamak olmamalı buna iyi bir yol değil mi?

Ancak, dilde bunun olacağını garanti eden hiçbir şey yok gibi görünüyor . Ashwini Chaudhari'nin belirttiği gibi, eğer tamsayıya dönüştürmek ve matematiksel testi yapmak yerine, integral olmayan bir değer verirseniz, tüm değerleri yinelemeye ve tek tek karşılaştırmaya geri döner. Ve sadece CPython 3.2+ ve PyPy 3.x sürümleri bu optimizasyonu içerdiğinden ve açık bir iyi fikir ve yapılması kolay olduğundan, IronPython veya NewKickAssPython 3.x'in bunu dışarıda bırakmamasının bir nedeni yoktur. (Ve aslında CPython 3.0-3.1 içermedi .)


Eğer rangeaslında bir jeneratör vardı gibi my_crappy_range, o zaman test etmek mantıklı olmaz __contains__en azından anlamda bariz olmaz kılan yolu bu şekilde ya. İlk 3 değeri zaten yinelediyseniz, 1hala injeneratör mü? Bunun için testler 1yinelemeli ve tüm değerleri 1(veya ilk değere kadar >= 1) tüketmeli mi?


10
Doğruyu yapmak oldukça önemli bir şey. Sanırım Python 2 ve 3 arasındaki farklar bu noktada kafa karışıklığına yol açmış olabilir. Her durumda, bir dizi türü olarak ( ve ile birlikte ) listelendiğinden berirangelisttuple fark etmiş olmalıydım .
Rick,

4
@RickTeachey: Aslında, 2.6+ (sanırım; belki 2.5+) xrangede bir dizidir. Bkz. 2.7 doküman . Aslında, her zaman neredeyse bir sekanstı.
abarnert

5
@ RickTeachey: Aslında yanılmışım; 2.6-2.7 (ve 3.0-3.1) 'de, bir sekans olduğunu iddia eder, ancak yine de neredeyse bir sekanstır. Diğer cevabımı gör.
abarnert

2
Bir yineleyici değil, bir dizi (Java açısından yinelenebilir, C # IEnumerable) # .__iter__()yineleyici döndürecek bir yöntem ile bir şey . Sırayla sadece bir kez kullanılabilir.
Smit Johnth

4
@ThomasAhle: Çünkü rangebir tamsayı olmadığında türleri kontrol etmez, çünkü her zaman bir tipin __eq__uyumlu bir tipine sahip olması mümkündür int. Tabii, strtabii ki iş olmaz, ama onlar aşağı açıkça her türlü kontrol ederek yavaş şeyler istemedim olamaz orada olmak (ve sonuçta bir stralt sınıf geçersiz olabilir __eq__ve içerdiği edilebilir range).
ShadowRanger

377

Kaynağı kullan , Luke!

CPython'da range(...).__contains__(yöntem sarmalayıcısı) sonunda değerin aralıkta olup olmadığını kontrol eden basit bir hesaplamaya delege eder. Buradaki hızın nedeni , aralık nesnesinin doğrudan yinelemesinden ziyade sınırlar hakkında matematiksel akıl yürütme kullanmaktır . Kullanılan mantığı açıklamak için:

  1. Sayı arasında olduğunu kontrol edin startve stopve
  2. Adım değerinin numaramızı "aşmadığını" kontrol edin.

Örneğin, 994içinde olduğu range(4, 1000, 2), çünkü:

  1. 4 <= 994 < 1000, ve
  2. (994 - 4) % 2 == 0.

Tam C kodu, bellek yönetimi ve referans sayma ayrıntıları nedeniyle biraz daha ayrıntılı olan aşağıda yer almaktadır, ancak temel fikir oradadır:

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);
}

Fikrin "eti" satırında belirtilmiştir :

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

Son bir not olarak - range_containskod snippet'inin altındaki işleve bakın . Kesin tip kontrolü başarısız olursa, açıklanan akıllı algoritmayı kullanmayız, bunun yerine aralıktaki aptal yineleme aramasına geri döneriz _PySequence_IterSearch! Bu davranışı yorumlayıcıda kontrol edebilirsiniz (burada v3.5.0 kullanıyorum):

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

Martijn'ın cevabına eklemek için bu, kaynağın ilgili kısmıdır ( aralık nesnesi yerel kodda yazıldığı için C dilinde):

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);
}

Bu nedenle PyLong( intPython 3'teki) nesneler için range_contains_long, sonucu belirlemek üzere işlevi kullanır. Ve bu fonksiyon esas obolarak belirtilen aralıkta olup olmadığını kontrol eder (C'de biraz daha karmaşık görünse de).

Bir intnesne değilse , değeri bulana (ya da bulamayana) kadar yinelemeye geri döner.

Bütün mantık şu şekilde sözde Python'a çevrilebilir:

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
@ChrisWesseling: Sanırım bu, Martijn'ın cevabını düzenlemenin burada uygun olmayacağı kadar farklı (ve yeterli) bir bilgi. Bu bir yargılama çağrısıdır, ancak insanlar genellikle diğer insanların cevaplarında ciddi değişiklikler yapmamakta yanılırlar.
abarnert

105

Eğer merak ediyorsanız neden bu optimizasyon eklendi range.__contains__ve neden değildi eklendi xrange.__contains__2.7'de:

İlk olarak, Ashwini Chaudhary'in keşfettiği gibi, sayı 1766304 açıkça optimize etmek için açıldı [x]range.__contains__. Bunun için bir yama 3.2 için kabul edildi ve teslim edildi , ancak 2.7'ye geri bildirilmedi çünkü "xrange öyle uzun bir süre böyle davrandı, yamayı bu kadar geç yapmak için bize ne satın aldığını göremiyorum." (2.7 o noktada neredeyse tamamen dışarıdaydı.)

O esnada:

Başlangıçta, xrangeoldukça sıralı olmayan bir nesneydi. As 3.1 dokümanlar ki:

Aralık nesnelerinin çok az davranışı vardır: yalnızca dizin oluşturma, yineleme ve lenişlevi destekler.

Bu tam olarak doğru değildi; Bir xrangenesne aslında indeksleme ve otomatik gelip birkaç şey desteklenen len, * dahil __contains__(doğrusal arama yoluyla). Ama kimse o sırada onları tam diziler haline getirmeye değeceğini düşünmedi.

Daha sonra, Soyut Temel Sınıflar PEP'in uygulanmasının bir parçası olarak, sadece aynı "çok az davranışı" ele almasına rağmen hangi yerleşik türlerin hangi ABC'lerin uygulanması olarak işaretlenmesi ve xrange/ rangeveya uygulanması gerektiğini iddia etmesi collections.Sequenceönemlidir. Sayı 9213'e kadar kimse bu sorunu fark etmedi . O sorunla ilgili yama sadece eklenen indexve count3.2 giden tarihiyle range, aynı zamanda optimize-çalışmış yeniden __contains__(ile hangi hisseleri aynı matematik indexve doğrudan tarafından kullanılır count). ** Bu değişiklik 3.2'de de gerçekleşti ve "yeni yöntemler ekleyen bir hata düzeltmesi" olduğu için 2.x'e aktarılmadı. (Bu noktada, 2.7 zaten rc statüsünü geçmişti.)

Bu nedenle, bu optimizasyonu 2.7'ye geri döndürmek için iki şans vardı, ancak her ikisi de reddedildi.


* Aslında, sadece indeksleme ile ücretsiz yineleme elde edersiniz, ancak 2.3 xrange nesnede özel bir yineleyici var.

** İlk sürüm aslında yeniden uyguladı ve ayrıntıları yanlış anladı - örneğin size verecekti MyIntSubclass(2) in range(5) == False. Ancak Daniel Stutzbach'ın yamanın güncellenmiş sürümü, önceki kodun çoğunu geri yükledi, genel jenerasyona geri dönüş de dahil olmak üzere , optimizasyon uygulanmadığında _PySequence_IterSearch3.2 öncesi range.__contains__örtük olarak kullanılan yavaş .


4
Buradaki yorumlardan: geliştirinxrange.__contains__ , sadece kullanıcılar için bir sürpriz öğesi bırakmak için Python 2'ye destek vermediler ve çok geç oldu. countVe index yama sonradan eklenmiştir. Dosya şu anda: hg.python.org/cpython/file/d599a3f2e72d/Objects/rangeobject.c
Ashwini Chaudhary

12
Ben bazı çekirdek python devs python 2.x için "zor aşk" için kısmi olduğundan şüpheli bir şüphem var çünkü onlar insanlar çok üstün python3 geçmek için teşvik etmek istiyorum :)
wim

4
Ayrıca eski sürümlere yeni özellikler eklemek için büyük bir yük olduğuna bahse girerim. Oracle'a gidip "Bak, Java 1.4 kullanıyorum ve lambda ifadelerini hak ediyorum! Onları hiçbir şey için geri ver."
Rob Grant

2
@ RickTeachey evet bu sadece bir örnek. Eğer 1.7 dersem yine de geçerlidir. Bu niteliksel olmayan nicel bir farktır. Temel olarak (ücretsiz) geliştiriciler sonsuza kadar 3.x'te yeni şeyler yapamaz ve yükseltmek istemeyenler için 2.x'e geri gönderemez. Bu çok büyük ve saçma bir yük. Sence akıl yürütmemde hala bir sorun var mı?
Rob Grant

3
@RickTeachey: 2.7, 3.3 değil 3.2 ve 3.2 arasındaydı. Bu da 3.2'deki son değişiklikler girildiğinde 2.7'de rc olduğu anlamına geliyor, bu da hata yorumlarının daha kolay anlaşılmasını sağlıyor. Her neyse, sanırım retrospektte birkaç hata yaptılar (özellikle insanların 2to3, kütüphanelerin yardımıyla çift versiyonlu kod yerine yerine geçeceklerini varsayarak six, bu yüzden dict.viewkeyshiç kimsenin kullanamayacağı şeylere sahibiz ). 3.2'de çok geç gelen birkaç değişiklik, ancak çoğunlukla 2.7 oldukça etkileyici bir "son 2.x" sürümüydü.
abarnert

47

Diğer cevaplar zaten iyi açıkladı, ancak aralık nesnelerinin doğasını gösteren başka bir deney sunmak istiyorum:

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

Gördüğünüz gibi, bir aralık nesnesi aralığını hatırlayan bir nesnedir ve yalnızca bir kerelik bir jeneratör değil, birçok kez (üzerinde yineleme yaparken bile) kullanılabilir.


27

Her şey değerlendirmeye tembel bir yaklaşım ve bazı ekstra optimizasyon ile ilgilidir range. Aralıklardaki değerlerin gerçek kullanıma kadar veya ekstra optimizasyon nedeniyle daha fazla hesaplanması gerekmez.

Bu arada, tam sayınız o kadar büyük değil, düşünün sys.maxsize

sys.maxsize in range(sys.maxsize) oldukça hızlı

optimizasyon nedeniyle - verilen tamsayıyı sadece minimum ve maksimum aralıklarla karşılaştırmak kolaydır.

fakat:

Decimal(sys.maxsize) in range(sys.maxsize) oldukça yavaş .

(bu durumda, optimizasyon yoktur range, bu nedenle python beklenmedik Ondalık alırsa, python tüm sayıları karşılaştırır)

Bir uygulama detayının farkında olmalısınız, ancak buna güvenilmemelisiniz, çünkü bu gelecekte değişebilir.


4
Büyük tamsayılar yüzerken dikkatli olun. Çoğu makinede float(sys.maxsize) != sys.maxsize)olsa da sys.maxsize-float(sys.maxsize) == 0.
holdenweb

18

TL; DR

Tarafından döndürülen nesne range()aslında bir rangenesnedir. Bu nesne yineleyici arabirimini uygular, böylece tıpkı bir jeneratör, liste veya demet gibi değerlerini sırayla yineleyebilirsiniz.

Ancak aynı zamanda__contains__ , inoperatörün sağ tarafında bir nesne göründüğünde çağrılan arayüz olan arayüzü de uygular . __contains__()Bir yöntem, döner boolbir sol tarafında olup olmadığını ürünün innesnesidir. Yana rangenesneler kendi sınırlarını ve adım biliyorum, bu O (1) 'de uygulamak çok kolaydır.


0
  1. Optimizasyon nedeniyle, verilen tam sayıları sadece min ve maks aralığı ile karşılaştırmak çok kolaydır.
  2. Sebebi aralığı () fonksiyonu Python3 içinde çok hızlı burada oldukça aralık nesnenin direkt yineleme yerine, sınırları matematiksel muhakeme kullanmasıdır.
  3. Burada mantığı açıklamak için:
    • Sayının başlatma ve durdurma arasında olup olmadığını kontrol edin.
    • Adım hassasiyeti değerinin sayımızın üzerine çıkıp çıkmadığını kontrol edin.
  4. Bir örnek verelim , 997 aralıktadır (4, 1000, 3) çünkü:

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


1
Bunun için kaynak paylaşabilir misiniz? Bu yasal gibi görünse bile, bu iddiaları gerçek kodla desteklemek iyi olur
Nico Haase

Bunun uygulanabileceğinin bir örneği olduğunu düşünüyorum. Tam olarak uygulanma şekli değil. Herhangi bir referans sağlanmamış olmasına rağmen, aralık için dahil etme kontrolünün neden liste veya gruptan daha hızlı olabileceğini anlamak için yeterince iyi bir ipucu
Mohammed Shareef C

0

Optimizasyonun başlatılmasını önlemek için bir jeneratör kavrayışı kullanan x-1 in (i for i in range(x))büyük xdeğerler deneyin range.__contains__.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.