Quando si lavora su essenzialmente un'implementazione tipo enumerato personalizzato, mi sono imbattuto in una situazione in cui sembra che ho dovuto ricavare separato ancora quasi sottoclassi identici sia da int
e long
dato che sono distinte classi in Python. Questo sembra un po 'ironico dal momento che le istanze di entrambi possono essere usate in modo intercambiabile perché per la maggior parte vengono semplicemente create automaticamente quando richiesto.Evitare di avere due sottoclassi numeriche diverse (int e long)?
Quello che ho funziona bene, ma nello spirito di DRY (Do not Repeat Yourself), non posso fare a meno di chiedermi se non c'è modo migliore o almeno più succinto di realizzare questo . L'obiettivo è di avere istanze di sottoclassi che possono essere utilizzate ovunque - o il più vicino possibile - che potrebbero essere state le istanze delle loro classi base. Idealmente, ciò dovrebbe avvenire automaticamente in modo simile al modo in cui il int()
integrato restituisce effettivamente uno long
ogni volta che ne viene rilevato uno.
Ecco il mio attuale implementazione:
class NamedInt(int):
"""Subclass of type int with a name attribute"""
__slots__ = "_name" # also prevents additional attributes from being added
def __setattr__(self, name, value):
if hasattr(self, name):
raise AttributeError(
"'NamedInt' object attribute %r is read-only" % name)
else:
raise AttributeError(
"Cannot add attribute %r to 'NamedInt' object" % name)
def __new__(cls, name, value):
self = super(NamedInt, NamedInt).__new__(cls, value)
# avoid call to this subclass's __setattr__
super(NamedInt, self).__setattr__('_name', name)
return self
def __str__(self): # override string conversion to be name
return self._name
__repr__ = __str__
class NamedLong(long):
"""Subclass of type long with a name attribute"""
# note: subtypes of variable length 'long' type can't have __slots__
def __setattr__(self, name, value):
if hasattr(self, name):
raise AttributeError(
"NamedLong object attribute %r is read-only" % name)
else:
raise AttributeError(
"Cannot add attribute %r to 'NamedLong' object" % name)
def __new__(cls, name, value):
self = super(NamedLong, NamedLong).__new__(cls, value)
# avoid call to subclass's __setattr__
super(NamedLong, self).__setattr__('_name', name)
return self
def __str__(self):
return self._name # override string conversion to be name
__repr__ = __str__
class NamedWholeNumber(object):
"""Factory class which creates either a NamedInt or NamedLong
instance depending on magnitude of its numeric value.
Basically does the same thing as the built-in int() function
does but also assigns a '_name' attribute to the numeric value"""
class __metaclass__(type):
"""NamedWholeNumber metaclass to allocate and initialize the
appropriate immutable numeric type."""
def __call__(cls, name, value, base=None):
"""Construct appropriate Named* subclass."""
# note the int() call may return a long (it will also convert
# values given in a string along with optional base argument)
number = int(value) if base is None else int(value, base)
# determine the type of named numeric subclass to use
if -sys.maxint-1 <= number <= sys.maxint:
named_number_class = NamedInt
else:
named_number_class = NamedLong
# return instance of proper named number class
return named_number_class(name, number)
Buona risposta, ma non gestisce 'NamedInt ('HexBased', 'deadbeef', 16)'. – martineau
Hmm, buon punto. Penso che possa essere risolto con varargs. Io modificherò per farlo. – Blckknght
Grazie per la correzione. È stato difficile decidere tra questa e la risposta di @ eryksun, poiché entrambi affrontano la questione DRY estremamente bene - ma alla fine l'ho scelto perché è l'IMHO più semplice e comprensibile. A proposito, è possibile aggiungere un attributo '__slots__' alla sola sottoclasse' NamedInt' (come fatto da eryksun) che sembra rispondere a quella necessità del probabile caso 'int' più comune (e _è_ una caratteristica importante per l'uso inteso). – martineau