Best way to do many try-except blocks within Python class assignments? - python

I have a class which is pulling JSON data with keys, but the problem is that per instance of this class, the JSON data may not have keys for everything I am trying to grab. Currently, my class is set up like this:
class Show():
def __init__(self, data):
self.data = data
self.status = self.data['status']
self.rating = self.data['rating']
self.genres = self.data['genres']
self.weight = self.data['weight']
self.updated = self.data['updated']
self.name = self.data['name']
self.language = self.data['language']
self.schedule = self.data['schedule']
self.url = self.data['url']
self.image = self.data['image']
And so on, there are more parameters than that. I'm trying to avoid the messiness of having a try-except block for EACH AND EVERY one of those (27) lines. Is there a better way? Ultimately, I want a parameter to be assigned None if the JSON key doesn't exist.

If you're going to set a default value to the attribute if it's not in the data dictionary, use data.get('key') rather than data['key']. The get method will return None if the key does not exist, rather than raising a KeyError exception. If you want a different default value than None, you can pass a second argument to get and that is what will be returned.
So, your code could become:
class Show():
def __init__(self, data):
self.data = data
self.status = self.data.get('status')
self.rating = self.data.get('rating')
self.genres = self.data.get('genres')
self.weight = self.data.get('weight')
self.updated = self.data.get('updated')
self.name = self.data.get('name')
self.language = self.data.get('language')
self.schedule = self.data.get('schedule')
self.url = self.data.get('url')
self.image = self.data.get('image')

Use dict.get, which provides a default value instead of raising an exception for missing keys.
For example, you can change this:
self.status = self.data['status']
into this:
self.status = self.data.get('status')

You could change your code to something like:
class Show():
def __init__(self, data):
self.data = data
self.__dict__.update(data)
data = {'status': True, 'ratings': [1,2,3], 'foo': "blahblah"}
aShow = Show(data)
"""
>>> aShow.status
True
>>> aShow.ratings
[1,2,3]
>>> aShow.something_not_in_dict
AttributeError: Show instance has no attribute 'something_not_in_dict'
"""
Which does exactly the same, and trying to access something from your Show instance that isn't a key in your data dictionary would raise an AttributeError

Related

How to convert dict to class attributes in Python

Instead of using a dict to store and pass data we are going completely OOPS approach of storing the data as class attributes and call the get methods defined according to need.
In Java i was able to achieve this but having some trouble in Python. Any Solution would be helpful.
import json
class InputModel:
def __init__(self, input_payload):
self.id1 = input_payload["id1"]
self.route = RouteModel(input_payload["route"])
self.id2 = input_payload["id2"]
self.id3 = input_payload["id3"]
self.id4 = input_payload["id4"]
self.id5 = input_payload["id5"]
def get_id1(self):
return self.id1
#similar for other ids
class RouteModel:
def __init__(self, input_payload_route):
self.id6 = input_payload_route["id6"]
self.id7 = input_payload_route["id7"]
def get_id6(self):
return self.id6
#similar for other ids
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
im = InputModel(json_dict)
print(im.get_id1())
print(im.get_id6())
not able to access the nested class attributes
Seems like you went for 1 extra indent in your class methods, thus you couldn't reach them.
Also, to reach id6 of RouteModel, you had to refer to 'route' first:
import json
class InputModel:
def __init__(self, input_payload):
self.id1 = input_payload["id1"]
self.route = RouteModel(input_payload["route"])
self.id2 = input_payload["id2"]
self.id3 = input_payload["id3"]
self.id4 = input_payload["id4"]
self.id5 = input_payload["id5"]
def get_id1(self):
return self.id1
#similar for other ids
class RouteModel:
def __init__(self, input_payload_route):
self.id6 = input_payload_route["id6"]
self.id7 = input_payload_route["id7"]
def get_id6(self):
return self.id6
#similar for other ids
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
im = InputModel(json_dict)
print(im.get_id1())
print(im.route.get_id6())
Output:
string
string
The problem is that you are only defining get_id* in your local scope, you need to assign it to the instance if you insist on defining it inside the __init__ method.
I minimized your code example to isolate your issue.
class RouteModel:
def __init__(self):
self.id6 = "foo"
def get_id6(self_=self):
return self_.id6
self.get_id6 = get_id6
rm = RouteModel()
print(rm.get_id6())
>>> "foo"
If I understand your question correctly, you want to be able to access the ids directly as attributes, no matter how deep they are nested in the dictionary.
This solution creates the attributes recursively:
import json
class InputModel:
def __init__(self, payload):
self.create_attrs(payload)
def create_attrs(self, d):
for key, value in d.items():
# if the value is a dict, call create_attrs recursively
if isinstance(value, dict):
self.create_attrs(value)
else:
# create an attribute key=value, e.g. id1="string"
setattr(self, key, value)
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
im = InputModel(json_dict)
print(im.id1)
print(im.id6)
After going through answers provided, mostly have defined instance attributes and not class attributes.
Correct me if I'm wrong here but I think this is how class attributes are defined right?
import json
class InputModel:
def __init__(self, input_payload):
InputModel.id1 = input_payload["id1"]
InputModel.route = RouteModel(input_payload["route"])
InputModel.id2 = input_payload["id2"]
InputModel.id3 = input_payload["id3"]
InputModel.id4 = input_payload["id4"]
InputModel.id5 = input_payload["id5"]
def get_id1():
return InputModel.id1
#OR
##classmethod
#def get_id1(cls):
# return cls.id1
#similar for other ids
class RouteModel:
def __init__(self, input_payload_route):
RouteModel.id6 = input_payload_route["id6"]
RouteModel.id7 = input_payload_route["id7"]
def get_id6():
return RouteModel.id6
#similar for other ids
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
InputModel(json_dict)
print(InputModel.get_id1())
print(InputModel.route.get_id6())
print(RouteModel.get_id6())

python, calling a method from another class

I am trying to call the sum_method function from my evaluation class to my main one, however I run into many errors. I want to use the new_data as the data parameter of my sum_method function.
evaluation class:
class evaluation():
def __init__(self, data):
self.data = data
def sum_method(self):
montant_init = self.data.loc[self.data['Initiateur'] == 'Glovoapp', 'Montant (centimes)'].sum()
print(montant_init)
main class:
class main(evaluation):
new_data.to_csv("transactions.csv", index=False)
self.data = new_data
def call_sum(self, new_data):
init_eval = evaluation.sum_method(self=new_data)
print(init_eval)
init_evalobj = main()
init_evalobj.call_sum()
if you use the method in your inherence class just use self
so:
init_eval = self.sum_method()
the self argument is passed in python automaticly as first parameter
update
you also should return a value:
def sum_method(self):
montant_init = self.data.loc[self.data['Initiateur'] == 'Glovoapp', 'Montant (centimes)'].sum()
print(montant_init)
return montant_init
I'd suggest making some changes to the both classes, to encapsulate the .data member variable in the base class. My preference would also be to separate out the calculation from the display, so leave all the print statements in the call_sum() function.
class evaluation:
def __init__(self, data):
self.data = data
def sum_method(self):
montant_init = self.data.loc[self.data['Initiateur'] == 'Glovoapp', 'Montant (centimes)'].sum()
return montant_init
class main(evaluation):
def __init__(self):
# Reduce csv content to what's needed for analysis
data_csv = pd.read_csv('transactions.csv')
# --> removing unnecessary data
new_data = data_csv[['Opération', 'Initiateur', 'Montant (centimes)', 'Monnaie',
'Date', 'Résultat', 'Compte marchand', 'Adresse IP Acheteur', 'Marque de carte']]
# --> saving changes...
new_data.to_csv("transactions.csv", index=False)
super().__init__(new_data) //Initialize the base class
def call_sum(self):
print('Glovoapp "montant" generated')
init_eval = self.sum_method() //Call the method from the base class
print(init_eval)

Pulling an object out of a queue based on a data member

I need to extend Queue so that I can pluck an object out of the Queue based on the value of one of the object's data members.
I've solved the problem like this and I wonder if I'm being dense. Do I really need to do the list conversion to find the object?
class Datum:
def __init__(self, id):
self.id = id
def __str__(self):
return str(self.id)
class PluckQueue(Queue):
def pluck(self, id):
with self.not_empty:
plucked = None
while plucked is None:
pluck_list = list(self.queue)
try:
plucked = next(xx for xx in pluck_list if xx.id == id )
except StopIteration:
plucked = None
if plucked is None:
self.not_empty.wait()
else:
index = pluck_list.index(plucked)
self.queue.remove(pluck_list[index])
return plucked
def __str__(self):
return str([str(xx) for xx in self.queue])
pq = PluckQueue()
pq.put(Datum('a'))
pq.put(Datum('b'))
pq.put(Datum('c'))
plucked = pq.pluck('b')
print(plucked)
print(pq)
This gives the result:
b
['a', 'c']
Am I missing an easier way to do this?
This approach has worked fine and isn't so kludgy after all. The big difference one might make is to pass a predicate instead of having the test hardcoded into the next statement.

How to update an instance variable when another variable is changed?

I have the following class and I want the instance variable api_id_bytes to update.
class ExampleClass:
def __init__(self):
self.api_key = ""
self.api_id = ""
self.api_id_bytes = self.api_key.encode('utf-8')
I'd like to be able to have this outcome:
>>>conn = ExampleClass()
>>>conn.api_key = "123"
>>>conn.api_id = "abc"
>>>print(conn.api_id_bytes)
b'123'
>>>
I basically need the self.api_key.encode('utf-8') to run when an api_id is entered but it doesn't, it only does through the initial conn = ExampleClass().
I'm not sure what this is called so searching didn't find an answer.
Here's how you could do it by making api_id_bytes a property.
class ExampleClass:
def __init__(self):
self.api_key = ""
self.api_id = ""
#property
def api_id_bytes(self):
return self.api_key.encode('utf-8')
Now conn.api_id_bytes will always be correct for the current value of conn.api_key.

How to extract string values into object fields

I have this dictionary:
{"id":3,"name":"MySQL","description":"MySQL Database Server - Fedora 21 - medium","image":"","flavor":""}
And I have this object:
class Record():
id = None
name = None
description = None
image = None
flavor = None
How can I assign values from the dictionary to their corresponding class fields?
Take a dict object as the parameter of init function:
class Record(object):
def __init__(self,record_dict):
try:
self.id = record_dict['id']
self.name = record_dict['name']
self.description = record_dict['description']
self.image = record_dict['image']
self.flavor = record_dict['flavor']
except KeyError:
print 'KeyError'
def get_name(self):
return self.name
adict = {"id":3,"name":"MySQL","description":"MySQL Database Server - Fedora 21 - medium","image":"","flavor":""}
one_obj = Record(adict)
print one_obj
print one_obj.get_name()
output:
<__main__.Record object at 0x022E4C90>
MySQL
works for me...
You probably want something like this:
class Record:
def __init__(self, myDict):
self.id = myDict[“id”]
self.name = myDict[“name”]
self.description = myDict[“description”]
self.image = myDict[“image”]
self.flavor = myDict[“flavor”]
And call it:
rec = Record(myDict)
See here to understand the difference between class and instance variables.
Long story short, class variables have a single value for every instance of the class while instance variables values are unique to each instance.
A class variable is defined like this:
class myClass:
Classvar = ‘something’
An instance variable is defined like this:
class myClass:
def __init__():
Self.instanceVar = ‘something else’
This has already been answered here:
Convert Python dict to object?
My favorite method is this one: x.__dict__.update(d)
You can assign them as follows, assuming your dictionary name is input
id = input['id']
name = input['name']
description = input['description']
image = input['image']
flavor = input['flavor']
Try this method in which you grab the attributes of the object:
r = Record()
attributes = [i for i in dir(r) if not i.startswith('_')]
Basically, there are a bunch of background attributes that contain a bunch of underscores. The dir method gets all the attributes and we create a list of the ones that we want. At this point:
# attributes = ['count', 'description', 'flavor', 'id', 'image', 'index', 'name']
So now we use __setattr__ to set the attributes we just grabbed according to the my_dict
for i in attributes:
r.__setattr__(i, my_dict[i])
See the code run online here.
When you create the Record, pass in the dictionary. Then map the key to the value.
Another simple method, see the code here
r = Record()
for k, v in my_dict.items():
exec('r.' + k + '="' + str(v) + '"')

Categories

Resources