Numpy has a type hint ArrayLike that is supposed to cover pretty much all objects you might want to convert to a numpy array (e.g. lists, objects implementing __array__ and some more).
It can be used like any type hint:
from numpy.typing import ArrayLike
def some_func(a : ArrayLike):
print(a)
But it might happen that you want check whether an object is an ArrayLike during runtime:
def some_func(a):
if type(a) == ArrayLike: # Does not work
print(a)
if issubclass(a, ArrayLike): # Raises an error
print(a)
if isinstance(a, ArrayLike): # Raises an error
print(a)
Is something like this possible using ArrayLike? Or should I use another method?
MWE:
import numpy as np
from numpy.typing import ArrayLike
class MyArrayLike:
def __array__(self, dtype = None):
return np.asarray([0, 1])
def some_func(a : ArrayLike):
if type(a) == ArrayLike:
print(a)
# if issubclass(a, ArrayLike):
# print(a)
# if isinstance(a, ArrayLike):
# print(a)
a = MyArrayLike()
some_func(a)
Related
from numpy import *
arr = array([1,2,3,4])
Here array() is a function so arr is not an object (I hope so).
Because an object is created using following syntax:
obj = class_name()
and array() is afunction
For eg :-
a = int(1) here int is a class
here a is an object
and i know python use object literal for our convenience hence we write it as a = 1
from numpy import *
arr = array([1,2,3,4])
But in the above case how arr is treated as an object I am telling this because
from numpy import *
arr = array([1,2,3,4])
print(arr.dtype)
In the above code we can see dtype() is called with the help of arr as an object
How is this possible ?
I just want to know how arr is treated as an object ?
I currently have this code
T = TypeVar("T")
Grid = Sequence[Sequence[T]]
def columns(grid: Grid) -> Iterable[list[T]]:
return ([row[i] for row in grid] for i in range(len(grid[0])))
But I think the T in the alias Grid is bound to a different T in the return type of the function.
How do I define Grid such that I can write
def columns(grid: Grid[T]) -> Iterable[list[T]]:
...
I've looked at typing.GenericAlias, but can't see how it helps me.
(I'm aware that Sequence[Sequence[T]] has no guarantee that the grid is actually rectangular, but that's not the problem I want to focus on here.)
When using type variable as a generic parameter, it can be replaced by other type variables, which is mentioned in the Generic Alias Type (but I only found this one):
The __getitem__() method of generic containers will raise an exception to disallow mistakes like dict[str][str]:
>>> dict[str][str]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: There are no type variables left in dict[str]
However, such expressions are valid when type variables are used. The index must have as many elements as there are type variable items in the GenericAlias object’s __args__.
>>> from typing import TypeVar
>>> Y = TypeVar('Y')
>>> dict[str, Y][int]
dict[str, int]
So there is no problem with your current implementation. In the interactive interpreter, you will see:
>>> from collections.abc import Sequence
>>> from typing import TypeVar
>>> T, R = TypeVar('T'), TypeVar('R')
>>> Grid = Sequence[Sequence[T]]
>>> Grid
collections.abc.Sequence[collections.abc.Sequence[~T]]
>>> Grid[R]
collections.abc.Sequence[collections.abc.Sequence[~R]]
Mypy will also correctly analyze them:
from collections.abc import Sequence, Iterable
from typing import TypeVar
T = TypeVar('T')
Grid = Sequence[Sequence[T]]
def columns(grid: Grid[T]) -> Iterable[list[T]]:
return ([row[i] for row in grid] for i in range(len(grid[0])))
c1: Iterable[list[int]] = columns([[1, 2, 3]]) # pass
c2: Iterable[list[int]] = columns([[4, 5, '6']])
# List item 2 has incompatible type "str"; expected "int" (13:42)
I have a function fun() that accepts a NumPy ArrayLike and a "matrix", and returns a numpy array.
from numpy.typing import ArrayLike
import numpy as np
def fun(A, x: ArrayLike) -> np.ndarray:
return (A # x) ** 2 - 27.0
What's the correct type for entities that have an # operation? Note that fun() could also accept a scipy.sparse; perhaps more.
You can use typing.Protocol to assert that the type implements __matmul__.
class SupportsMatrixMultiplication(typing.Protocol):
def __matmul__(self, x):
...
def fun(A: SupportsMatrixMultiplication, x: ArrayLike) -> np.ndarray:
return (A # x) ** 2 - 27.0
You can, I believe, further refine this by providing type hints for x and a return type hint, if you want more than just supporting # as an operator.
I am using the numpy.random.choice module to generate an 'array' of choices based on an array of functions:
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base=[f, g]
funcs=np.random.choice(base,size=2)
This code will produce an 'array' of 2 items referencing a function from the base array.
The reason for this post is, I have printed the outcome of funcs and recieved:
[<function f at 0x00000225AC94F0D0> <function f at 0x00000225AC94F0D0>]
Clearly this returns a reference to the functions in some form, not that I understand what that form is or how to manipulate it, this is where the problem comes in. I want to change the choice of function, so that it is no longer random and instead depends on some conditions, so it might be:
for i in range(2):
if testvar=='true':
choice[i] = 0
if testvar== 'false':
choice[i] = 1
This would return an array of indicies to be put in later function
The problem is, the further operations of the code (I think) require this previous form of function reference: [ ] as an input, instead of a simple array of 0,1 Indicies and I don't know how I can get an array of form [ ] by using if statements.
I could be completely wrong about the rest of the code requiring this input, but I don't know how I can amend it, so am hence posting it here. The full code is as follows: (it is a slight variation of code provided by #Attack68 on Evolving functions in python) It aims to store a function that is multiplied by a random function on each iteration and integrates accordingly. (I have put a comment on the code above the function that is causing the problem)
import numpy as np
import scipy.integrate as int
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base = [f, g]
funcs = np.random.choice(base, size=2)
print(funcs)
#The below function is where I believe the [<function...>] input to be required
def apply(x, funcs):
y = 1
for func in funcs:
y *= func(x)
return y
print('function value at 1.5 ', apply(1.5, funcs))
answer = int.quad(apply, 1, 2, args=(funcs,))
print('integration over [1,2]: ', answer)
Here is my attempt of implementing a non-random event:
import numpy as np
import scipy.integrate as int
import random
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base = [f, g]
funcs = list()
for i in range(2):
testvar=random.randint(0,100) #In my actual code, this would not be random but dependent on some other situation I have not accounted for here
if testvar>50:
func_idx = 0 # choose a np.random operation: 0=f, 1=g
else:
func_idx= 1
funcs.append(func_idx)
#funcs = np.random.choice(base, size=10)
print(funcs)
def apply(x, funcs):
y = 1
for func in funcs:
y *= func(x)
return y
print('function value at 1.5 ', apply(1.5, funcs))
answer = int.quad(apply, 1, 2, args=(funcs,))
print('integration over [1,2]: ', answer)
This returns the following error:
TypeError: 'int' object is not callable
If: You are trying to refactor your original code that operates on a list of randomly chosen functions to a version that operates with random indices which correspond to items in a list of functions. Refactor apply.
def apply(x,indices,base=base):
y = 1
for i in indices:
f = base[i]
y *= f(x)
return y
...this returns a reference to the functions in some form, not that I understand what that form is or how to manipulate it...
Functions are objects, the list contains a reference to the objects themselves. They can be used by either assigning them to a name then calling them or indexing the list and calling the object:
>>> def f():
... return 'f'
>>> def g():
... return 'g'
>>> a = [f,g]
>>> q = a[0]
>>> q()
'f'
>>> a[1]()
'g'
>>> for thing in a:
print(thing())
f
g
Or you can pass them around:
>>> def h(thing):
... return thing()
>>> h(a[1])
'g'
>>>
If you still want to use your function apply as-is, you need to keep your input a list of functions. Instead of providing a list of indices, you can use those indices to create your list of functions.
Instead of apply(1.5, funcs), try:
apply(1.5, [base(n) for n in funcs])
Given an object that is both iterable but also can efficiently convert itself to a NumPy array, what can I use to help NumPy create an array from it? __array_interface__?
# Naive class that need to be iterated over
class myclass1():
def to_array(self):
return np.arange(4)
def __getitem__(self, i):
if i > 3:
raise IndexError
return i
def __len__(self):
return 4
# "Smart" class that knows how to construct NumPy buffer
class myclass2():
# The result is always the same, but there is no buffer that is
# part of the instance. Instead the buffer is generated when
# calling to_array()
def to_array(self):
return np.arange(4)
#property
def __array_interface__(self):
data = self.to_array()
return {
'shape': (4,),
'data': data,
'typestr': data.dtype.str,
}
a = myclass1()
np.array(a)
b = myclass2()
np.array(b)
Or does this produce memory leaks, double frees or other memory ownership issues?