fork download
  1. import functools
  2.  
  3.  
  4. def isdunder(x):
  5. return isinstance(x, str) and x.startswith('__') and x.endswith('__')
  6.  
  7.  
  8. class DunderSet:
  9. def __contains__(self, x):
  10. return isdunder(x)
  11.  
  12.  
  13. def wrap_method(method, xtype, cast):
  14.  
  15. @functools.wraps(method)
  16. def retval(*args, **kwargs):
  17. result = method(*args, **kwargs)
  18. return cast(result) if type(result) == xtype else result
  19.  
  20. return retval
  21.  
  22.  
  23. def wrap_getter(method, xtype, cast, exceptions):
  24. @functools.wraps(method)
  25. def retval(self, name, *args, **kwargs):
  26. result = method(self, name, *args, **kwargs)
  27. return result if name in exceptions else check_type(result, xtype, cast)
  28.  
  29. return retval
  30.  
  31.  
  32. def check_type(value, xtype, cast):
  33. if type(value) == xtype:
  34. return cast(value)
  35. if callable(value):
  36. return wrap_method(value, xtype, cast)
  37. return value
  38.  
  39.  
  40. class ClosedMeta(type):
  41. def __new__(meta, name, bases, dct, **kwargs):
  42. if 'exceptions' in kwargs:
  43. exceptions = set(['__new__', '__init__', *map(str, kwargs.pop('exceptions'))])
  44. else:
  45. exceptions = DunderSet()
  46. target = kwargs.pop('target', bases[0] if bases else object)
  47.  
  48. cls = super().__new__(meta, name, bases, dct, **kwargs)
  49.  
  50. for base in cls.__mro__:
  51. for name, item in base.__dict__.items():
  52. if isdunder(name) and (base is cls or name not in dct) and callable(item):
  53. if name in ('__getattribute__', '__getattr__'):
  54. setattr(cls, name, wrap_getter(item, target, cls, exceptions))
  55. elif name not in exceptions:
  56. setattr(cls, name, wrap_method(item, target, cls))
  57. return cls
  58.  
  59. def __init__(cls, *args, **kwargs):
  60. return super().__init__(*args)
  61.  
  62.  
  63. class MyInt(int):
  64. def __contains__(self, x):
  65. return x == self
  66. def my_op(self, other):
  67. return int(self * self // other)
  68.  
  69.  
  70. class ClosedInt(MyInt, metaclass=ClosedMeta, target=int,
  71. exceptions=['__index__', '__int__', '__trunc__']):
  72. pass
  73.  
  74. class MyClass(ClosedInt, metaclass=type):
  75. def __add__(self, other):
  76. return 1
  77.  
  78. print(type(MyInt(1) + MyInt(2)))
  79. print(0 in MyInt(0), 1 in MyInt(0))
  80. print(type(MyInt(4).my_op(16)))
  81.  
  82. print(type(ClosedInt(1) + ClosedInt(2)))
  83. print(0 in ClosedInt(0), 1 in ClosedInt(0))
  84. print(type(ClosedInt(4).my_op(16)))
  85.  
  86. print(type(MyClass(1) + ClosedInt(2)))
Success #stdin #stdout 0.02s 27624KB
stdin
Standard input is empty
stdout
<class 'int'>
True False
<class 'int'>
<class '__main__.ClosedInt'>
True False
<class '__main__.ClosedInt'>
<class 'int'>