# Base class. Uses a descriptor to set a value

class Descriptor:

    def __init__(self, name=None, **opts):

        self.name = name

        for key, value in opts.items():

            setattr(self, key, value)


    def __set__(self, instance, value):

        instance.__dict__[self.name] = value


# Decorator for applying type checking

def Typed(expected_type, cls=None):

    if cls is None:

        return lambda cls: Typed(expected_type, cls)


    super_set = cls.__set__

    def __set__(self, instance, value):

        if not isinstance(value, expected_type):

            raise TypeError('expected ' + str(expected_type))

        super_set(self, instance, value)

    cls.__set__ = __set__

    return cls


# Decorator for unsigned values

def Unsigned(cls):

    super_set = cls.__set__

    def __set__(self, instance, value):

        if value < 0:

            raise ValueError('Expected >= 0')

        super_set(self, instance, value)

    cls.__set__ = __set__

    return cls


# Decorator for allowing sized values

def MaxSized(cls):

    super_init = cls.__init__

    def __init__(self, name=None, **opts):

        if 'size' not in opts:

            raise TypeError('missing size option')

        super_init(self, name, **opts)

    cls.__init__ = __init__


    super_set = cls.__set__

    def __set__(self, instance, value):

        if len(value) >= self.size:

            raise ValueError('size must be < ' + str(self.size))

        super_set(self, instance, value)

    cls.__set__ = __set__

    return cls


# Specialized descriptors

@Typed(float)

@Unsigned

class UnsignedFloat(Descriptor)

    pass


@Typed(str)

@MaxSized

class SizedString(Descriptor):

    pass



기본적인 디스크립터를 만드는 작업 자체야 다른 언어랑 크게 다를게 없는데

특화된 디스크립터를 클래스 데코레이터를 조합해가며 만드는 거 보고 아 역시 생산성 죽인다 싶었음 ㅇㅇ