Có thể thực hiện Python cho vòng lặp phạm vi mà không có biến lặp không?


186

Có thể làm theo mà không cần i?

for i in range(some_number):
    # do something

Nếu bạn chỉ muốn làm điều gì đó N số lần và không cần trình lặp.


21
Đây là một câu hỏi hay! PyDev thậm chí còn gắn cờ 'i' là một cảnh báo cho 'biến không sử dụng'. Giải pháp dưới đây loại bỏ cảnh báo này.
Ashwin Nanjappa

@Ashwin Bạn có thể sử dụng \ @UnuseVariable để xóa cảnh báo đó. Lưu ý rằng tôi cần phải thoát biểu tượng 'tại' để nhận xét này.
Raffi Khatchadourian

Tôi đứng đầu câu hỏi tương tự bạn. Thật khó chịu với cảnh báo môn vị. Tất nhiên, bạn có thể vô hiệu hóa các cảnh báo bằng cách đàn áp bổ sung như @Raffi Khatchadourian đề xuất. Nó sẽ là tốt đẹp để tránh cảnh báo pylint bình luận đàn áp.
tangoal

Câu trả lời:


109

Tắt đầu tôi, không.

Tôi nghĩ điều tốt nhất bạn có thể làm là một cái gì đó như thế này:

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

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

Nhưng tôi nghĩ bạn chỉ có thể sống với ibiến phụ .

Đây là tùy chọn để sử dụng _biến, trong thực tế, chỉ là một biến khác.

for _ in range(n):
    do_something()

Lưu ý rằng _được chỉ định kết quả cuối cùng được trả về trong phiên python tương tác:

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

Vì lý do này, tôi sẽ không sử dụng nó theo cách này. Tôi không biết về bất kỳ thành ngữ nào như Ryan đã đề cập. Nó có thể gây rối cho thông dịch viên của bạn.

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

Và theo ngữ pháp Python , nó là một tên biến được chấp nhận:

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

4
"Nhưng tôi nghĩ rằng bạn chỉ có thể sống với thêm" i "" Vâng, đó chỉ là một điểm học tập.
James McMahon

1
@nemo, bạn có thể thử thực hiện cho _ trong phạm vi (n): nếu bạn không muốn sử dụng tên chữ và số.
Không biết

Là _ một biến trong trường hợp đó? Hay đó là một cái gì đó khác trong Python?
James McMahon

1
@nemo Có nó chỉ là một tên biến chấp nhận được. Trong trình thông dịch, nó được tự động gán biểu thức cuối cùng bạn thực hiện.
Không biết

3
@kurczak Có một điểm. Sử dụng _làm cho nó rõ ràng rằng nó nên được bỏ qua. Nói rằng không có điểm nào trong việc này giống như nói rằng không có điểm nào trong việc nhận xét mã của bạn - bởi vì dù sao nó cũng sẽ làm như vậy.
Tiên nữ Lambda

69

Bạn có thể đang tìm kiếm

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

đây là cách nhanh nhất để lặp lại timesthời gian trong Python.


2
Tôi không quan tâm đến hiệu suất, tôi chỉ tò mò nếu có một cách khó khăn hơn để viết tuyên bố. Mặc dù tôi đã sử dụng Python một cách rời rạc trong khoảng 2 năm nhưng tôi vẫn cảm thấy thiếu rất nhiều thứ. Itertools là một trong những điều đó, cảm ơn bạn đã thông tin.
James McMahon

5
Điều đó thật thú vị, tôi đã không nhận ra điều đó. Tôi chỉ xem qua các tài liệu itertools; nhưng tôi tự hỏi tại sao điều này nhanh hơn là chỉ sử dụng phạm vi hoặc xrange?
si28719e

5
@blackkantara: nhanh hơn vì không cần trả về chỉ số lặp hiện tại, đây là một phần có thể đo được của chi phí xrange (và phạm vi của Python 3, cung cấp một trình lặp chứ không phải danh sách). @nemo, phạm vi được tối ưu hóa hết mức có thể, nhưng cần xây dựng và trả về danh sách là công việc nặng nề hơn so với trình lặp (trong Py3, phạm vi trả về một trình lặp, như xrange của Py2; khả năng tương thích ngược không cho phép thay đổi như vậy trong Py2), đặc biệt là một thứ không cần trả về giá trị khác nhau.
Alex Martelli

4
@Cristian, vâng, rõ ràng chuẩn bị và trả lại một int Python mỗi lần, inc. gc làm việc, có một chi phí có thể đo lường được - sử dụng một bộ đếm trong nội bộ là không có vấn đề.
Alex Martelli

4
Giờ thì tôi đã hiểu. Sự khác biệt đến từ chi phí hoạt động của GC, không phải từ "thuật toán". Bằng cách này, tôi chạy một cách nhanh chóng timeit chuẩn và tăng tốc là ~ 1.42x.
Cristian Ciupitu

59

Thành ngữ chung để gán cho một giá trị không được sử dụng là đặt tên cho nó _.

for _ in range(times):
    do_stuff()

18

Điều mà mọi người đề nghị bạn sử dụng _ không nói là _ thường được sử dụng như một lối tắt đến một trong các chức năng gettext , vì vậy nếu bạn muốn phần mềm của mình có sẵn nhiều ngôn ngữ thì tốt nhất bạn nên tránh sử dụng nó cho các mục đích khác.

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

Đối với tôi, việc sử dụng này _có vẻ như là một ý tưởng tồi tệ, tôi sẽ không xung đột với nó.
KeithWM

9

Đây là một ý tưởng ngẫu nhiên sử dụng (lạm dụng?) Mô hình dữ liệu ( liên kết 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

Tôi tự hỏi nếu có một cái gì đó như thế này trong các thư viện tiêu chuẩn?


10
Tôi nghĩ rằng có một phương pháp như __nonzero__với tác dụng phụ là một ý tưởng khủng khiếp.
ThiefMaster

2
Tôi sẽ sử dụng __call__thay thế. while x():không khó để viết
Jasmijn

1
Ngoài ra còn có một đối số để tránh tên Counter; chắc chắn, nó không được bảo lưu hoặc trong phạm vi tích hợp, nhưng collections.Counterlà một điều , và làm cho một lớp cùng tên có nguy cơ gây nhầm lẫn cho người duy trì (không phải điều này chưa gây rủi ro cho điều đó).
ShadowRanger

7

Bạn có thể sử dụng _11 (hoặc bất kỳ số nào hoặc số nhận dạng không hợp lệ khác) để ngăn chặn việc đặt tên với gettext. Bất cứ khi nào bạn sử dụng dấu gạch dưới + định danh không hợp lệ, bạn sẽ nhận được một tên giả có thể được sử dụng trong vòng lặp.


Đẹp! PyDev đồng ý với bạn: điều này thoát khỏi cảnh báo màu vàng "Biến không sử dụng".
mike gặm nhấm

2

Có thể câu trả lời sẽ phụ thuộc vào vấn đề bạn gặp phải khi sử dụng iterator? có thể được sử dụng

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

hoặc là

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

loop(100, lambda a:a)

nhưng thẳng thắn tôi thấy không có điểm nào trong việc sử dụng các phương pháp như vậy


1
Lưu ý: Python (chắc chắn không phải là trình thông dịch tham chiếu CPython, ít nhất là không phải hầu hết các trình thông dịch khác) không tối ưu hóa đệ quy đuôi, vì vậy N sẽ bị giới hạn ở một số thứ trong vùng lân cận giá trị của sys.getrecursionlimit()(mặc định ở đâu đó trong bốn mức thấp phạm vi chữ số trên CPython); việc sử dụng sys.setrecursionlimitsẽ tăng giới hạn, nhưng cuối cùng, bạn đã đạt đến giới hạn ngăn xếp C và trình thông dịch sẽ chết với tràn ngăn xếp (không chỉ tăng đẹp RuntimeError/ RecursionError).
ShadowRanger


1

Thay vì một bộ đếm không cần thiết, bây giờ bạn có một danh sách không cần thiết. Giải pháp tốt nhất là sử dụng một biến bắt đầu bằng "_", thông báo cho người kiểm tra cú pháp rằng bạn biết rằng bạn không sử dụng biến đó.

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

0

Tôi thường đồng ý với các giải pháp được đưa ra ở trên. Cụ thể với:

  1. Sử dụng dấu gạch dưới trong for-loop (2 dòng trở lên)
  2. Xác định bộ whileđếm bình thường (3 dòng trở lên)
  3. Khai báo một lớp tùy chỉnh với __nonzero__việc thực hiện (nhiều dòng hơn)

Nếu một trong số đó là xác định một đối tượng như trong # 3, tôi sẽ khuyên bạn nên thực hiện giao thức với từ khóa hoặc áp dụng bối cảnh .

Hơn nữa tôi đề xuất một giải pháp khác. Nó là một lớp lót 3 và không phải là sang trọng tối cao, nhưng nó sử dụng gói itertools và do đó có thể được quan tâm.

from itertools import (chain, repeat)

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

Trong các ví dụ 2 này là số lần lặp lại vòng lặp. chuỗi đang gói hai vòng lặp lặp lại , lần đầu tiên bị giới hạn nhưng lần thứ hai là vô hạn. Hãy nhớ rằng đây là những đối tượng lặp thực sự, do đó chúng không yêu cầu bộ nhớ vô hạn. Rõ ràng điều này chậm hơn nhiều so với giải pháp số 1 . Trừ khi được viết như là một phần của chức năng, nó có thể yêu cầu dọn sạch cho biến thời gian .


2
chainlà không cần thiết, times = repeat(True, 2); while next(times, False):làm điều tương tự.
AChampion

0

Chúng tôi đã có một số niềm vui với những điều sau đây, thú vị để chia sẻ như vậy:

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

Các kết quả:

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

0

Nếu do_somethinglà một hàm đơn giản hoặc có thể được gói trong một, thì một lần đơn giản map()có thể 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)

Nếu bạn muốn truyền đối số cho do_something, bạn cũng có thể tìm thấy công thức itertoolsrepeatfunc đọc tốt:

Để vượt qua các đối số tương tự:

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)

Để vượt qua các đối số khác nhau:

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

deque(starmap(do_something, argses), 0)

-1

Nếu bạn thực sự muốn tránh đặt một cái gì đó có tên (một biến lặp như trong OP, hoặc danh sách không mong muốn hoặc trình tạo không mong muốn trả về đúng thời gian mong muốn), bạn có thể làm điều đó nếu bạn thực sự muốn:

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

Thủ thuật được sử dụng là tạo một lớp ẩn danh type('', (), {})dẫn đến một lớp có tên trống, nhưng NB không được chèn vào không gian tên cục bộ hoặc toàn cầu (ngay cả khi tên không trống được cung cấp). Sau đó, bạn sử dụng một thành viên của lớp đó làm biến lặp không thể truy cập được vì lớp mà thành viên của nó không thể truy cập được.


Rõ ràng đây là cố ý bệnh hoạn, vì vậy chỉ trích nó là bên cạnh điểm, nhưng tôi sẽ lưu ý thêm một cạm bẫy ở đây. Trên CPython, trình thông dịch tham chiếu, các định nghĩa lớp là tuần hoàn tự nhiên (tạo một lớp không thể tránh khỏi việc tạo ra một chu trình tham chiếu ngăn chặn việc dọn dẹp xác định của lớp dựa trên việc đếm tham chiếu). Điều đó có nghĩa là bạn đang chờ đợi GC tuần hoàn để khởi động và dọn dẹp lớp học. Nó thường sẽ được thu thập như một phần của thế hệ trẻ, theo mặc định được thu thập thường xuyên, nhưng ngay cả như vậy, mỗi vòng lặp có nghĩa là ~ 1,5 KB rác với thời gian tồn tại không xác định.
ShadowRanger

Về cơ bản, để tránh một biến được đặt tên (thường) sẽ được dọn sạch một cách nhất định trên mỗi vòng lặp (khi nó bật lại và giá trị cũ được dọn sạch), bạn đang tạo một biến không tên rất lớn được làm sạch không xác định và có thể dễ dàng kéo dài lâu hơn.
ShadowRanger


-7

Thế còn:

while range(some_number):
    #do something

3
Đó là một vòng lặp vô hạn vì điều kiện range(some_number)luôn luôn đúng!
gây chết người

@deadly: Chà, nếu some_numbernhỏ hơn hoặc bằng 0, nó không phải là vô hạn, nó chỉ không bao giờ chạy. :-) Và nó không hiệu quả đối với một vòng lặp vô hạn (đặc biệt là trên Py2), vì nó tạo ra một list(Py2) hoặc rangeđối tượng (Py3) mới cho mỗi thử nghiệm (nó không phải là hằng số theo quan điểm của trình thông dịch, nó phải tải rangesome_numbermỗi vòng lặp, gọi range, sau đó kiểm tra kết quả).
ShadowRanger
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.