Reputation: 8697
I've a few classes like the following one and would like to avoid too much repetition and unnecessarily bloated code. I'm looking for a specific optimization, not a code review.
Current code:
class Item:
def __init__(self, name, price):
self.name = name
self.price = price
@classmethod
def from_dictionary(cls, dictionary):
return cls(
name=dictionary['name'] if 'name' in dictionary else '',
price=dictionary['price'] if 'price' in dictionary else ''
)
Desired (pseudo) code:
class Item:
def __init__(self, *args, **kwargs):
for kwarg in kwargs:
self[kwarg] = kwargs[kwarg]
@classmethod
def from_dictionary(cls, dictionary):
return cls(
for key, value in dictionary:
key=value or ''
)
How can I achieve something close to my desired code in Python?
Upvotes: 1
Views: 233
Reputation: 27273
You can use the builtin setattr
to set attributes:
class Item:
def __init__(self, *args, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
@classmethod
def from_dictionary(cls, dictionary):
return cls(**dictionary)
In the classmethod, we use the **
operator to unpack the given dictionary.
Upvotes: 2
Reputation: 123443
Since classes are first-class objects in Python, you could define a function that created a class with the desired attributes. For example:
def make_class(classname, *attrnames):
class Class:
def __init__(self, *args):
for name, value in zip(attrnames, args):
setattr(self, name, value)
@classmethod
def from_dictionary(cls, dictionary):
inst = cls() # Empty instance.
for name in attrnames:
setattr(inst, name, dictionary.get(name, ''))
return inst
Class.__name__ = classname
return Class
if __name__ == '__main__':
Class1 = make_class('Class1', 'name', 'price')
obj1 = Class1('item1', 42)
print(f'obj1 is instance of {type(obj1).__name__}')
print(vars(obj1))
print()
Class2 = make_class('Class2', 'foo', 'bar')
obj2 = Class2('whatcha')
print(f'obj2 is instance of {type(obj2).__name__}')
print(vars(obj2))
print()
obj3 = Class1.from_dictionary({'name': 'item2', 'price': 3.14})
print(f'obj3 is instance of {type(obj3).__name__}')
print(vars(obj3))
Output:
obj1 is instance of Class1
{'name': 'item1', 'price': 42}
obj2 is instance of Class2
{'foo': 'whatcha'}
obj3 is instance of Class1
{'name': 'item2', 'price': 3.14}
Upvotes: 1
Reputation: 11
Use the Python library marshmallow. Then it's as simple as adding @addschema as a class decorator. This will allow for easy deserialization. The code would look like:
item = Item. Schema().load(item_dict)
https://marshmallow.readthedocs.io/en/stable/
Upvotes: 0