2016-02-18 17 views
5

Ho una serie di funzioni che servono per classificare i dati. Ogni funzione riceve lo stesso input. L'obiettivo di questo sistema è quello di essere in grado di inserire nuove funzioni di classificazione a volontà senza dover regolare nulla.Fudging la linea tra classi e funzioni

Per fare ciò, utilizzo una funzione classes_in_module sollevata da here. Quindi, ogni classificatore in un file python verrà eseguito su ogni input.

Tuttavia, sto scoprendo che l'implementazione del classificatore come classe o funzione è kludgy. Le classi significano istanziazione ed esecuzione, mentre le funzioni mancano di introspezione pulita per permettermi di interrogare il nome o utilizzare l'ereditarietà per definire valori comuni.

Ecco un esempio. In primo luogo, l'implementazione della classe:

class AbstractClassifier(object): 
    @property 
    def name(self): 
     return self.__class__.__name__ 

class ClassifierA(AbstractClassifier): 
    def __init__(self, data): 
     self.data = data 
    def run(self): 
     return 1 

Questo può quindi essere utilizzato in questo modo, supponendo che classifier_list è l'uscita di classes_in_module su un file contenente ClassifierA tra gli altri:

result = [] 
for classifier in classifier_list: 
    c = classifier(data) 
    result.append(c.run()) 

Tuttavia, questo sembra un un po 'sciocco. Questa classe è ovviamente statica e non ha realmente bisogno di mantenere il proprio stato, poiché viene usata una volta e scartata. Il classificatore è davvero una funzione, ma poi perdo la possibilità di avere una proprietà condivisa name - Dovrei usare la tecnica di introspezione brutta sys._getframe().f_code.co_name e replicare quel codice per ogni funzione di classificazione. E anche qualsiasi altra proprietà condivisa tra i classificatori andrebbe persa.

Cosa ne pensi? Dovrei semplicemente accettare questo uso sbagliato delle classi? O c'è un modo migliore?

+1

Le funzioni del classificatore devono davvero cercare il proprio nome? – jwodder

+0

Forse il compromesso sarebbe utilizzare il decoratore '@ staticmethod'? – nthall

+0

@jwodder renderebbe le cose più convenienti. Hai ragione, però, che potrei risolvere il problema del nome. Tuttavia, ci sono ancora altre proprietà condivise nella classe base astratta che non ho incluso nell'esempio. –

risposta

1

Se non avete bisogno di diverse istanze della classe (e sembra non) fare un'istanza della classe e modificare la run-__call__:

class AbstractClassifier(object): 
    @property 
    def name(self): 
     return self.__class__.__name__ 

class ClassifierA(AbstractClassifier): 
    def __call__(self, data): 
     return 1 
ClassifierA = ClassifierA()  # see below for alternatives 

e poi nel vostro altro codice :

result = [] 
for classifier in classifier_list: 
    result.append(classifier(data)) 

Invece di avere ClassifierA = ClassifierA() (che non è molto elegante), si potrebbe fare:

classifier_list = [c() for c in (ClassifierA, ClassifierB, ...)] 

Questo metodo consente di mantenere le classi a portata di mano nel caso in cui sia necessario crearne altre; se non mai bisogno di avere più di un'istanza si potrebbe usare un decoratore per IAYG (istanziare come si va;):

def instantiate(cls): 
    return cls() 

@instantiate 
class ClassifierZ(object): 
    def __call__(self, data): 
     return some_classification 
+1

C'è un errore di sintassi sull'ultima riga (punto extra). –

+1

Questo dovrebbe essere 'ClassifierA()()', giusto? Una volta per istanziare, quindi una seconda coppia di parentesi per chiamare effettivamente. –

+0

@ SeánHayes: Ah, grazie! –

1

Per utilizzare un'istanza di classe in funzione:

class ClassifierA(AbstractClassifier): 
    def __init__(self, data): 
     self.data = data 
    def __call__(self): 
     return 1 

result = [] 
for classifier in classifier_list: 
    c = classifier(data) 
    result.append(c()) 

Oppure per utilizzare solo le funzioni:

classifier_list = [] 
def my_decorator(func): 
    classifier_list.append(func) 
    return func 

@my_decorator 
def classifier_a(data): 
    return 1 

result = [] 
for classifier in classifier_list: 
    c = classifier(data) 
    result.append(c) 
2

Le funzioni possono contenere dati membro. Puoi anche trovare il nome di una funzione usando l'attributo func_name.

def classifier(data): 
    return 1 
classifier.name = classifier.func_name 

print(classifier.name) #classifier 

Se si desidera che più funzioni si comportino nello stesso modo, è possibile utilizzare un decoratore.

function_tracker = [] 

def add_attributes(function): 
    function.name = function.func_name 
    function.id = len(function_tracker) 
    function_tracker.append(function) 
    return function 

@add_attributes 
def classifier(data): 
    return 1 

print(classifier.name, classifier.id) # 'classifier', 0 

Sarebbe questo lavoro per evitare classi nel vostro caso specifico?

Problemi correlati