基本的にカスタム列挙型の実装に取り組んでいるときに、両方から別々であるがほぼ同一のサブクラスを派生させる必要があるように見える状況に遭遇しました。これらはPythonでは別個のクラスであるためです。ほとんどの場合、必要なときに自動的に作成されるため、2つのインスタンスは通常交換可能に使用できるため、これは皮肉なことのように思われます。int
long
私が持っているものはうまく機能しますが、DRY(Do n't Repeat Yourself)の精神では、これを達成するためのより良い、または少なくともより簡潔な方法がないかどうか疑問に思わずにはいられません。目標は、基本クラスのインスタンスがあった可能性のあるすべての場所で、または可能な限りそれに近い場所で使用できるサブクラスインスタンスを作成することです。理想的には、これは、ビルトインが必要と検出したときにint()
実際にを返すのと同じように自動的に発生するはずです。long
これが私の現在の実装です:
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)