Я пытаюсь сделать систему пользовательских типов в Python.Ниже приведен код.
from inspect import Signature, Parameter
class Descriptor():
def __init__(self, name=None):
self.name = name
def __set__(self, instance, value):
instance.__dict__[self.name] = value
def __get__(self, instance, cls):
return instance.__dict__[self.name]
class Typed(Descriptor):
ty = object
def __set__(self, instance, value):
if not isinstance(value, self.ty):
raise TypeError('Expected %s' %self.ty)
super().__set__(instance, value)
class Integer(Typed):
ty = int
class Float(Typed):
ty = float
class String(Typed):
ty = str
class Positive(Descriptor):
def __set__(self, instance, value):
if value < 0:
raise ValueError('Expected >= 0')
super().__set__(instance, value)
class PosInteger(Integer, Positive):
pass
class Sized(Descriptor):
def __init__(self, *args, maxlen, **kwargs):
self.maxlen = maxlen
super().__init__(*args, **kwargs)
def __set__(self, instance, value):
if len(value) > self.maxlen:
raise ValueError('TooBig')
super().__set__(instance, value)
class SizedString(String, Sized):
pass
def make_signature(names):
return Signature([Parameter(name, Parameter.POSITIONAL_OR_KEYWORD) for name in names])
class StructMeta(type):
def __new__(cls, name, bases, clsdict):
fields = [key for key, value in clsdict.items() if isinstance(value, Descriptor)]
for name in fields:
#print(type(clsdict[name]))
clsdict[name].name = name
clsobj = super().__new__(cls, name, bases, clsdict)
sig = make_signature(fields)
setattr(clsobj, '__signature__', sig)
return clsobj
class Structure(metaclass = StructMeta):
def __init__(self, *args, **kwargs):
bound = self.__signature__.bind(*args, **kwargs)
for name, value in bound.arguments.items():
setattr(self, name, value)
Используя описанную выше систему типов, я избавился от всего стандартного кода и дублирующего кода, который мне пришлось бы писать в классах (в основном внутри init) для проверки типов, проверки значенийи т.д.
Используя приведенный выше код, мои классы выглядели бы так просто, как это
class Stock(Structure):
name = SizedString(maxlen=9)
shares = PosInteger()
price = Float()
stock = Stock('AMZN', 100, 1600.0)
До этого все работает нормально.Теперь я хочу расширить функциональность проверок этого типа и создать классы, содержащие объекты других классов.Например, цена теперь больше не является Float, а имеет тип Price (то есть цена другого класса).
class Price(Structure):
currency = SizedString(maxlen=3)
value = Float()
class Stock(Structure):
name = SizedString(maxlen=9)
shares = PosInteger()
price = Price() # This won't work.
Это не будет работать, потому что строка "price = Price ()" будет вызывать конструкторPrice и будет ожидать, что валюта и значение будут переданы в конструктор, потому что Price является структурой, а не дескриптором.Выдает «TypeError: отсутствует обязательный аргумент:« currency »».
Но я хочу, чтобы это работало и выглядело как выше, потому что в конце цена также является типом, подобным PosInteger, но в то же время она также должна быть структурой.т.е. цена должна наследоваться от структуры, но в то же время она также должна быть дескриптором.
Я могу заставить его работать, определив другой класс, скажем "PriceType"
class Price(Structure):
currency = SizedString(maxlen=3)
value = Float()
class PriceType(Typed):
ty = Price
class Stock(Structure):
name = SizedString(maxlen=9)
shares = PosInteger()
price = PriceType()
stock = Stock('AMZN', 100, Price('INR', 2400.0))
Но это выглядит немного странно - Price и PriceType как два разностных класса.Может ли кто-нибудь помочь мне понять, могу ли я избежать создания класса PriceType?
Я также теряю функциональность для предоставления значений по умолчанию для полей.
Например, как я могу сохранить значение по умолчанию для поля акции в Акции в 0 или значение по умолчанию для поля валюты в Прайсе в 'USD'?то есть что-то вроде ниже.
class Stock:
def __init__(name, price, shares=0)
class Price
def __init__(value, currency = 'USD')