Skip to content

BUG: Inconsistent behavior for derived array types with numpy functions #30551

@mmohrhard

Description

@mmohrhard

Describe the issue:

I'm not certain whether this is an issue with the implementation or the documentation but it seems that the two are not in sync. From the documentation of __array_finalize__ for derived np.ndarray types we find the following statement:

One sees that the super call, which goes to ndarray.__new__, passes __array_finalize__ the new object, of our own class (self) as well as the object from which the view has been taken (obj). As you can see from the output above, the self is always a newly created instance of our subclass, and the type of obj differs for the three instance creation methods:

When called from the explicit constructor, obj is None

When called from view casting, obj can be an instance of any subclass of ndarray, including our own.

When called in new-from-template, obj is another instance of our own subclass, that we might use to update the new self instance.

In the provided example case we have a case where a new object of the derived class has been created, __new__ has not been called and yet __array_finalize__ has been called with obj == None

As far as I can see this happens because internally the new array is allocated with tp_alloc and tp_new is never called but then __array_finalize__ is being called explicitly in the C code.

Note that the code example in the documentation shows the same pattern and therefore exhibits the same problem.

Reproduce the code example:

import numpy as np


class A(np.ndarray):

    def __new__(cls, shape, dtype):
        array = np.array(shape, dtype=dtype)
        out = array.view(cls)
        out.custom_attribute = 1

        return out

    def __array_finalize__(self, obj):
        if obj is None:
            return

        self.custom_attribute = 2


a = A(shape=(2, 3), dtype=np.uint8)
print(a.custom_attribute)

b = np.unpackbits(a)
print(b.custom_attribute)

Error message:

1
Traceback (most recent call last):
  File "/temp/numpy_bug_report/test.py", line 24, in <module>
    print(b.custom_attribute)
          ^^^^^^^^^^^^^^^^^^
AttributeError: 'A' object has no attribute 'custom_attribute'

Python and NumPy Versions:

2.4.0
3.14.2 | packaged by conda-forge | (main, Dec  6 2025, 11:21:58) [GCC 14.3.0]

Runtime Environment:

[{'numpy_version': '2.4.0',
  'python': '3.14.2 | packaged by conda-forge | (main, Dec  6 2025, 11:21:58) '
            '[GCC 14.3.0]',
  'uname': uname_result(system='Linux', node='host', release='3.10.0-1160.80.1.1.el7.dug.x86_64', version='#1 SMP Tue Nov 22 09:43:42 GMT 2022', machine='x86_64')},
 {'simd_extensions': {'baseline': ['X86_V2'],
                      'found': ['X86_V3'],
                      'not_found': ['X86_V4', 'AVX512_ICL', 'AVX512_SPR']}},
 {'ignore_floating_point_errors_in_matmul': False},
 {'architecture': 'Haswell',
  'filepath': '/envs/numpy_bugreport/lib/libopenblasp-r0.3.30.so',
  'internal_api': 'openblas',
  'num_threads': 40,
  'prefix': 'libopenblas',
  'threading_layer': 'pthreads',
  'user_api': 'blas',
  'version': '0.3.30'}]

How does this issue affect you or how did you find it:

This pattern of a derived np.ndarray class can for example be found in the devito project: https://github.com/devitocodes/devito/blob/f020eb1616cbe8ccd864b416649b5db1e0a0f3ef/devito/data/data.py#L99

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions