2016-07-06 11 views
6

Trong một dự án, tôi đã tạo ra một lớp, và tôi cần một hoạt động giữa lớp mới này và một ma trận thực, vì vậy tôi bị quá tải các __rmul__ chức năng như thế nàyArray và __rmul__ nhà điều hành trong Python NumPy

class foo(object): 

    aarg = 0 

    def __init__(self): 
     self.aarg = 1 


    def __rmul__(self,A): 
     print(A) 
     return 0 

    def __mul__(self,A): 
     print(A) 
     return 0 

nhưng khi tôi gọi nó, kết quả là không phải những gì tôi mong đợi

A = [[i*j for i in np.arange(2) ] for j in np.arange(3)] 
A = np.array(A) 
R = foo() 
C = A * R 

Output:

0 
0 
0 
1 
0 
2 

tôi t dường như chức năng này được gọi là 6 lần, một lần cho mỗi phần tử.

Thay vào đó, __mul__ chức năng làm việc rất

C = R * A 

Output:

[[0 0] 
[0 1] 
[0 2]] 

Nếu A không phải là một mảng, nhưng chỉ có một danh sách liệt kê, cả hai làm việc tốt

A = [[i*j for i in np.arange(2) ] for j in np.arange(3)] 
R = foo() 
C = A * R 
C = R * A 

Đầu ra

[[0, 0], [0, 1], [0, 2]] 
[[0, 0], [0, 1], [0, 2]] 

Tôi thực sự muốn chức năng __rmul__ của mình để hoạt động trên mảng (hàm nhân bản gốc của tôi không giao hoán). Làm thế nào tôi có thể giải quyết nó?

+0

Bạn cảm thấy thế nào về việc kiểm tra loại 'A'? –

Trả lời

4

Hành vi được mong đợi.

Trước hết, bạn phải hiểu cách hoạt động như x*y thực sự được thực hiện. Trình thông dịch trăn sẽ trước tiên cố gắng tính x.__mul__(y). Nếu cuộc gọi này trả lại NotImplemented, nó sẽ rồi cố gắng tính y.__rmul__(x). Ngoại trừ khi y là một lớp con thích hợp của loại x, trong trường hợp này, thông dịch viên đầu tiên sẽ xem xét y.__rmul__(x) và sau đó x.__mul__(y).

Bây giờ điều xảy ra là numpy xử lý các đối số khác nhau tùy thuộc vào việc anh ta có nghĩ đối số là vô hướng hoặc mảng hay không.

Khi xử lý các mảng * nhân nhân tố theo phần tử, trong khi phép nhân vô hướng nhân tất cả các mục nhập của một mảng theo vô hướng đã cho.

Trong trường hợp của bạn, foo() được coi là vô hướng bởi vón cục và do đó nhân rộng tất cả các phần tử của mảng theo foo.Hơn nữa, vì NumPy không biết về loại foo nó trả về một mảng với dtype=object, vì vậy các đối tượng quay trở lại là:

array([[0, 0], 
     [0, 0], 
     [0, 0]], dtype=object) 

Lưu ý: mảng numpy 's không không trở NotImplemented khi bạn cố gắng tính toán sản phẩm, do đó, thông dịch viên gọi phương thức __mul__ của numpy, thực hiện phép nhân vô hướng như chúng ta đã nói. Tại thời điểm này, sẽ cố gắng nhân mỗi mục của mảng bằng "vô hướng" foo() và đây là nơi mà phương thức __rmul__ của bạn được gọi, vì các số trong mảng trả về NotImplemented khi số __mul__ của chúng được gọi với đối số foo.

Rõ ràng nếu bạn thay đổi thứ tự của đối số thành phép nhân ban đầu, phương thức __mul__ của bạn sẽ được gọi ngay lập tức và bạn không gặp bất kỳ sự cố nào.

Vì vậy, để trả lời câu hỏi của bạn, một cách để xử lý này là phải có foo kế thừa từ ndarray, do đó nguyên tắc thứ hai được áp dụng:

class foo(np.ndarray): 
    def __new__(cls): 
     # you must implement __new__ 
    # code as before 

Cảnh báo tuy nhiên đó subclassing ndarray isn't straightforward. Hơn nữa, bạn có thể có các tác dụng phụ khác, vì giờ đây lớp học của bạn là ndarray.

+0

Bởi vì logic nào quyết định rằng các cá thể 'foo' là vô hướng? – wim

+0

@wim Nếu nó không phải là một mảng, nó là một vô hướng. Đơn giản vậy thôi. – Bakuriu

1

Tôi không thể giải thích vấn đề cơ bản chính xác như Bakuriu, nhưng có thể có một giải pháp khác.

Bạn có thể bắt buộc sử dụng phương pháp đánh giá của mình bằng cách xác định __array_priority__. Như được giải thích here trong tài liệu gọn gàng.

Trong trường hợp của bạn, bạn đã phải thay đổi định nghĩa lớp của bạn để:

MAGIC_NUMBER = 15.0 
# for the necessary lowest values of MAGIC_NUMBER look into the numpy docs 
class foo(object): 
    __array_priority__ = MAGIC_NUMBER 
    aarg = 0 

    def __init__(self): 
     self.aarg = 1 


    def __rmul__(self,A): 
     print(A) 
     return 0 

    def __mul__(self,A): 
     print(A) 
     return 0 
0

Bạn có thể xác định __numpy_ufunc__ chức năng trong lớp học của bạn. Nó hoạt động ngay cả mà không có phân lớpnp.ndarray. Bạn có thể tìm tài liệu ở đây: https://docs.scipy.org/doc/numpy/neps/ufunc-overrides.html

Dưới đây là một ví dụ dựa trên trường hợp của bạn:

class foo(object): 

    aarg = 0 

    def __init__(self): 
     self.aarg = 1 

    def __numpy_ufunc__(self, *args): 
     pass 

    def __rmul__(self,A): 
     print(A) 
     return 0 

    def __mul__(self,A): 
     print(A) 
     return 0 

Và nếu chúng ta thử nó,

A = [[i*j for i in np.arange(2) ] for j in np.arange(3)] 
A = np.array(A) 
R = foo() 
C = A * R 

Output:

[[0 0] 
[0 1] 
[0 2]] 

Nó hoạt động!