Lặp trong python

Iterator vòng lặp trong Python thường được trả về cho chúng ta một vòng lặp đối tượng của vòng lặp- một giá trị tại một thời điểm. Hàm iter () và next () gọi chung để tạo thành giao thức lặp trình. Ví dụ về trình duyệt trong python

Ví dụ

>>> next(it)

đầu ra

Traceback (most recent call last)

Trình tạo khái niệm trình tạo trong Python

Trình tạo hàm là các hàm đặc biệt trả về một trình lặp và lập trình viên có thể lặp lại nó dưới dạng một danh sách để truy cập từng đối tượng một. Đây là một cách đơn giản để bạn tạo các vòng lặp của chương trình.  

Để tạo một Trình tạo hàm, bạn cần tạo một lớp và sau đó thực hiện các phương thức __iter__() và __next__() , đồng thời quản lý trạng thái bên trong và nâng cao ngoại lệ StopIteration khi nó kết thúc.  

Ví dụ

a=(1,2,3,4,5,6,7,8,9,10,11)

for i in range(1,10,2):

    print(i)

đầu ra

1

3

5

7

9

>>> Đọc thêm. Tái cấu trúc lại mã Python - Lộ diện 6 cách cấu trúc lại mã Python

Ưu điểm của trình tạo Trình tạo trong Python

  1. Dễ dàng phát triển nhanh chóng ở mọi nơi

Trình tạo Python rất dễ thực hiện trong các mã động cũng như do người dùng xác định. Chúng thường tìm tải các giá trị từ các thư viện, giúp chúng có thể truy cập được trên mọi phiên bản python

Một chức năng thông thường để trả về một chuỗi sẽ tạo ra toàn bộ chuỗi trong bộ nhớ trước khi trả về kết quả trong thời gian chạy chính

  1. Biểu tượng Infinite Stream

Trình tạo có thể biểu thị độ dài của một chuỗi số vô hạn được lấy tại một thời điểm. And this thing has been supports by library function

  1. Trình tạo Pipelining trong Python

Nhiều trình tạo có thể được sử dụng để chuyển một loạt các hoạt động trong thời gian chạy chính

>>> Đọc thêm. Mô-đun trong Python - Lấy lại lòng kiến ​​thức về Mô-đun trong Python

Sự khác biệt giữa Iterator và Generator trong Python

  1. Khi tạo trình Python, chúng ta sử dụng một hàm. Nhưng khi tạo một trình lặp trong Python, chúng ta sử dụng các hàm iter() và next()
  2. Một trình tạo trong python sử dụng từ khóa 'yield'. Một vòng lặp Python thì không
  3. Trình tạo Python lưu trạng thái của các biến cục bộ mỗi khi 'yield tạm dừng vòng lặp trong Python. Một trình lặp không sử dụng các biến cục bộ, tất cả những gì nó cần đều có thể lặp lại
  4. Một trình tạo có thể có bất kỳ số lượng nào của câu lệnh 'yield'
  5. Bạn có thể phát triển vòng lặp khai thác của riêng mình bằng cách sử dụng một lớp Python;
  6. Để viết trình tạo Python, bạn có thể sử dụng hàm Python hoặc hàm hiểu. Nhưng đối với một trình lặp, bạn phải sử dụng hàm iter() và next()
  7. Trình tạo trong Python cho phép chúng ta viết mã nhanh và thu gọn. Đây là một lợi thế so với các vòng lặp của Python. Họ cũng đơn giản hơn để viết mã như vậy với tùy chỉnh trình tùy chỉnh
  8. Bộ nhớ phiên bản Python tiết kiệm hơn. Vui lòng xem điều này với ví dụ bên dưới
>>> def func():

       i=1

       while i>0:

                 yield i

                 i-=1

>>> for i in func():

             print(i)

Mối quan hệ giữa Iterator và Generator trong Python

Mối quan hệ giữa Iterator và Generator trong Python

Mối quan hệ giữa Iterator và Generator trong Python

Trình tạo Python là một trình sửa lỗi

Trình tạo trong Python là một lớp con của Iterator. Để chứng minh điều này, chúng ta có thể sử dụng chức năng Issubclass()

>>> import collections,types

>>> issubclass(types.GeneratorType,collections.Iterator)

True

>>> issubclass(collections.Generator,collections.Iterator)

True

>>> issubclass(collections.Iterator,types.GeneratorType)

False

Iterator in Python could it repeatable

Iterator là một lớp phụ của Iterable

>>> issubclass(collections.Iterator,collections.Iterable)

True

>>>Tham khảo. Key learning set Python

Kết luận. Trên đây là một số điểm khác biệt giữa Iterator và Generator trong Python. Bạn có thể sử dụng Iterator giao thức trực tiếp khi bạn cần mở rộng một đối tượng Python như một đối tượng có thể được lặp lại

Tuy nhiên, trong phần lớn các trường hợp, bạn phù hợp nhất để sử dụng năng suất để xác định hàm trả về Trình tạo vòng lặp hoặc xem xét Biểu thức trình tạo. Hy vọng bài viết này hữu ích với bạn. Tìm hiểu thêm về Python và các ngôn ngữ lập trình khác thông qua các khóa học lập trình tại Viện công nghệ thông tin T3H

Mảng hỗ trợ giao thức lặp và có thể được lặp lại như danh sách Python. Xem phần trong hướng dẫn bắt đầu nhanh để biết các ví dụ và cách sử dụng cơ bản. Phần còn lại của tài liệu này trình bày đối tượng và đề cập đến cách sử dụng nâng cao hơn

Đối tượng iterator, được giới thiệu trong NumPy 1. 6, cung cấp nhiều cách linh hoạt để truy cập tất cả các phần tử của một hoặc nhiều mảng một cách có hệ thống. Trang này giới thiệu một số cách cơ bản để sử dụng đối tượng để tính toán trên mảng trong Python, sau đó kết luận với cách một người có thể tăng tốc vòng lặp bên trong Cython. Vì phần hiển thị Python của là một ánh xạ tương đối đơn giản của API bộ lặp mảng C, nên những ý tưởng này cũng sẽ cung cấp trợ giúp khi làm việc với phép lặp mảng từ C hoặc C++

Lặp lại mảng đơn

Nhiệm vụ cơ bản nhất có thể được thực hiện với là truy cập mọi phần tử của một mảng. Mỗi phần tử được cung cấp từng phần tử một bằng giao diện trình lặp Python tiêu chuẩn

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a):
..     print(x, end=' ')
...
0 1 2 3 4 5

Một điều quan trọng cần lưu ý đối với lần lặp này là thứ tự được chọn để khớp với bố cục bộ nhớ của mảng thay vì sử dụng thứ tự C hoặc Fortran tiêu chuẩn. Điều này được thực hiện để đạt hiệu quả truy cập, phản ánh ý tưởng rằng theo mặc định, người ta chỉ muốn truy cập từng phần tử mà không cần quan tâm đến thứ tự cụ thể. Chúng ta có thể thấy điều này bằng cách lặp lại phép chuyển vị của mảng trước đó, so với việc lấy một bản sao của phép chuyển vị đó theo thứ tự C

Ví dụ

________số 8_______

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5

Các phần tử của a và a. T được duyệt theo cùng một thứ tự, cụ thể là thứ tự chúng được lưu trữ trong bộ nhớ, trong khi các phần tử của một. T. copy(order=’C’) được truy cập theo một thứ tự khác vì chúng đã được đưa vào một bố cục bộ nhớ khác

Kiểm soát thứ tự lặp lại

Đôi khi, điều quan trọng là phải truy cập các phần tử của một mảng theo một thứ tự cụ thể, bất kể bố cục của các phần tử trong bộ nhớ. Đối tượng cung cấp một tham số thứ tự để kiểm soát khía cạnh lặp lại này. Mặc định, có hành vi được mô tả ở trên, là order=’K’ để giữ thứ tự hiện có. Điều này có thể được ghi đè bằng order=’C’ cho đơn hàng C và order=’F’ cho đơn hàng Fortran

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5

Sửa đổi giá trị mảng

Theo mặc định, toán hạng đầu vào được coi là đối tượng chỉ đọc. Để có thể sửa đổi các phần tử mảng, bạn phải chỉ định chế độ đọc-ghi hoặc chỉ ghi bằng cờ 'readwrite' hoặc 'writeonly' trên mỗi toán hạng

Sau đó, nditer sẽ tạo ra các mảng bộ đệm có thể ghi mà bạn có thể sửa đổi. Tuy nhiên, vì nditer phải sao chép dữ liệu bộ đệm này trở lại mảng ban đầu sau khi quá trình lặp kết thúc, bạn phải báo hiệu khi quá trình lặp kết thúc bằng một trong hai phương pháp. bạn có thể

  • đã sử dụng nditer làm trình quản lý ngữ cảnh bằng cách sử dụng câu lệnh with và dữ liệu tạm thời sẽ được ghi lại khi thoát khỏi ngữ cảnh

  • gọi phương thức đóng của iterator sau khi lặp xong, thao tác này sẽ kích hoạt quá trình ghi lại

Trình chỉ định không còn có thể được lặp lại sau khi lệnh đóng được gọi hoặc ngữ cảnh của nó bị thoát

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> a
array([[0, 1, 2],
       [3, 4, 5]])
>>> with np.nditer(a, op_flags=['readwrite']) as it:
..    for x in it:
..        x[...] = 2 * x
...
>>> a
array([[ 0,  2,  4],
       [ 6,  8, 10]])

Nếu bạn đang viết mã cần hỗ trợ các phiên bản cũ hơn của numpy, hãy lưu ý rằng trước 1. 15, không phải là trình quản lý bối cảnh và không có phương thức đóng. Thay vào đó, nó dựa vào hàm hủy để bắt đầu ghi lại bộ đệm

Sử dụng vòng lặp bên ngoài

Trong tất cả các ví dụ cho đến nay, các phần tử của a được cung cấp lần lượt bởi trình vòng lặp, bởi vì tất cả logic vòng lặp là bên trong của trình vòng lặp. Mặc dù điều này đơn giản và thuận tiện, nhưng nó không hiệu quả lắm. Cách tiếp cận tốt hơn là di chuyển vòng lặp trong cùng một chiều vào mã của bạn, bên ngoài trình vòng lặp. Bằng cách này, các hoạt động vector hóa của NumPy có thể được sử dụng trên các phần tử lớn hơn đang được truy cập

Sẽ cố gắng cung cấp các khối càng lớn càng tốt cho vòng lặp bên trong. Bằng cách buộc thứ tự 'C' và 'F', chúng tôi nhận được các kích thước vòng lặp bên ngoài khác nhau. Chế độ này được bật bằng cách chỉ định cờ lặp

Quan sát rằng với mặc định giữ trật tự bộ nhớ riêng, trình vòng lặp có thể cung cấp một đoạn một chiều duy nhất, trong khi khi buộc thứ tự Fortran, nó phải cung cấp ba đoạn gồm hai phần tử mỗi đoạn

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop']):
..     print(x, end=' ')
...
[0 1 2 3 4 5]

>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
..     print(x, end=' ')
...
[0 3] [1 4] [2 5]

Theo dõi một chỉ mục hoặc nhiều chỉ mục

Trong quá trình lặp lại, bạn có thể muốn sử dụng chỉ mục của phần tử hiện tại trong tính toán. Ví dụ: bạn có thể muốn truy cập các phần tử của một mảng theo thứ tự bộ nhớ, nhưng sử dụng thứ tự C, thứ tự Fortran hoặc chỉ mục đa chiều để tra cứu các giá trị trong một mảng khác

Chỉ mục được theo dõi bởi chính đối tượng iterator và có thể truy cập thông qua thuộc tính chỉ mục hoặc thuộc tính multi_index, tùy thuộc vào nội dung được yêu cầu. Các ví dụ dưới đây hiển thị các bản in thể hiện sự tiến triển của chỉ mục

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> for x in it:
..     print("%d <%d>" % (x, it.index), end=' ')
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5>

>>> it = np.nditer(a, flags=['multi_index'])
>>> for x in it:
..     print("%d <%s>" % (x, it.multi_index), end=' ')
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)>

>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
..     for x in it:
..         x[...] = it.multi_index[1] - it.multi_index[0]
...
>>> a
array([[ 0,  1,  2],
       [-1,  0,  1]])

Việc theo dõi một chỉ mục hoặc nhiều chỉ mục không tương thích với việc sử dụng vòng lặp bên ngoài vì nó yêu cầu một giá trị chỉ mục khác cho mỗi phần tử. Nếu bạn cố gắng kết hợp các cờ này, đối tượng sẽ đưa ra một ngoại lệ

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
0

Vòng lặp thay thế và truy cập phần tử

Để làm cho các thuộc tính của nó dễ dàng truy cập hơn trong quá trình lặp, có một cú pháp thay thế để lặp, hoạt động rõ ràng với chính đối tượng trình lặp. Với cấu trúc vòng lặp này, giá trị hiện tại có thể truy cập được bằng cách lập chỉ mục vào trình vòng lặp. Các thuộc tính khác, chẳng hạn như các chỉ số được theo dõi vẫn như trước. Các ví dụ bên dưới tạo ra kết quả giống hệt với các ví dụ trong phần trước

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
1

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
2

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
3

Bộ đệm các phần tử mảng

Khi buộc một thứ tự lặp lại, chúng tôi quan sát thấy rằng tùy chọn vòng lặp bên ngoài có thể cung cấp các phần tử theo các phần nhỏ hơn vì các phần tử không thể được truy cập theo thứ tự thích hợp với một bước tiến liên tục. Khi viết mã C, điều này nói chung là tốt, tuy nhiên trong mã Python thuần túy, điều này có thể làm giảm đáng kể hiệu suất

Bằng cách bật chế độ đệm, các khối do trình vòng lặp cung cấp cho vòng lặp bên trong có thể được làm lớn hơn, giảm đáng kể chi phí hoạt động của trình thông dịch Python. Trong ví dụ buộc thứ tự lặp lại Fortran, vòng lặp bên trong sẽ thấy tất cả các phần tử trong một lần khi kích hoạt bộ đệm

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
4

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
5

Lặp lại dưới dạng một kiểu dữ liệu cụ thể

Đôi khi cần coi một mảng là một kiểu dữ liệu khác với kiểu dữ liệu được lưu trữ dưới dạng. Chẳng hạn, một người có thể muốn thực hiện tất cả các tính toán trên số float 64 bit, ngay cả khi các mảng đang được thao tác là số float 32 bit. Ngoại trừ khi viết mã C cấp thấp, tốt hơn hết là để trình vòng lặp xử lý việc sao chép hoặc lưu vào bộ đệm thay vì tự truyền kiểu dữ liệu trong vòng lặp bên trong

Có hai cơ chế cho phép thực hiện điều này, bản sao tạm thời và chế độ đệm. Với các bản sao tạm thời, một bản sao của toàn bộ mảng được tạo với kiểu dữ liệu mới, sau đó việc lặp lại được thực hiện trong bản sao. Quyền ghi được cho phép thông qua chế độ cập nhật mảng ban đầu sau khi hoàn tất quá trình lặp. Hạn chế chính của các bản sao tạm thời là bản sao tạm thời có thể tiêu tốn một lượng lớn bộ nhớ, đặc biệt nếu kiểu dữ liệu lặp lại có kích thước mục lớn hơn so với bản gốc

Chế độ đệm giảm thiểu vấn đề sử dụng bộ nhớ và thân thiện với bộ đệm hơn so với tạo bản sao tạm thời. Ngoại trừ các trường hợp đặc biệt, khi cần toàn bộ mảng cùng một lúc bên ngoài trình vòng lặp, nên sử dụng bộ đệm để sao chép tạm thời. Trong NumPy, bộ nhớ đệm được sử dụng bởi các ufunc và các chức năng khác để hỗ trợ các đầu vào linh hoạt với chi phí bộ nhớ tối thiểu

Trong các ví dụ của chúng tôi, chúng tôi sẽ xử lý mảng đầu vào bằng một kiểu dữ liệu phức tạp, để chúng tôi có thể lấy căn bậc hai của các số âm. Nếu không kích hoạt các bản sao hoặc chế độ đệm, trình vòng lặp sẽ đưa ra một ngoại lệ nếu loại dữ liệu không khớp chính xác

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
6

Trong chế độ sao chép, 'bản sao' được chỉ định làm cờ cho mỗi toán hạng. Điều này được thực hiện để cung cấp quyền kiểm soát theo kiểu từng toán hạng. Chế độ đệm được chỉ định làm cờ lặp

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
7

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
8

Trình lặp sử dụng quy tắc truyền của NumPy để xác định xem một chuyển đổi cụ thể có được phép hay không. Theo mặc định, nó thực thi truyền 'an toàn'. Điều này có nghĩa là, ví dụ, nó sẽ đưa ra một ngoại lệ nếu bạn cố coi mảng float 64 bit là mảng float 32 bit. Trong nhiều trường hợp, quy tắc ‘same_kind’ là quy tắc hợp lý nhất để sử dụng, vì nó sẽ cho phép chuyển đổi từ float 64 sang 32 bit, chứ không phải từ float sang int hoặc từ phức sang float

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
..     print(x, end=' ')
...
0 1 2 3 4 5
9

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
0

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
1

Một điều cần chú ý là chuyển đổi trở lại kiểu dữ liệu ban đầu khi sử dụng toán hạng chỉ đọc hoặc ghi. Một trường hợp phổ biến là triển khai vòng lặp bên trong theo số float 64 bit và sử dụng phép truyền 'same_kind' để cho phép các loại dấu phẩy động khác cũng được xử lý. Trong khi ở chế độ chỉ đọc, một mảng số nguyên có thể được cung cấp, chế độ đọc-ghi sẽ đưa ra một ngoại lệ vì chuyển đổi trở lại mảng sẽ vi phạm quy tắc truyền

Ví dụ

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
2

Lặp lại mảng phát sóng

NumPy có một bộ quy tắc để xử lý các mảng có hình dạng khác nhau được áp dụng bất cứ khi nào các hàm nhận nhiều toán hạng kết hợp phần tử khôn ngoan. cái này gọi là. Đối tượng có thể áp dụng các quy tắc này cho bạn khi bạn cần viết một hàm như vậy

Ví dụ, chúng tôi in ra kết quả của việc phát một mảng một và hai chiều cùng nhau

Ví dụ

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
3

Khi xảy ra lỗi phát sóng, trình lặp sẽ đưa ra một ngoại lệ bao gồm các hình dạng đầu vào để giúp chẩn đoán sự cố

Ví dụ

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
4

Mảng đầu ra được phân bổ Iterator

Một trường hợp phổ biến trong các hàm NumPy là có các đầu ra được phân bổ dựa trên việc phát đầu vào và ngoài ra còn có một tham số tùy chọn gọi là 'ra' nơi kết quả sẽ được đặt khi nó được cung cấp. Đối tượng cung cấp một thành ngữ thuận tiện giúp hỗ trợ cơ chế này rất dễ dàng

Chúng tôi sẽ chỉ ra cách thức hoạt động của nó bằng cách tạo một hàm bình phương đầu vào của nó. Hãy bắt đầu với một định nghĩa hàm tối thiểu không bao gồm hỗ trợ tham số 'ngoài'

Ví dụ

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
5

Theo mặc định, việc sử dụng các cờ 'phân bổ' và 'chỉ ghi' cho các toán hạng được chuyển vào là Không có. Điều này có nghĩa là chúng tôi chỉ có thể cung cấp hai toán hạng cho trình vòng lặp và nó xử lý phần còn lại

Khi thêm tham số 'ra', chúng ta phải cung cấp các cờ đó một cách rõ ràng, bởi vì nếu ai đó chuyển vào một mảng là 'ra', thì trình vòng lặp sẽ mặc định là 'chỉ đọc' và vòng lặp bên trong của chúng ta sẽ không thành công. Lý do 'chỉ đọc' là mặc định cho các mảng đầu vào là để tránh nhầm lẫn về việc vô tình kích hoạt thao tác rút gọn. Nếu giá trị mặc định là 'readwrite', thì bất kỳ thao tác phát sóng nào cũng sẽ kích hoạt quá trình giảm, một chủ đề sẽ được đề cập sau trong tài liệu này

Trong khi chúng tôi đang ở đó, hãy cũng giới thiệu cờ 'no_broadcast', điều này sẽ ngăn đầu ra không được phát sóng. Điều này rất quan trọng vì chúng tôi chỉ muốn một giá trị đầu vào cho mỗi đầu ra. Tổng hợp nhiều hơn một giá trị đầu vào là một thao tác rút gọn yêu cầu xử lý đặc biệt. Nó đã gây ra lỗi vì việc giảm phải được bật rõ ràng trong cờ lặp, nhưng thông báo lỗi do vô hiệu hóa phát sóng dễ hiểu hơn nhiều đối với người dùng cuối. Để biết cách khái quát hàm bình phương thành một phép rút gọn, hãy xem hàm tổng bình phương trong phần về Cython

Để hoàn thiện, chúng tôi cũng sẽ thêm cờ 'external_loop' và 'buffered', vì đây là những thứ bạn thường muốn vì lý do hiệu suất

Ví dụ

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
6

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
7

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
8

>>> for x in np.nditer(a.T.copy(order='C')):
..     print(x, end=' ')
...
0 3 1 4 2 5
9

Lặp lại sản phẩm bên ngoài

Bất kỳ phép toán nhị phân nào cũng có thể được mở rộng thành phép toán mảng theo kiểu sản phẩm bên ngoài như trong và đối tượng cung cấp cách để thực hiện điều này bằng cách ánh xạ rõ ràng các trục của toán hạng. Cũng có thể thực hiện việc này bằng cách lập chỉ mục, nhưng chúng tôi sẽ chỉ cho bạn cách sử dụng trực tiếp tham số nditer op_axes để thực hiện việc này mà không cần chế độ xem trung gian

Chúng ta sẽ thực hiện một tích ngoài đơn giản, đặt kích thước của toán hạng thứ nhất trước kích thước của toán hạng thứ hai. Tham số op_axes cần một danh sách các trục cho mỗi toán hạng và cung cấp ánh xạ từ các trục của trình vòng lặp tới các trục của toán hạng

Giả sử toán hạng đầu tiên là một chiều và toán hạng thứ hai là hai chiều. Trình vòng lặp sẽ có ba chiều, vì vậy op_axes sẽ có hai danh sách 3 phần tử. Danh sách đầu tiên chọn ra một trục của toán hạng đầu tiên và là -1 cho phần còn lại của các trục lặp, với kết quả cuối cùng là [0, -1, -1]. Danh sách thứ hai chọn ra hai trục của toán hạng thứ hai, nhưng không được trùng với các trục đã chọn trong toán hạng thứ nhất. Danh sách của nó là [-1, 0, 1]. Toán hạng đầu ra ánh xạ lên các trục của bộ lặp theo cách tiêu chuẩn, vì vậy chúng tôi có thể cung cấp Không thay vì xây dựng một danh sách khác

Hoạt động trong vòng lặp bên trong là một phép nhân đơn giản. Mọi thứ liên quan đến sản phẩm bên ngoài được xử lý bởi thiết lập iterator

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
0

Lưu ý rằng khi đóng trình vòng lặp, chúng ta không thể truy cập và phải sử dụng tham chiếu được tạo bên trong trình quản lý ngữ cảnh

Lặp lại giảm

Bất cứ khi nào một toán hạng có thể ghi có ít phần tử hơn không gian lặp đầy đủ, thì toán hạng đó đang được rút gọn. Đối tượng yêu cầu bất kỳ toán hạng rút gọn nào được gắn cờ là đọc-ghi và chỉ cho phép rút gọn khi 'reduce_ok' được cung cấp dưới dạng cờ lặp

Đối với một ví dụ đơn giản, hãy xem xét việc lấy tổng của tất cả các phần tử trong một mảng

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
1

Mọi thứ phức tạp hơn một chút khi kết hợp các toán hạng rút gọn và phân bổ. Trước khi bắt đầu lặp lại, bất kỳ toán hạng rút gọn nào cũng phải được khởi tạo về giá trị ban đầu của nó. Đây là cách chúng ta có thể làm điều này, tính tổng dọc theo trục cuối cùng của một

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
2

Để thực hiện giảm vùng đệm, cần có một điều chỉnh khác trong quá trình thiết lập. Thông thường, việc xây dựng trình vòng lặp liên quan đến việc sao chép bộ đệm dữ liệu đầu tiên từ các mảng có thể đọc được vào bộ đệm. Mọi toán hạng rút gọn đều có thể đọc được, vì vậy nó có thể được đọc vào bộ đệm. Thật không may, việc khởi tạo toán hạng sau khi hoạt động tạo bộ đệm này hoàn tất sẽ không được phản ánh trong bộ đệm mà phép lặp bắt đầu và kết quả rác sẽ được tạo ra

Cờ lặp “delay_bufalloc” ở đó để cho phép các toán hạng rút gọn được cấp phát lặp tồn tại cùng với bộ đệm. Khi cờ này được đặt, trình vòng lặp sẽ không khởi tạo bộ đệm của nó cho đến khi nhận được lệnh đặt lại, sau đó nó sẽ sẵn sàng để lặp lại thông thường. Đây là ví dụ trước trông như thế nào nếu chúng ta cũng kích hoạt tính năng đệm

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
3

Đặt Inner Loop trong Cython

Những người muốn có hiệu suất thực sự tốt từ các hoạt động cấp thấp của họ nên cân nhắc trực tiếp việc sử dụng API lặp được cung cấp trong C, nhưng đối với những người không thoải mái với C hoặc C++, Cython là nền tảng trung gian tốt với sự đánh đổi hiệu suất hợp lý. Đối với đối tượng, điều này có nghĩa là để trình vòng lặp đảm nhiệm việc phát, chuyển đổi dtype và lưu vào bộ đệm, đồng thời đưa vòng lặp bên trong cho Cython

Ví dụ của chúng ta, chúng ta sẽ tạo hàm tổng bình phương. Để bắt đầu, hãy triển khai chức năng này bằng Python đơn giản. Chúng tôi muốn hỗ trợ tham số 'trục' tương tự như hàm numpy, vì vậy chúng tôi sẽ cần tạo một danh sách cho tham số op_axes. Đây là giao diện của nó

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
4

Để Cython-ize hàm này, chúng ta thay thế vòng lặp bên trong (y[…] += x*x) bằng mã Cython dành riêng cho kiểu dtype float64. Với cờ 'external_loop' được bật, các mảng được cung cấp cho vòng lặp bên trong sẽ luôn là một chiều, do đó cần thực hiện rất ít việc kiểm tra

Đây là danh sách của sum_squares. pyx

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
5

Trên máy này, xây dựng các. pyx thành một mô-đun trông giống như sau, nhưng bạn có thể phải tìm một số hướng dẫn về Cython để cho bạn biết chi tiết cụ thể về cấu hình hệ thống của mình

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
6

Chạy cái này từ trình thông dịch Python tạo ra các câu trả lời giống như mã Python/NumPy gốc của chúng tôi đã làm

Ví dụ

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
..     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
..     print(x, end=' ')
...
0 3 1 4 2 5
7

Thực hiện một chút thời gian trong IPython cho thấy rằng việc giảm phân bổ bộ nhớ và chi phí của vòng lặp bên trong Cython đang mang lại khả năng tăng tốc rất tốt đối với cả mã Python đơn giản và biểu thức sử dụng hàm tổng tích hợp sẵn của NumPy