food_data is a variable containing JSON data. Using the data, I want to create a list of Food objects, like so
foods = []
for data_row in food_data:
foods.append(Food(data_row))
This is what my Food class looks like as of right now:
class Food(dict):
""" by inheriting from dict, Food objects become automatically serializable for JSON formatting """
def __init__(self, data):
""" create a serialized food object with desired fields """
id = data["id"]
name = data["title"]
image = data["image"]
super().__init__(self, id=id, name=name, image=image)
And here is some example data:
[
{
"id": 738290,
"title": "Pasta with Garlic, Scallions, Cauliflower & Breadcrumbs",
"image": "https://spoonacular.com/recipeImages/716429-312x231.jpg",
},
{
"id": 343245,
"title": "What to make for dinner tonight?? Bruschetta Style Pork & Pasta",
"image": "https://spoonacular.com/recipeImages/715538-312x231.jpg",
}
]
Is there a method I can write for the Food class that will take the data and return a list of different versions of itself?
I would start by not subclassing dict: there is a better way to make an instance of Food serializable.
Next, make Food.__init__ dumb: three arguments, used to set three attributes.
Then, define a class method that is responsible for parsing an arbitrary dict with at least id, title, and image keys to get the values expected by Food.__init__.
Finally, define a method that turns an instance of Food back into a dict (though not necessarily the same dict that from_dict uses; generate one that serializes the way you want).
class Food:
def __init__(self, id, name, image):
self.id = id
self.name = name
self.image = image
#classmethod
def from_dict(cls, d):
return cls(id=d['id'], name=d['title'], image=d['image'])
def to_dict(self):
return dict(id=self.id, name=self.name, image=self.image)
foods = [Food.from_dict(d) for d in food_data]
To make your instance serializable, define a customer encoder that uses your to_dict method,
class FoodEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Food):
return obj.to_dict()
return super().default(obj)
This piggy backs on the default encoder; if the immediate object is a Food, default returns a serializable dict. Otherwise, it defers to its parent to try to serialize it.
Then use that class in the call to json.dumps.
print(json.dumps(foods, cls=FoodEncoder))
I think in my case I may have been over-engineering my code. But I received many responses that did help me out in other aspects so I'm going to offer them here:
#Juanpa
Use a list comprehension
foods = [Food[data] for data in food_data]
#Chepner - unrelated but useful
subclass json.JSONEncoder instead of dict for serializability
#Matthias
Create a staticmethod within the class to return a list of objects
#staticmethod
def create_foods(food_data):
foods = []
for data_row in food_data:
foods.append(Food(data_row))
Related
The class I have been using looks simple, like this:
class Transaction(dict):
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
and then sending in:
transaction = Transaction({"to": "0x000", "from": "0x001": "timestamp": 1234})
and of course can be used like this transaction.to, however it looks like transaction.from does not work because from is a python reserved keyword
So I am curious using that simple class, is there a way to reassign from in the class to be something like
self.sender = dict.from
I have been trying with __init__ but with no luck
I also have written the class just with an __init__ and then assigning all values using self but with out a getter the class is not iterable
What I have been doing looks like this
# given data - {"to": "0x000", "from": "0x001": "timestamp": 1234}
item["sender"] = item["from"]
transaction = Transaction(item)
and then I have reference to it like transaction.sender.
If I understand correctly, your end goal is to have a class that can be instantiated from a dict and expose the keys as attributes. I'm inferring that the dict can only contain certain keys since you're talking about mapping "from" to sender. In that case, I would do this completely differently: don't subclass dict, instead have an alternate constructor that can handle the dict. I'd keep a "normal" constructor mostly for the sake of the repr.
For example:
class Transaction:
def __init__(self, to, from_, timestamp):
self.to = to
self.from_ = from_
self.timestamp = timestamp
#classmethod
def from_dict(cls, d):
return cls(d['to'], d['from'], d['timestamp'])
def __repr__(self):
"""Show construction."""
r = '{}({!r}, {!r}, {!r})'.format(
type(self).__name__,
self.to,
self.from_,
self.timestamp)
return r
transaction = Transaction.from_dict({"to": "0x000", "from": "0x001", "timestamp": 1234})
print(transaction) # -> Transaction('0x000', '0x001', 1234)
print(transaction.from_) # -> 0x001
Here I'm using the trailing underscore convention covered in PEP 8:
single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.
tkinter.Toplevel(master, class_='ClassName')
By the way, if it's useful, the keyword module contains the names of all Python keywords.
Suppose I have a python class like:
class User:
name = None
id = None
dob = None
def __init__(self, id):
self.id = id
Now I am doing something like this:
userObj = User(id=12) # suppose I don't have values for name and dob yet
## some code here and this code gives me name and dob data in dictionary, suppose a function call
user = get_user_data() # this returns the dictionary like {'name': 'John', 'dob': '1992-07-12'}
Now, the way to assign data to user object is userObj.name = user['name'] and userObj.dob = user['dob']. Suppose, User has 100 attributes. I will have to explicitly assign these attributes. Is there an efficient way in Python which I can use to assign the values from a dictionary to the corresponding attributes in the object? Like, name key in the dictionary is assigned to the name attribute in the object.
1. Modify the Class definition
class User():
def __init__(self, id):
self.data = {"id":id}
userObj = User(id=12)
2. Update the dict()
user = {"name":"Frank", "dob":"Whatever"} # Get the remaining data from elsewhere
userObj.data.update(user) # Update the dict in your userObj
print(userObj.data)
Here you go !
Instead of mapping a dict to the variable keys. You can use setattr to set variables in an object.
class User:
name = None
id = None
dob = None
def __init__(self, id):
self.id = id
def map_dict(self, user_info):
for k, v in user_info.items():
setattr(self, k, v)
Then for boiler code to use it.
userObj = User(id=12)
user_dict = {
'name': 'Bob',
'dob': '11-20-1993',
'something': 'blah'
}
userObj.map_dict(user_dict)
First, there is no need to predeclare properties in python.
class Foo:
bar: int # This actually creates a class member, not an instance member
...
If you want to add values to a class instance just use setattr()
d = {
'prop1': 'value1',
'prop2': 'value2',
'prop2': 'value2'
}
x = Foo()
for prop in d.keys():
setattr(x, prop, d[prop])
class User(dict):
def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
self.__dict__ = self
and then just get your dictionary and do:
userObj = User(dictionary)
EDIT:
user the function setattr() then
[setattr(userObj, key, item) for key,item in dict.items()]
In Case you REALLY need to
This solution is for the case, other solutions dont work for you and you cannot change your class.
Issue
In case you cannot modify your class in any way and you have a dictionary, that contains the information you want to put in your object, you can first get the custom members of your class by using the inspect module:
import inspect
import numpy as np
members = inspect.getmembers(User)
Extract your custom attributes from all members by:
allowed = ["__" not in a[0] for a in members]
and use numpy list comprehention for the extraction itself:
members = np.array(members)["__" not in a[0] for a in members]
Modify the user
So lets say you have the following user and dict and you want to change the users attributes to the values in the dictionary (behaviour for creating a new user is the same)
user = User(1)
dic = {"name":"test", "id": 2, "dob" : "any"}
then you simply use setattr():
for m in members:
setattr(user, m[0], dic[m[0]])
For sure there are better solutins, but this might come in handy in case other things dont work for you
Update
This solution uses the attribute definitions based on your class you use. So in case the dictionary has missing values, this solution might be helpful. Else Rashids solution will work well for you too
I am getting Data via a REST-Interface and I want to store those data in a class-object.
my class could looks like this:
class Foo:
firstname = ''
lastname = ''
street = ''
number = ''
and the json may look like this:
[
{
"fname": "Carl",
"lname": "any name",
"address": ['carls street', 12]
}
]
What's the easiest way to map between the json and my class?
My problem is: I want to have a class with a different structure than the json.
I want the names of the attributes to be more meaningful.
Of course I know that I could simply write a to_json method and a from_json method which does what I want.
The thing is: I have a lot of those classes and I am looking for more declarative way to write the code.
e.g. in Java I probably would use mapstruct.
Thanks for your help!
Use a dict for the json input. Use **kwargs in an __init__ method in your class and map the variables accordingly.
I had a similar problem, and I solved it by using #classmethod
import json
class Robot():
def __init__(self, x, y):
self.type = "new-robot"
self.x = x
self.y = y
#classmethod
def create_robot(cls, sdict):
if sdict["type"] == "new-robot":
position = sdict["position"]
return cls(position['x'], position['y'])
else:
raise Exception ("Unable to create a new robot!!!")
if __name__=='__main__':
input_string = '{"type": "new-robot", "position": {"x": 3, "y": 3}}'
cmd = json.loads(input_string)
bot = Robot.create_robot(cmd)
print(bot.type)
Perhaps you could you two classes, one directly aligned with the Json (your source class) and the other having the actual structure you need. Then you could map them using the ObjectMapper class[https://pypi.org/project/object-mapper/]. This is very close to the MapStruct Library for Java.
ObjectMapper is a class for automatic object mapping. It helps you to create objects between project layers (data layer, service layer, view) in a simple, transparent way.
I want to know how many objects I have created in a Product class and print all names stored in the class. And is there any way to store all objects in JSON format which has been defined in Product Class?
class Product:
pass
a=Product()
a.name="Pune"
a.apple=2
b=Product()
b.name="Delhi"
b.apple=4
without assigning count in class if I want to know how much I have stored data in that class. what should I do? Is there any better way to access the "apple"
an instance variable of every instance object.
Is there any way to convert class object in JSON format like:
[{"name": "Pune", "apple":2},
{"name": "Delhi", "apple":4}]
You can use __init__ and class variable to do that.
Whenever you create an object of Product class your can add those instance variable to class list
class Product:
names = []
apples = []
def __init__(self, name, apple):
# self.name = name
# self.apple = apple
Product.names.append(name)
Product.apples.append(apple)
a=Product("Pune", 2)
b=Product("Delhi", 4)
print(Product.names)
Output:
['Pune', 'Delhi']
I'm working on a trivial problem here to deserialize some JSON (I cannot change the format, it's not a service I created) into Python objects. I've managed to do the conversion using lambda's, but I'd like to use an object_hook now, to see if the it's possible to do a conversion using the json.loads method. However, that's where I'm failing right now, and I was wondering if someone could point me in the right direction.
This is the code I currently have:
import json
class Light:
def __init__(self, id, name):
self.id = id
self.name = name
response = '{"1": {"name": "bedroom"}, "2": {"name": "kitchen"}}'
def object_decoder(obj):
return Light(......)
print json.loads(response, object_hook=object_decoder)
As you can see, the response is one document with two keys, named 1 and 2. It would be nice if I can make the the code work in a way that the json.loads would return two Light objects, but at the moment, I'm stuck, and I don't know how to iterate over response to make this work.
object_hook won't help you, since you have id and name on the different levels in the json string:
object_hook, if specified, will be called with the result of every
JSON object decoded and its return value will be used in place of the
given dict.
Let's see why object_hook won't help. If you print objects that are coming to the object_decoder function, you'll see that it is going up from the deep, like this:
{u'name': u'bedroom'}
{u'name': u'kitchen'}
{u'1': None, u'2': None}
None
This means that you cannot join object_decoder calls in order to produce a Light instance.
How about using custom JSONDecoder class instead:
import json
class Light:
def __init__(self, id, name):
self.id = id
self.name = name
response = '{"1": {"name": "bedroom"}, "2": {"name": "kitchen"}}'
class Decoder(json.JSONDecoder):
def decode(self, s):
obj = super(Decoder, self).decode(s)
return [Light(id=k, name=v['name']) for k, v in obj.iteritems()]
lights = json.loads(response, cls=Decoder)
print lights # prints [<__main__.Light instance at 0x9c3b50c>, <__main__.Light instance at 0x9c3b56c>]
print [light.__dict__ for light in lights] # prints [{'id': u'1', 'name': u'bedroom'}, {'id': u'2', 'name': u'kitchen'}]
This is actually the same as making json.loads() and then instantiate classes after.
If you are able to change the format of the string, I suggest you use jsonpickle. I've founded it perfect for this sort of thing.