Qual è il modo migliore per creare una classe di utilità statica in python? Sta usando odore di codice metaclassi?

6

Ok, quindi ho bisogno di creare un gruppo di classi di utilità in python. Normalmente userei solo un semplice modulo per questo, ma ho bisogno di essere in grado di ereditare per condividere il codice comune tra di loro. Il codice comune deve fare riferimento allo stato del modulo utilizzandolo in modo che le importazioni semplici non funzionino correttamente. Non mi piacciono i singletons , e le classi che usano il decoratore classmethod non hanno supporto per le proprietà python.

Un modello che vedo molto usato è la creazione di una classe python interna preceduta da un trattino basso e la creazione di una singola istanza che viene quindi importata o impostata in modo esplicito come modulo stesso . Questo viene anche usato da fabric per creare un oggetto ambiente comune (fabric.api.env).

Ho capito che un altro modo per ottenere questo risultato sarebbe con le metaclassi. Ad esempio:

#util.py
class MetaFooBase(type):
    @property
    def file_path(cls):
        raise NotImplementedError

    def inherited_method(cls):
        print cls.file_path


#foo.py
from util import *
import env

class MetaFoo(MetaFooBase):
    @property
    def file_path(cls):
        return env.base_path + "relative/path"

    def another_class_method(cls):
        pass

class Foo(object): __metaclass__ = MetaFoo


#client.py
from foo import Foo

file_path = Foo.file_path

Mi piace questo approccio meglio del primo modello per alcuni motivi:

In primo luogo, l'istanziazione di Foo sarebbe priva di significato in quanto non ha attributi o metodi, il che assicura che questa classe agisca come una vera e propria utility di interfaccia, diversamente dal primo modello che si basa sulla convenzione di sottolineatura per dissuadere il codice client dalla creazione di più istanze del classe interna.

In secondo luogo, la sottoclassificazione di MetaFoo in un modulo diverso non sarebbe così imbarazzante perché non importerei una classe con un trattino basso che è intrinsecamente contrario alla sua convenzione di denominazione privata.

In terzo luogo, questa sembra essere l'approssimazione più vicina a una classe statica che esiste in python, poiché tutto il codice meta si applica solo alla classe e non alle sue istanze. Ciò è dimostrato dalla comune convenzione sull'uso di cls invece di self nei metodi di classe. Inoltre, la classe base eredita dal tipo anziché dall'oggetto, il che impedirebbe agli utenti di provare a utilizzarlo come base per altre classi non statiche. La sua implementazione come classe statica è evidente anche quando viene utilizzata dalla convenzione di denominazione Foo, al contrario di foo, che indica che viene utilizzato un metodo di classe statico.

Per quanto mi ritenga opportuno, ritengo che altri potrebbero sentirsi non pythonic perché non è un sanzionato uso per metaclassi che dovrebbe essere evitato < em> 99% del tempo. Trovo anche che la maggior parte degli sviluppatori di python tendono a evitare i metaclassi che potrebbero influire sul riutilizzo / manutenibilità del codice. Questo codice è considerato odore di codice nella comunità Python? Chiedo perché sto creando un pacchetto pypi e vorrei fare tutto il possibile per aumentare l'adozione.

    
posta rsimp 17.05.2013 - 19:18
fonte

3 risposte

2

Penso che sia una cattiva idea.

È un uso molto strano delle meta-classi che contribuiranno alla confusione di chiunque cerchi di seguire il tuo codice. Per far sì che valga la pena usare questa tecnica, hai bisogno dei vantaggi per superare quella confusione.

Per i tuoi motivi:

  1. Creare una classe Foo accessibile che può essere istanziata, ma che in realtà non funziona come un oggetto tipico è molto più incline a un uso improprio di una classe interna che sai che non dovresti chiamare.
  2. Non nominare la classe base con un trattino basso. Se è destinato ad essere ereditato da altri moduli, non dovrebbe essere chiamato così.
  3. Esiste già un metodo per implementare le classi statiche in python, staticmethod o classmethod . Come notato, non è difficile implementare un staticproperty

In generale, c'è un tema nel tuo approccio per cercare di impedire al codice client di fare la cosa sbagliata. Non è un atteggiamento pitonico. L'approccio pitone è quello di fidarsi del codice cliente. Se vogliono fare qualcosa di pazzo, lasciali fare. Non cercare di impedire al client di creare un'istanza dell'oggetto interno, potrebbe esserci una buona ragione per farlo.

Sono un po 'purista e penso che non dovresti memorizzare uno stato come questo. Quindi il fatto stesso che tu stia facendo la domanda significa che lo stai già facendo male. Se hai lo stato, penso che dovrebbe essere in un oggetto. Penso che sia necessario evitare qualsiasi tipo di stato a livello di modulo. Penso che sia una buona idea implementare qualcosa del genere se non hai uno stato di cambiamento.

Per chiudere, mi piacerebbe collegare lo Scambio di stack di revisione del codice , dove sono un moderatore. Se pubblichi il tuo codice, puoi ottenere suggerimenti su come migliorarlo. Penso che potresti ricevere alcuni suggerimenti utili su come ristrutturare il tuo codice per evitare che questa domanda venga visualizzata.

    
risposta data 29.06.2013 - 08:49
fonte
0

I metaclassi sono uno strumento potente. Il più delle volte non sono necessari, ma sembra che tu abbia solide ragioni per farlo. Non vedo il tuo uso di metaclassi, o la sua mancanza, che influisce sull'adozione del tuo pacchetto su PyPI.

    
risposta data 30.05.2013 - 06:56
fonte
0

Se avessi davvero bisogno di questo livello di configurabilità, creerei delle classi factory che incollano insieme le classi di utilità e le restituiscono come oggetti.

Il meccanismo che menzioni per la prima volta è abbastanza buono, a parte l'assegnazione fino al modulo. Puoi solo restituirli e memorizzarli in uno spazio dei nomi normale come un normale umano. Non c'è bisogno di aggirare i confini logici e fare in modo che tutti cerchino ovunque il tuo codice.

Quindi puoi creare codice che assomigli a stereotipi di diversi aspetti delle tue classi e fare affidamento su una semplice ereditarietà di mix-in per comporre le tue classi di fabbrica. Questo è più semplice del metaclassing, che comporta la modifica di dizionari di metodi, ecc., In modi che non assomiglino a ciò che sono: codice di scrittura del codice che eredita da un altro codice.

Anche i metaclass sono fuori mano nella mia esperienza.

E solo un meccanismo può davvero usarli alla volta in un intero codice, a meno che tutti quelli che li usano siano stati straordinariamente puntigliosi. Quindi, usandoli qui, potresti averne precluso un uso futuro migliore.

    
risposta data 27.08.2014 - 23:06
fonte

Leggi altre domande sui tag