2015-01-05 19 views
7

Giả sử tôi nhập:Python: nhầm lẫn giữa các loại và dtypes

a = uint8(200) 
a*2 

Sau đó, kết quả là 400, và nó được viết lại là kiểu uint16.

Tuy nhiên:

a = array([200],dtype=uint8) 
a*2 

và kết quả là

array([144], dtype=uint8) 

Các nhân đã được thực hiện modulo 256, để đảm bảo rằng kết quả nằm trong một byte.

Tôi bị nhầm lẫn về "loại" và "dtypes" và vị trí được sử dụng tùy theo loại khác. Và như bạn thấy, loại có thể tạo ra sự khác biệt đáng kể trong đầu ra.

Ví dụ: tôi có thể tạo một số uint8 dtype để hoạt động trên số đó sẽ được thực hiện modulo 256 không? Ngoài ra, tôi có thể tạo một mảng kiểu uint8 kiểu (không phải dtype) để các phép toán trên nó sẽ tạo ra các giá trị ngoài phạm vi 0-255 không?

Trả lời

2

Mảng sumpy chứa các phần tử cùng loại, vì vậy np.array([200],dtype=uint8)một mảng có một giá trị loại uint8. Khi bạn thực hiện np.uint8(200), bạn không có mảng, chỉ một giá trị. Điều này tạo nên sự khác biệt lớn.

Khi thực hiện một số thao tác trên mảng, loại này vẫn giữ nguyên, không phân biệt giá trị đơn có tràn hay không. Tự động upcasting trong mảng bị cấm, vì kích thước của toàn bộ mảng phải thay đổi. Điều này chỉ được thực hiện nếu người dùng rõ ràng muốn điều đó. Khi thực hiện một phép toán trên một giá trị duy nhất, nó có thể dễ dàng upcast, không ảnh hưởng đến các giá trị khác.

8

type của mảng NumPy là numpy.ndarray; đây chỉ là kiểu đối tượng Python (tương tự như cách type("hello") là ví dụ str).

dtype chỉ xác định cách byte trong bộ nhớ sẽ được diễn giải bởi vô hướng (ví dụ: một số) hoặc mảng và cách thức xử lý byte (ví dụ: int/float). Vì lý do đó bạn không thay đổi type của một mảng hoặc vô hướng, chỉ cần dtype của nó.

Khi bạn quan sát, nếu bạn nhân hai số vô hướng, kiểu dữ liệu kết quả là loại "an toàn" nhỏ nhất mà cả hai giá trị đều có thể truyền. Tuy nhiên, nhân một mảng và một vô hướng sẽ đơn giản trả về một mảng của cùng một kiểu dữ liệu. Các documentation cho hàm là rõ ràng khi một vô hướng cụ thể hoặc mảng đối tượng của dtype được thay đổi:

Loại thăng tiến trong NumPy hoạt động tương tự các quy tắc trong các ngôn ngữ như C++, với một số khác biệt nhỏ. Khi cả hai vô hướng và mảng được sử dụng, loại của mảng được ưu tiên và giá trị thực tế của vô hướng được tính đến.

Các tài liệu tiếp tục:

Nếu chỉ có vô hướng hoặc hạng mục tối đa của vô hướng cao hơn so với loại tối đa của mảng, các kiểu dữ liệu được kết hợp với promote_types để tạo ra giá trị trả về .

Vì vậy, đối np.uint8(200) * 2, hai vô hướng, các kiểu dữ liệu kết quả sẽ là kiểu trả về bởi np.promote_types:

>>> np.promote_types(np.uint8, int) 
dtype('int32') 

Đối np.array([200], dtype=np.uint8) * 2 datatype của mảng được ưu tiên hơn các vô hướng int và một datatype np.uint8 được trả về.

Để giải quyết câu hỏi cuối cùng của bạn về giữ lại dtype của một đại lượng vô hướng trong các hoạt động, bạn sẽ phải hạn chế các kiểu dữ liệu của bất kỳ vô hướng khác mà bạn sử dụng để tránh tự động dtype xúc tiến NumPy của:

>>> np.array([200], dtype=np.uint8) * np.uint8(2) 
144 

Việc thay thế, tất nhiên, chỉ đơn giản là bọc giá trị đơn lẻ trong một mảng NumPy (và sau đó NumPy sẽ không đúc nó trong các hoạt động với vô hướng khác nhau dtype).

Đẩy mạnh các loại hình một mảng trong khi hoạt động, bạn có thể quấn bất kỳ vô hướng trong một mảng đầu tiên:

>>> np.array([200], dtype=np.uint8) * np.array([2]) 
array([400]) 
+0

"' dtype' chỉ định nghĩa cách byte trong bộ nhớ sẽ được giải thích bởi một vô hướng" → nó cũng xác định cách thức mà họ đang giải thích (ví dụ. 'Int32' vs' float32'). – Veedrac

+0

Cảm ơn, Veedrac, tôi đã thực hiện câu đó chính xác hơn một chút. –

2

Các đơn giản, cao cấp câu trả lời là NumPy lớp một hệ thống kiểu thứ hai trên đỉnh loại Python hệ thống.

Khi bạn yêu cầu type đối tượng NumPy, bạn nhận được loại vùng chứa - một cái gì đó như numpy.ndarray. Nhưng khi bạn yêu cầu dtype, bạn sẽ nhận được loại (một phần được quản lý gọn gàng) của các yếu tố .

>>> from numpy import * 
>>> arr = array([1.0, 4.0, 3.14]) 
>>> type(arr) 
<type 'numpy.ndarray'> 
>>> arr.dtype 
dtype('float64') 

Đôi khi, như khi sử dụng các loại mặc định float, các kiểu dữ liệu nguyên tố (dtype) là tương đương với một loại Python. Nhưng đó là tương đương, không giống hệt nhau:

>>> arr.dtype == float 
True 
>>> arr.dtype is float 
False 

Trong trường hợp khác, không có loại Python tương đương. Ví dụ: khi bạn chỉ định uint8. Các giá trị/kiểu dữ liệu như vậy có thể được Python quản lý, nhưng không giống như trong C, Rust và các ngôn ngữ hệ thống khác, quản lý các giá trị thẳng hàng với các kiểu dữ liệu máy (như uint8 liên kết chặt chẽ với tính toán "unsigned bytes") use-case cho Python.

Vì vậy, câu chuyện lớn là NumPy cung cấp các vùng chứa như mảng và ma trận hoạt động theo hệ thống kiểu riêng của nó. Và nó cung cấp một loạt các thói quen hữu ích, được tối ưu hóa tốt để hoạt động trên các thùng chứa đó (và các phần tử của chúng). Bạn có thể trộn và so khớp NumPy và tính toán Python bình thường, nếu bạn sử dụng dịch vụ chăm sóc.

Không có loại Python uint8.Có một hàm constructor tên uint8, mà khi gọi trở lại một loại NumPy: "Tôi có thể tạo ra một loạt các loại (không dtype) uint8 ..."

>>> u = uint8(44) 
>>> u 
44 
>>> u.dtype 
dtype('uint8') 
>>> type(u) 
<type 'numpy.uint8'> 

Vì vậy, Không, bạn không thể. Không có động vật như vậy. Bạn có thể tính toán bị ràng buộc thành các quy tắc uint8 mà không sử dụng NumPy arrays (giá trị vô hướng NumPy). Ví dụ:

>>> uint8(44 + 1000) 
20 
>>> uint8(44) + uint8(1000) 
20 

Nhưng nếu bạn muốn để tính toán giá trị mod 256, nó có thể dễ dàng hơn để sử dụng mod điều hành của Python:

>> (44 + 1000) % 256 
20 

Driving dữ liệu giá trị lớn hơn 255 thành uint8 kiểu dữ liệu và sau đó thực hiện số học là một cách khá xa để có được số học mod-256. Nếu bạn không cẩn thận, bạn sẽ khiến Python "nâng cấp" giá trị của bạn thành số nguyên đầy đủ (tiêu diệt mod-256), hoặc kích hoạt các ngoại lệ tràn (vì các thủ thuật hoạt động tốt trong ngôn ngữ C và máy thường được gắn cờ bởi ngôn ngữ cấp cao hơn).