import inspect
reserved_props = {
'__module__',
'__qualname__',
'__annotations__',
}
class StructMeta(type):
def __new__(cls, name, bases, props):
slots = []
defaults = {}
for key, val in props.items():
if key not in reserved_props and not inspect.isfunction(val):
defaults[key] = val
slots.append(key)
for slot_name in slots:
del props[slot_name]
props['__defaults__'] = defaults
props['__slots__'] = tuple(slots)
return type.__new__(cls, name, bases, props)
class Struct(metaclass=StructMeta):
def __init__(self, *args, **kwargs):
non_empty_slots = set()
for value, key in zip(args, self.__slots__):
setattr(self, key, value)
non_empty_slots.add(key)
for key, value in kwargs.items():
setattr(self, key, value)
non_empty_slots.add(key)
for slot in set(self.__slots__) - non_empty_slots:
setattr(self, slot, self.__defaults__[slot])
def __str__(self):
values = ['{}:{}'.format(k, getattr(self, k)) for k in self.__slots__]
return '<{} {}>'.format(self.__class__.__name__, ', '.join(values))
class User(Struct):
name: str = 'John Smith'
email: str = None
def say_hello(self):
print(f'{self.name} says hello')
if __name__ == '__main__':
user = User()
print(user)
user = User('Jackie', 'jackie@example.com')
print(user)
user = User(email='johnsmith@example.com')
print(user)