2011-01-21 14 views
6

[Python 3]numpy.ndarray: la conversione in un "normale" classe

mi piace ndarray ma trovo fastidioso da usare.

Ecco un problema che ho di fronte. Voglio scrivere class Array che erediterà gran parte delle funzionalità di ndarray, ma ha solo un modo per essere istanziato: come un array pieno di zero di una certa dimensione. Speravo di scrivere:

class Array(numpy.ndarray): 
    def __init__(size): 
    # What do here? 

mi piacerebbe chiamare super().__init__ con alcuni parametri per creare una matrice zero pieno, ma non funzionerà in quanto ndarray utilizza una funzione globale numpy.zeros (piuttosto che un costruttore) per creare un array pieno di zero.

Domande:

  1. Perché ndarray utilizzare le funzioni globali (modulo) invece di costruttori, in molti casi? È un grosso fastidio se sto cercando di riutilizzarli in un ambiente orientato agli oggetti.

  2. Qual è il modo migliore per definire class Array di cui ho bisogno? Devo compilare manualmente lo ndarray con zero o esiste un modo per riutilizzare la funzione zeros?

+2

Non vedo perché tu voglia creare la tua classe. Qual è il vantaggio rispetto alla funzione di fabbrica 'numpy.zeros()'? Se il nome non ti piace, basta rinominarlo, come 'create_array = numpy.zeros'. –

+0

Non sono più sicuro che ci sia un vantaggio. Forse non sono abituato alle funzioni di fabbrica. Dovrò pensarci. – max

+0

@Sven Marnach: ho trovato diversi link su questo tema generale: http://stackoverflow.com/questions/628950/constructors-vs-factory-methods http://stackoverflow.com/questions/2959871/factory -vs-instance-constructors http://stackoverflow.com/questions/4617311/creation-of-objects-constructors-or-static-factory-methods Niente di specifico per Python, ma i commenti generali di altre lingue sembrano applicare. E per quanto posso dire, non c'è davvero alcun svantaggio per le funzioni di fabbrica (a parte le mie preferenze personali). – max

risposta

6

Se non vi piace l'interfaccia ndarray allora non ereditare. È possibile definire la propria interfaccia e delegare il resto a ndarray e numpy.

import functools 
import numpy as np 


class Array(object): 

    def __init__(self, size): 
     self._array = np.zeros(size) 

    def __getattr__(self, attr): 
     try: return getattr(self._array, attr) 
     except AttributeError: 
      # extend interface to all functions from numpy 
      f = getattr(np, attr, None) 
      if hasattr(f, '__call__'): 
       return functools.partial(f, self._array) 
      else: 
       raise AttributeError(attr) 

    def allzero(self): 
     return np.allclose(self._array, 0) 


a = Array(10) 
# ndarray doesn't have 'sometrue()' that is the same as 'any()' that it has. 
assert a.sometrue() == a.any() == False 
assert a.allzero() 

try: a.non_existent 
except AttributeError: 
    pass 
else: 
    assert 0 
+0

Molto pulito. Suppongo che tutte le funzioni globali di 'numpy' abbiano la stessa semantica quando vengono chiamate con l'argomento' ndarray' come metodo di istanza dello stesso nome di 'ndarray'. – max

+0

Per passare a Python 3, rimuovo '(oggetto)' dalla definizione della classe, corretto? [Non che lasciarlo potrebbe causare problemi.] – max

+0

@max: il codice funziona senza modifiche sia su 2.x che su Python 3. – jfs

7

Perché ndarray utilizzare le funzioni globali (modulo) invece di costruttori, in molti casi?

  1. per essere compatibile/simile a Matlab, in cui funzioni come zeros o ones proviene.
  2. Le funzioni globali di fabbrica sono veloci da scrivere e facili da capire. Quale dovrebbe essere la semantica di un costruttore, ad es. come esprimeresti un semplice zeros o empty o ones con un singolo costruttore? In effetti, tali funzioni di fabbrica sono abbastanza comuni, anche in altri linguaggi di programmazione.

Qual è il modo migliore per definire class Array che ho bisogno?

import numpy 

class Array(numpy.ndarray): 
    def __new__(cls, size): 
     result = numpy.ndarray.__new__(Array, size) 
     result.fill(0) 
     return result 
arr = Array(5) 

def test(a): 
    print type(a), a 

test(arr) 
test(arr[2:4]) 
test(arr.view(int)) 

arr[2:4] = 5.5 

test(arr) 
test(arr[2:4]) 
test(arr.view(int)) 

notare che questa è Python 2, ma richiederebbe solo piccole modifiche per funzionare con Python 3.

+1

Alcune osservazioni: 1. Il parametro 'shape' del costruttore' ndarray' può essere semplicemente un numero intero anziché una tupla. 2. 'dtype = float' è il valore predefinito. 3. In Python 3, puoi omettere gli argomenti in 'super()' in questo caso. Combinando queste osservazioni, le prime due linee del costruttore si riducono a 'super() .__ init __ (size)'. –

+0

Oh, e ho appena notato che la tua chiamata 'super()' è sbagliata - dovrebbe essere 'super (Array, self)' in Python 2.x. –

+0

Hai ragione, ho modificato la mia risposta. (La firma del costruttore non è documentata, come al solito.) – Philipp

4

L'ereditarietà di ndarray è un po 'complicata. ndarray non ha nemmeno il metodo __init(self,)___, quindi non può essere chiamato dalla sottoclasse, ma ci sono dei motivi. Si prega di consultare la documentazione numpy di subclassing.

A proposito, potresti essere più specifico delle tue esigenze particolari?È ancora abbastanza facile preparare una classe (utilizzando ndarray) per le proprie esigenze, ma una sottoclasse di ndarray per passare tutti i macchinosi è un problema molto diverso.

Sembra che non posso commentare il mio post a se stante, strano
@Philipp: Si chiamerà da Python, ma non da NumPy. Esistono tre modi per istanziare ndarray e le linee guida su come gestire tutti i casi sono fornite su quel documento.

+1

Mi sembra di aver perso questo documento. Ma perché nel mio esempio è chiamato il metodo '__init__' anche se' ndarray' aveva '__new__'? – Philipp

+0

@Philipp: guarda il mio commento alla tua risposta. – max

+0

Volevo solo la mia classe che controllo completamente come viene istanziata. Hai ragione la sottoclasse di narray è troppo difficile. Seguirò l'approccio contenitore in @ J.F. Sebastian. – max

Problemi correlati