Wenn ich im Wesentlichen an einer Implementierung des benutzerdefinierten Enumerationstyps arbeitete, stieß ich auf eine Situation, in der ich separate fast identische Unterklassen von beiden int
und long
ableiten musste, da sie unterschiedliche Klassen in Python sind. Dies scheint etwas ironisch zu sein, da Instanzen der beiden in der Regel austauschbar verwendet werden können, da sie zum größten Teil nur automatisch erstellt werden, wenn dies erforderlich ist.Vermeiden Sie zwei verschiedene numerische Unterklassen (int und long)?
Was ich habe funktioniert gut, aber im Geiste von DRY (Do not Repeat Yourself), kann ich nicht helfen, aber frage mich, ob es keine bessere oder zumindest eine prägnantere Möglichkeit, dies zu erreichen . Das Ziel ist es, Unterklassen-Instanzen zu haben, die überall - oder so nah wie möglich - verwendet werden können, die Instanzen ihrer Basisklassen hätten sein können. Idealerweise sollte dies automatisch geschehen, ähnlich wie die eingebaute int()
tatsächlich eine long
zurückgibt, wann immer sie eine benötigt.
Hier ist meine aktuelle Implementierung:
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)
Gute Antwort, aber behandelt 'NamedInt ('HexBased', 'Deadbeef', 16)' nicht. – martineau
Hmm, guter Punkt. Ich denke, es kann mit Varargs behoben werden. Ich werde das bearbeiten. – Blckknght
Danke für die Lösung. War schwer zu entscheiden zwischen dieser und @ eryksuns Antwort, da beide das DRY-Thema sehr gut ansprechen - aber letztendlich dieses gewählt haben, weil es das einfachste und verständlichste IMHO ist. Übrigens ist es möglich, ein '__slots__'-Attribut nur der' NamedInt'-Unterklasse hinzuzufügen (wie es Eryks getan hat), die diese Notwendigkeit für den wahrscheinlich häufigeren 'int'-Fall zu adressieren scheint (und _ist_ ein wichtiges Merkmal für die beabsichtigte Verwendung). – martineau