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.