Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Module for wrapping an array without being an array.
3This can be desirable because we would like atoms.cell to be like an array,
4but we don't like atoms.positions @ atoms.cell to also be a cell, and as far
5as I/we know, it's not possible to subclass array without that kind of thing
6happening.
8For most methods and attributes we can just bind the name as a property:
10 @property
11 def reshape(self):
12 return np.asarray(self).reshape
14For 'in-place' operations like += we need to make sure to return self
15rather than the array though:
17 def __iadd__(self, other):
18 np.asarray(self).__iadd__(other)
19 return self
21This module provides the @arraylike decorator which does these things
22for all the interesting ndarray methods.
23"""
26from functools import update_wrapper
27import numpy as np
30inplace_methods = ['__iadd__', '__imul__', '__ipow__', '__isub__',
31 '__itruediv__', '__imatmul__']
33forward_methods = ['__abs__', '__add__', '__contains__', '__eq__',
34 '__ge__', '__getitem__', '__gt__', '__hash__',
35 '__iter__', '__le__', '__len__', '__lt__',
36 '__mul__', '__ne__', '__neg__', '__pos__',
37 '__pow__', '__radd__', '__rmul__', '__rpow__',
38 '__rsub__', '__rtruediv__', '__setitem__',
39 '__sub__', '__truediv__']
41default_methods = ['__eq__', '__le__', '__lt__', '__ge__',
42 '__gt__', '__ne__', '__hash__']
44if hasattr(np.ndarray, '__matmul__'):
45 forward_methods += ['__matmul__', '__rmatmul__']
48def forward_inplace_call(name):
49 arraymeth = getattr(np.ndarray, name)
51 def f(self, obj):
52 a = self.__array__()
53 arraymeth(a, obj)
54 return self
56 update_wrapper(f, arraymeth)
57 return f
60def wrap_array_attribute(name):
61 wrappee = getattr(np.ndarray, name)
62 if wrappee is None: # For example, __hash__ is None
63 assert name == '__hash__'
64 return None
66 def attr(self):
67 array = np.asarray(self)
68 return getattr(array, name)
70 update_wrapper(attr, wrappee)
72 # We don't want to encourage too liberal use of the numpy methods,
73 # nor do we want the web docs to explode with numpy docstrings or
74 # break our own doctests.
75 #
76 # Therefore we cheat and remove the docstring:
77 attr.__doc__ = None
78 return property(attr)
81def arraylike(cls):
82 """Decorator for being like an array without being an array.
84 Poke decorators onto cls so that getting an attribute
85 really gets that attribute from the wrapped ndarray.
87 Exceptions are made for in-place methods like +=, *=, etc.
88 These must return self since otherwise myobj += 1 would
89 magically turn into an ndarray."""
90 for name in inplace_methods:
91 if hasattr(np.ndarray, name) and not hasattr(cls, name):
92 meth = forward_inplace_call(name)
93 setattr(cls, name, meth)
95 allnames = [name for name in dir(np.ndarray) if not name.startswith('_')]
96 for name in forward_methods + allnames:
97 if hasattr(cls, name) and name not in default_methods:
98 continue # Was overridden -- or there's a conflict.
100 prop = wrap_array_attribute(name)
101 setattr(cls, name, prop)
102 return cls