เป็นไปได้หรือไม่ที่จะใช้ Python สำหรับช่วงลูปโดยไม่มีตัวแปรตัววนซ้ำ


187

เป็นไปได้หรือไม่ที่จะทำตามโดยไม่ต้องทำi?

for i in range(some_number):
    # do something

หากคุณต้องการทำบางสิ่งบางอย่างจำนวน N ครั้งและไม่ต้องการตัววนซ้ำ


21
นี่เป็นคำถามที่ดี! PyDev ทำเครื่องหมาย 'i' เป็นคำเตือนสำหรับ 'ตัวแปรที่ไม่ได้ใช้' วิธีการแก้ปัญหาด้านล่างลบคำเตือนนี้
Ashwin Nanjappa

@Ashwin คุณสามารถใช้ \ @UnusedVariable เพื่อลบคำเตือนนั้นได้ โปรดทราบว่าฉันต้องหลบหนีสัญลักษณ์ 'at' เพื่อให้ความคิดเห็นนี้ผ่านไป
Raffi Khatchadourian

ฉันถามคำถามเดียวกันกับคุณ มันน่ารำคาญกับคำเตือน pylint แน่นอนคุณสามารถปิดการใช้งานคำเตือนโดยการปราบปรามเพิ่มเติมเช่น @Raffi Khatchadourian เสนอ มันจะดีเพื่อหลีกเลี่ยงคำเตือน pylint และแสดงความคิดเห็นปราบปราม
tangoal

คำตอบ:


110

ปิดส่วนหัวของฉันไม่

ฉันคิดว่าสิ่งที่ดีที่สุดที่คุณสามารถทำได้คือสิ่งนี้:

def loop(f,n):
    for i in xrange(n): f()

loop(lambda: <insert expression here>, 5)

แต่ฉันคิดว่าคุณสามารถอยู่กับiตัวแปรพิเศษได้

นี่คือตัวเลือกในการใช้_ตัวแปรซึ่งในความเป็นจริงเป็นเพียงตัวแปรอื่น

for _ in range(n):
    do_something()

โปรดทราบว่า_มีการกำหนดผลลัพธ์สุดท้ายที่ส่งคืนในเซสชัน python แบบโต้ตอบ:

>>> 1+2
3
>>> _
3

ด้วยเหตุนี้ฉันจะไม่ใช้มันในลักษณะนี้ ฉันไม่รู้สำนวนใด ๆ ตามที่ Ryan กล่าวถึง มันอาจทำให้ล่ามของคุณยุ่งเหยิง

>>> for _ in xrange(10): pass
...
>>> _
9
>>> 1+2
3
>>> _
9

และตามPython ไวยากรณ์มันเป็นชื่อตัวแปรที่ยอมรับได้:

identifier ::= (letter|"_") (letter | digit | "_")*

4
"แต่ฉันคิดว่าคุณสามารถอยู่กับสิ่งพิเศษ" i "" ใช่มันเป็นเพียงจุดทางวิชาการ
James McMahon

1
@nemo คุณสามารถลองใช้ _ ในช่วง (n): หากคุณไม่ต้องการใช้ชื่อและตัวอักษร
ไม่ทราบ

_ เป็นตัวแปรในกรณีนั้นหรือไม่? หรือว่าอย่างอื่นใน Python?
James McMahon

1
@nemo ใช่มันเป็นเพียงชื่อตัวแปรที่ยอมรับได้ ในล่ามจะมีการกำหนดนิพจน์สุดท้ายที่คุณทำโดยอัตโนมัติ
ไม่ทราบ

3
@ kurczak มีจุดอยู่ การใช้_ทำให้ชัดเจนว่าควรละเว้น การบอกว่าไม่มีจุดในการทำเช่นนี้เหมือนกับการบอกว่าไม่มีจุดในการแสดงความคิดเห็นรหัสของคุณ - เพราะมันจะทำเหมือนกันทุกประการ
แลมบ์ดาแฟรี่

69

คุณอาจกำลังมองหา

for _ in itertools.repeat(None, times): ...

นี่เป็นวิธีที่เร็วที่สุดในการทำซ้ำtimesเวลาใน Python


2
ฉันไม่ได้กังวลเกี่ยวกับการแสดงฉันแค่อยากรู้อยากเห็นว่ามีวิธีการเขียนข้อความที่สั้นลง ในขณะที่ฉันใช้ Python เป็นระยะ ๆ ประมาณ 2 ปีตอนนี้ฉันยังรู้สึกว่ามีหลายสิ่งที่ฉันขาดไป Itertools เป็นหนึ่งในสิ่งเหล่านั้นขอบคุณสำหรับข้อมูล
James McMahon

5
นั่นเป็นเรื่องที่น่าสนใจฉันไม่รู้ ฉันเพิ่งดูเอกสาร itertools; แต่ฉันสงสัยว่าทำไมจึงเร็วกว่าแค่ใช้ช่วงหรือ xrange
si28719e

5
@blackkettle: เร็วขึ้นเพราะไม่จำเป็นต้องส่งคืนดัชนีการวนซ้ำปัจจุบันซึ่งเป็นส่วนที่วัดได้ของต้นทุน xrange (และช่วงของ Python 3 ซึ่งให้ค่าตัววนซ้ำไม่ใช่รายการ) @nemo, range นั้นได้รับการปรับปรุงให้ดีที่สุดเท่าที่จะเป็นไปได้ แต่จำเป็นต้องสร้างและส่งคืน list ซึ่งเป็นงานที่หนักกว่า iterator อย่างหลีกเลี่ยงไม่ได้ (ใน Py3, range จะคืนค่า iterator เช่น xrange ของ Py2; ใน Py2) โดยเฉพาะอย่างยิ่งที่ไม่จำเป็นต้องส่งคืนค่าที่เปลี่ยนแปลง
Alex Martelli

4
@Cristian ใช่ชัดเจนเตรียมและส่งกลับ Python int ทุกครั้ง inc งาน GC มีค่าใช้จ่ายที่วัดได้ - การใช้ตัวนับภายในนั้นไม่สำคัญ
Alex Martelli

4
ฉันเข้าใจแล้ว. ความแตกต่างมาจากค่าใช้จ่าย GC ไม่ใช่จาก "อัลกอริทึม" โดยวิธีการที่ฉันเรียกใช้มาตรฐานtimeitอย่างรวดเร็วและการเร่งความเร็วคือ ~ 1.42x
Cristian Ciupitu

59

_สำนวนทั่วไปสำหรับการกำหนดค่าที่ไม่ได้ถูกใช้เป็นชื่อให้มัน

for _ in range(times):
    do_stuff()

18

สิ่งที่ทุกคนแนะนำให้คุณใช้ _ ไม่ได้กล่าวคือ _ มักใช้เป็นทางลัดไปยังหนึ่งในฟังก์ชั่นgettextดังนั้นหากคุณต้องการให้ซอฟต์แวร์ของคุณมีภาษามากกว่าหนึ่งภาษาคุณควรหลีกเลี่ยงการใช้มัน เพื่อวัตถุประสงค์อื่น

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print _('This is a translatable string.')

สำหรับฉันแล้วการใช้งานนี้_ดูเหมือนเป็นความคิดที่แย่มากฉันไม่รังเกียจที่จะขัดแย้งกับมัน
KeithWM

9

นี่คือแนวคิดแบบสุ่มที่ใช้ (ละเมิด?) ตัวแบบข้อมูล ( ลิงก์ Py3 )

class Counter(object):
    def __init__(self, val):
        self.val = val

    def __nonzero__(self):
        self.val -= 1
        return self.val >= 0
    __bool__ = __nonzero__  # Alias to Py3 name to make code work unchanged on Py2 and Py3

x = Counter(5)
while x:
    # Do something
    pass

ฉันสงสัยว่ามีอะไรแบบนี้ในห้องสมุดมาตรฐานหรือไม่?


10
ฉันคิดว่าการมีวิธีเช่น__nonzero__กับผลข้างเคียงเป็นความคิดที่น่ากลัว
ThiefMaster

2
ฉันจะใช้__call__แทน while x():ไม่ใช่เรื่องยากที่จะเขียน
Jasmijn

1
นอกจากนี้ยังมีข้อโต้แย้งในการหลีกเลี่ยงชื่อCounter; แน่นอนว่ามันไม่ได้ถูกสงวนไว้หรืออยู่ในขอบเขต แต่collections.Counterเป็นสิ่งและทำให้คลาสที่มีชื่อเดียวกันมีความเสี่ยงต่อความสับสนของผู้ดูแล
ShadowRanger

7

คุณสามารถใช้ _11 (หรือหมายเลขใด ๆ หรือตัวระบุที่ไม่ถูกต้องอื่น ๆ ) เพื่อป้องกันการตั้งชื่อด้วย gettext เมื่อใดก็ตามที่คุณใช้ขีดเส้นใต้ + ตัวระบุที่ไม่ถูกต้องคุณจะได้รับชื่อดัมมี่ที่สามารถใช้ในการวนซ้ำ


ดี! PyDev เห็นด้วยกับคุณ: นี่เป็นการกำจัดคำเตือนสีเหลือง "ตัวแปรที่ไม่ได้ใช้"
ไมค์สัตว์ฟันแทะ

2

อาจเป็นคำตอบนั้นขึ้นอยู่กับปัญหาที่คุณมีเมื่อใช้ตัววนซ้ำ? อาจจะใช้

i = 100
while i:
    print i
    i-=1

หรือ

def loop(N, doSomething):
    if not N:
        return
    print doSomething(N)
    loop(N-1, doSomething)

loop(100, lambda a:a)

แต่ตรงไปตรงมาฉันไม่เห็นจุดที่ใช้วิธีดังกล่าว


1
หมายเหตุ: Python (ไม่ใช่นักแปลอ้างอิง CPython อย่างน้อยน่าจะไม่ใช่คนอื่น ๆ ส่วนใหญ่) ไม่ได้ปรับการเรียกซ้ำหางให้เหมาะสมดังนั้น N จะถูก จำกัด เฉพาะบางสิ่งในละแวกที่มีค่าsys.getrecursionlimit()(ซึ่งเป็นค่าเริ่มต้นสำหรับสี่ที่ต่ำ ช่วงหลักของ CPython); การใช้sys.setrecursionlimitจะเพิ่มขีด จำกัด แต่ในที่สุดคุณจะถึงขีด จำกัด C ของสแต็กและล่ามจะตายเมื่อมีสแตกมากเกินไป(ไม่ใช่แค่เพิ่มความสวยงามRuntimeError/ RecursionError)
ShadowRanger


1

แทนที่จะเป็นตัวนับที่ไม่จำเป็นตอนนี้คุณมีรายการที่ไม่จำเป็น ทางออกที่ดีที่สุดคือการใช้ตัวแปรที่ขึ้นต้นด้วย "_" ซึ่งจะบอกตัวตรวจสอบไวยากรณ์ที่คุณทราบว่าคุณไม่ได้ใช้ตัวแปร

x = range(5)
while x:
  x.pop()
  print "Work!"

0

โดยทั่วไปฉันเห็นด้วยกับวิธีแก้ปัญหาที่ระบุไว้ข้างต้น คือ:

  1. ใช้เครื่องหมายขีดเส้นใต้ใน for -loop (2 บรรทัดขึ้นไป)
  2. กำหนดปกติ whileนับ (3 บรรทัดขึ้นไป)
  3. ประกาศคลาสที่กำหนดเองด้วย__nonzero__การนำไปใช้ (อีกหลายบรรทัด)

หากมีการกำหนดวัตถุเช่นเดียวกับใน# 3ฉันขอแนะนำให้ใช้โปรโตคอลสำหรับกับคำหลักหรือใช้บริบท contextlib

ต่อไปฉันขอเสนอวิธีแก้ปัญหาอื่น มันเป็นซับใน 3 อันและไม่ได้มีความสง่างามอย่างสูงสุด แต่มันใช้แพ็คเกจitertoolsซึ่งอาจเป็นที่สนใจ

from itertools import (chain, repeat)

times = chain(repeat(True, 2), repeat(False))
while next(times):
    print 'do stuff!'

ในตัวอย่างเหล่านี้2คือจำนวนครั้งในการวนซ้ำลูป ห่วงโซ่คือการวนซ้ำสองซ้ำตัวแรกจะถูก จำกัด แต่ตัวที่สองไม่มีที่สิ้นสุด โปรดจำไว้ว่าสิ่งเหล่านี้เป็นวัตถุตัววนซ้ำจริงดังนั้นพวกเขาจึงไม่ต้องการหน่วยความจำที่ไม่มีที่สิ้นสุด เห็นได้ชัดว่านี่เป็นช้ามากแล้ววิธีการแก้ปัญหา# 1 เว้นแต่จะเขียนเป็นส่วนหนึ่งของฟังก์ชั่นอาจต้องล้างค่าสำหรับตัวแปรครั้ง


2
chainไม่จำเป็นtimes = repeat(True, 2); while next(times, False):ทำในสิ่งเดียวกัน
AChampion

0

เราสนุกกับสิ่งต่อไปนี้และน่าสนใจที่จะแบ่งปัน:

class RepeatFunction:
    def __init__(self,n=1): self.n = n
    def __call__(self,Func):
        for i in xrange(self.n):
            Func()
        return Func


#----usage
k = 0

@RepeatFunction(7)                       #decorator for repeating function
def Job():
    global k
    print k
    k += 1

print '---------'
Job()

ผล:

0
1
2
3
4
5
6
---------
7

0

ถ้าdo_somethingเป็นฟังก์ชั่นที่เรียบง่ายหรือสามารถห่อในหนึ่งง่ายmap()สามารถdo_something range(some_number)ครั้ง:

# Py2 version - map is eager, so it can be used alone
map(do_something, xrange(some_number))

# Py3 version - map is lazy, so it must be consumed to do the work at all;
# wrapping in list() would be equivalent to Py2, but if you don't use the return
# value, it's wastefully creating a temporary, possibly huge, list of junk.
# collections.deque with maxlen 0 can efficiently run a generator to exhaustion without
# storing any of the results; the itertools consume recipe uses it for that purpose.
from collections import deque

deque(map(do_something, range(some_number)), 0)

หากคุณต้องการส่งผ่านข้อโต้แย้งไปยังdo_somethingคุณอาจพบว่าสูตรitertoolsrepeatfuncอ่านได้ดี:

ในการผ่านข้อโต้แย้งเดียวกัน:

from collections import deque
from itertools import repeat, starmap

args = (..., my args here, ...)

# Same as Py3 map above, you must consume starmap (it's a lazy generator, even on Py2)
deque(starmap(do_something, repeat(args, some_number)), 0)

ในการผ่านการโต้แย้งที่แตกต่าง:

argses = [(1, 2), (3, 4), ...]

deque(starmap(do_something, argses), 0)

-1

หากคุณจริงๆต้องการที่จะหลีกเลี่ยงการวางบางสิ่งบางอย่างที่มีชื่อ (ทั้งตัวแปรซ้ำเช่นเดียวกับใน OP หรือรายการที่ไม่พึงประสงค์หรือเครื่องกำเนิดไฟฟ้าที่ไม่พึงประสงค์กลับมาจริงจำนวนเงินที่ต้องการของเวลา) คุณสามารถทำมันหากคุณต้องการจริงๆ:

for type('', (), {}).x in range(somenumber):
    dosomething()

เคล็ดลับที่ใช้คือการสร้างคลาสที่ไม่ระบุชื่อtype('', (), {})ซึ่งส่งผลให้คลาสมีชื่อว่างเปล่า แต่ NB ที่ไม่ได้แทรกไว้ในเนมสเปซท้องถิ่นหรือทั่วโลก (แม้ว่าจะมีการระบุชื่อที่ไม่ว่าง) จากนั้นคุณใช้สมาชิกของคลาสนั้นเป็นตัวแปรการวนซ้ำซึ่งไม่สามารถเข้าถึงได้เนื่องจากคลาสที่สมาชิกที่เป็นสมาชิกนั้นไม่สามารถเข้าถึงได้


เห็นได้ชัดว่านี่เป็นพยาธิวิทยาโดยเจตนาดังนั้นการวิพากษ์วิจารณ์มันอยู่ข้าง ๆ จุด แต่ฉันจะสังเกตเห็นข้อผิดพลาดเพิ่มเติมที่นี่ บน CPython ตัวแปลอ้างอิงคำจำกัดความของคลาสนั้นเป็นวัฏจักรตามธรรมชาติ (การสร้างคลาสที่สร้างวงรอบการอ้างอิงที่หลีกเลี่ยงไม่ได้ซึ่งป้องกันการล้างค่าที่ไม่ได้กำหนดของคลาสตามการนับการอ้างอิง) นั่นหมายความว่าคุณกำลังรอ GC ที่จะเตะและทำความสะอาดชั้นเรียน มันมักจะถูกรวบรวมเป็นส่วนหนึ่งของคนรุ่นใหม่ซึ่งโดยปกติแล้วจะถูกเก็บรวบรวมบ่อยครั้ง แต่ถึงกระนั้นวงแต่ละวงก็หมายถึงขยะประมาณ 1.5 กิโลไบต์ต่ออายุการใช้งานที่ไม่ได้กำหนดไว้
ShadowRanger

โดยทั่วไปเพื่อหลีกเลี่ยงตัวแปรที่มีชื่อซึ่งจะ (โดยทั่วไป) ล้างทำความสะอาดอย่างแน่นอนในแต่ละลูป (เมื่อมันรีบาวด์และล้างค่าเก่า) คุณกำลังสร้างตัวแปรที่ไม่มีชื่อขนาดใหญ่ นานกว่านี้
ShadowRanger


-7

เกี่ยวกับ:

while range(some_number):
    #do something

3
นั่นคือการวนซ้ำไม่สิ้นสุดเนื่องจากเงื่อนไขrange(some_number)เป็นจริงเสมอ!
มรณะ

@adadly: ถ้าsome_numberน้อยกว่าหรือเท่ากับ0มันไม่ใช่อนันต์มันก็ไม่เคยวิ่ง :-) และค่อนข้างไม่มีประสิทธิภาพสำหรับการวนซ้ำไม่ จำกัด (โดยเฉพาะอย่างยิ่งใน Py2) เนื่องจากมันสร้างlist(Py2) ใหม่หรือrangeวัตถุ (Py3) สำหรับการทดสอบแต่ละครั้ง (ไม่ใช่ค่าคงที่จากมุมมองของล่ามจึงต้องโหลดrangeและsome_numberทุกลูปโทรrangeจากนั้นทดสอบผลลัพธ์)
ShadowRanger
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.