5

基本的にカスタム列挙型の実装に取り​​組んでいるときに、両方から別々であるがほぼ同一のサブクラスを派生させる必要があるように見える状況に遭遇しました。これらはPythonでは別個のクラスであるためです。ほとんどの場合、必要なときに自動的に作成されるため、2つのインスタンスは通常交換可能に使用できるため、これは皮肉なことのように思われます。intlong

私が持っているものはうまく機能しますが、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)
4

3 に答える 3

2

クラスデコレータのバージョンは次のとおりです。

def named_number(Named):

    @staticmethod
    def __new__(cls, name, value, base=None):
        value = int(value) if base is None else int(value, base)
        if isinstance(value, int):
            NamedNumber = Named  # NamedInt / NamedLong
        else:
            NamedNumber = cls = NamedLong
        self = super(NamedNumber, cls).__new__(cls, value)
        super(NamedNumber, self).__setattr__('_name', name)
        return self

    def __setattr__(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "'%r' object attribute %r is read-only" % (Named, name))
        else:
            raise AttributeError(
                "Cannot add attribute %r to '%r' object" % (name, Named))

    def __repr__(self):
        return self._name

    __str__ = __repr__

    for k, v in locals().items():
        if k != 'Named':
            setattr(Named, k, v)

    return Named

@named_number
class NamedInt(int):
    __slots__ = '_name'

@named_number
class NamedLong(long): pass
于 2012-10-19T19:28:05.337 に答える
2

アロケーターをオーバーライドすると、適切な型のオブジェクトを返すことができます。

class NamedInt(int):
  def __new__(...):
    if should_be_NamedLong(...):
      return NamedLong(...)
     ...
于 2012-10-19T16:27:52.750 に答える
2

多重継承によって DRY 問題を解決する方法を次に示します。残念ながら、それは__slots__(コンパイル時TypeErrorの s を引き起こす) とうまく動作しないので、私はそれを省かなければなりませんでした。うまくいけば、__dict__値がユース ケースのメモリを浪費しすぎないことを願っています。

class Named(object):
    """Named object mix-in. Not useable directly."""
    def __setattr__(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "%r object attribute %r is read-only" %
                (self.__class__.__name__, name))
        else:
            raise AttributeError(
                "Cannot add attribute %r to %r object" %
                (name, self.__class__.__name__))

    def __new__(cls, name, *args):
        self = super(Named, cls).__new__(cls, *args)
        super(Named, self).__setattr__('_name', name)
        return self

    def __str__(self):  # override string conversion to be name
        return self._name

    __repr__ = __str__

class NamedInt(Named, int):
    """NamedInt class. Constructor will return a NamedLong if value is big."""
    def __new__(cls, name, *args):
        value = int(*args) # will raise an exception on invalid arguments
        if isinstance(value, int):
            return super(NamedInt, cls).__new__(cls, name, value)
        elif isinstance(value, long):
            return NamedLong(name, value)

class NamedLong(Named, long):
    """Nothing to see here."""
    pass
于 2012-10-19T17:41:29.387 に答える