Using Python "json" module to make a class serializable - python

I am working in Python with an Email() class that I would like to extend into a SerializeEmail() class, which simply adds two further methods, .write_email() and .read_email(). I would like this sort of behaviour:
# define email
my_email = SerializeEmail()
my_email.recipients = 'link#hyrule.com'
my_email.subject = 'RE: Master sword'
my_email.body = "Master using it and you can have this."
# write email to file system for hand inspection
my_email.write_email('my_email.txt')
...
# Another script reads in email
my_verified_email = SerializeEmail()
my_verified_email.read_email('my_email.txt')
my_verified_email.send()
I have navigated the json encode/decode process, and I can successfully write my SerializeEmail() object, and read it in, however, I can't find a satisfactory way to recreate my object via a SerializeEmail.read_email() call.
class SerializeEmail(Email):
def write_email(self,file_name):
with open(file_name,"w") as f:
json.dump(self,f,cls=SerializeEmailJSONEncoder,sort_keys=True,indent=4)
def read_email(self,file_name):
with open(file_name,"r") as f:
json.load(f,cls=SerializeEmailJSONDecoder)
The problem here is that the json.load() call in my read_email() method returns an instance of my SerializeEmail object, but doesn't assign that object to the current instance that I'm using to call it. So right now I'd have to do something like this,
another_email = my_verified_email.read_email('my_email.txt')
when what I want is for the call to my_veridied_email.read_email() to populate the current instance of my_verified_email with the data on the file. I've tried
self = json.load(f,cls=SerializeEmailJSONDecoder)
but that doesn't work. I could just assign each individual element of my returned object to my "self" object, but that seems ad-hoc and inelegant, and I'm looking for the "right way" to do this, if it exists. Any suggestions? If you think that my whole approach is flawed and recommend a different way of accomplishing this task, please sketch it out for me.

While you could jump through a number of hoops to load serialized content into an existing instance, I wouldn't recommend doing so. It's an unnecessary complication which really gains you nothing; it means that the extra step of creating a dummy instance is required every time you want to load an e-mail from JSON. I'd recommend using either a factory class or a factory method which loads the e-mail from the serialized JSON and returns it as a new instance. My personal preference would be a factory method, which you'd accomplish as follows:
class SerializeEmail(Email):
def write_email(self,file_name):
with open(file_name,"w") as f:
json.dump(self,f,cls=SerializeEmailJSONEncoder,sort_keys=True,indent=4)
#staticmethod
def read_email(file_name):
with open(file_name,"r") as f:
return json.load(f,cls=SerializeEmailJSONDecoder)
# You can now create a new instance by simply doing the following:
new_email = SerializeEmail.read_email('my_email.txt')
Note the #staticmethod decorator, which allows you to call the method on the class without any implicit first argument being passed in. Normally factory methods would be #classmethods, but since you're loading the object from JSON, the implicit class argument is unnecessary.
Notice how, with this modification, you don't need to instantiate a SerializeEmail object before you can load another one from JSON. You simply call the method directly on the class and get the desired behavior.

Related

How to dynamiclly call SOLID princles following classes

I have a module where I try to follow the SOLID principles to create and generate data and I think the following is based around the Liskov Substitution Principle:
class BaseLoader(ABC):
def __init__(self, dataset_name='mnist'):
self.dataset_name=dataset_name
class MNISTLoader(BaseLoader):
def load(self):
# Logic for loading the data
pass
class OCTMNISTLoader(Baseloader):
def download(self):
# Logic for downloading the data
pass
Now I want to create an instance based on a parsed argument or a loaded config file, I wonder if the following is the best practice or if better ways exist to create dynamically an instance:
possible_instances = {'mnist': MNISTLoader, 'octmnist': OCTMNISTLoader}
choosen_dataset = 'mnist'
instance = possible_instances[choosen_dataset](dataset_name=choosen_dataset)
EDIT #1:
We also thought about using a function to call the classes dynamically. This function is than placed inside the module, which includes the classes:
def get_loader(loader_name:str) -> BaseLoader:
loaders = {
'mnist': MNISTLoader,
'octmnist': OCTMNISTLoader
}
try:
return loaders[loader_name]
except KeyError as err:
raise CustomError("good error message")
I am still not shure which is the most pythonic way to solve this.
I wouldn't say this has much to do with LSP since both classes inherit only from an abstract base class that never gets instantiated. You are simply sharing a dataset_name member of the base class to reduce code duplication. And forget about the default value in argument dataset_name='mnist' it has no point the way you are using it.
I don't know much about Python, but to instantiate a class from a string you would usually want to have a factory class where you would use whatever ugly method you come up with to map a string to an instance of a matching class, perhaps a simple if/elif. The factory method could contain something like this.
if loader_name == "mnist":
return MNISTLoader(loader_name)
elif loader_name == "octmnist":
return OCTMNISTLoader(loader_name)
# but you probably don't need to pass loader_name argument
While the above is not an elegant solution, you now have a reusable class with a method that is the single source of truth for how strings should map to type.
Also, you may remove the dataset_name init argument and member, unless you have a reason for objects to hold that information. I guess you tried to use the base init method like a factory but that's not the way to go.
One problem your code has is the methods "load" and "download", how will you know which one of the two methods to call if you fetch instances dynamically? Again, if you named them differently because you thought this had something to do with LSP, it doesn't. You are losing the advantage of polymorphism by having two methods with different names. Just have both classes extend the same abstract method (since you're using the ABC thing) "load" and be done with it. Then you can do whatever_the_object_type_is.load()

Overriding the Python help() function behaviour

I've created a Python proxy class that calls methods on a remote object. I've used a closure to override the doc attribute on my dynamically created methods so that
help(obj.method)
gives me the help on my remote object method. I then decided that I wanted to do the same thing for the object attributes. I have in my class something like:
class Proxy:
def __getattr__(self, name):
# Do stuff to get remote attribute
Now when calling code like this:
help(obj.attribute)
I of course get the doc string of the queried value type (string, int or whatever was returned).
The only way I can think to avoid this is to get a stack dump inside __getattr__(), identify and look for the help() call, and conditionally return object/class instead of the remotely queried value.
This is obviously non-ideal because there are quite a few ways one could specify the same thing, however it would help me from the command-line which is the most likely place I am to use this, so perhaps better than nothing.
Is there a better way?

New object instance every call

DJANGO APP
I have interface for sending e-mail in Django:
from my_app.utils import com
com.mail.email_category1(subject, template, ...)
...
com.mail.email_category2(subject, template, ...)
I have also another interafces for ie. for SMS:
com.sms.sms_category1(template, ...)
In my_app.utils.com there are functions defined:
# my_app.utils.com
mail = CommunicationMail()
sms = CommunicationSms()
...
and categories are methods of above classes.
QUESTION
Is it possible to call new instance of CommunicationMail every time I call com.mail.email_category...? The problem is that it is the same object instance every call, so ie. when running in parallel as a task, they share properties and overlaps.
This would be the recommended structure:
from my_app.utils import com
com.Mail().email_category1(template, ...)
where my_app.utils.com is:
Mail = CommunicationMail
If you really wanted to keep the com.mail.email_category1 notation, Python would let you, of course, being the dynamic language that it is
(__getattr__ documentation):
# my_app.utils.com
class CommunicationMailFactory:
def __getattr__(self, name):
instance = CommunicationMail()
return getattr(instance, name)
mail = CommunicationMailFactory()
But use the first method! “Why,” you ask.
For one, it is makes it clear what you are doing: You are instantiating a new instance and calling a method. This is not clear with the __getattr__ hackery.
Second, you can assign the freshly instantiated instance to a variable mail1 and then call mail1.email_category1(subject, template, ...) or whatever. You have no such normal, expected flexibility with the __getattr__ hackery.
Python modules are singleton, so it will only import it once, so mail = CommunicationMail() is executed once.
you can:
from my_app.utils import com
com.CommunicationSms().sms_category1(template, ...)

Class decorator to auto-update properties dictionary on disk?

I am working on a project where I have a number of custom classes to interface with a varied collection of data on a user's system. These classes only have properties as user-facing attributes. Some of these properties are decently resource intensive, so I want to only run the generation code once, and store the returned value on disk (cache it, that is) for faster retrieval on subsequent runs. As it stands, this is how I am accomplishing this:
def stored_property(func):
"""This ``decorator`` adds on-disk functionality to the `property`
decorator. This decorator is also a Method Decorator.
Each key property of a class is stored in a settings JSON file with
a dictionary of property names and values (e.g. :class:`MyClass`
stores its properties in `my_class.json`).
"""
#property
#functools.wraps(func)
def func_wrapper(self):
print('running decorator...')
try:
var = self.properties[func.__name__]
if var:
# property already written to disk
return var
else:
# property written to disk as `null`
return func(self)
except AttributeError:
# `self.properties` does not yet exist
return func(self)
except KeyError:
# `self.properties` exists, but property is not a key
return func(self)
return func_wrapper
class MyClass(object):
def __init__(self, wf):
self.wf = wf
self.properties = self._properties()
def _properties(self):
# get name of class in underscore format
class_name = convert(self.__class__.__name__)
# this is a library used (in Alfred workflows) for interacted with data stored on disk
properties = self.wf.stored_data(class_name)
# if no file on disk, or one of the properties has a null value
if properties is None or None in properties.values():
# get names of all properties of this class
propnames = [k for (k, v) in self.__class__.__dict__.items()
if isinstance(v, property)]
properties = dict()
for prop in propnames:
# generate dictionary of property names and values
properties[prop] = getattr(self, prop)
# use the external library to save that dictionary to disk in JSON format
self.wf.store_data(class_name, properties,
serializer='json')
# return either the data read from file, or data generated in situ
return properties
#this decorator ensures that this generating code is only run if necessary
#stored_property
def only_property(self):
# some code to get data
return 'this is my property'
This code works precisely as I need it, but it still forces me to manually add the _properties(self) method to each class wherein I need this functionality (currently, I have 3). What I want is a way to "insert" this functionality into any class I please. I think that a Class Decorator could get this job done, but try as I might, I can't quite figure out how to wrangle it.
For the sake of clarity (and in case a decorator is not the best way to get what I want), I will try to explain the overall functionality I am after. I want to write a class that contains some properties. The values of these properties are generated via various degrees of complex code (in one instance, I'm searching for a certain app's pref file, then searching for 3 different preferences (any of which may or may not exist) and determining the best single result from those preferences). I want the body of the properties' code only to contain the algorithm for finding the data. But, I don't want to run that algorithmic code each time I access that property. Once I generate the value once, I want to write it to disk and then simply read that on all subsequent calls. However, I don't want each value written to its own file; I want a dictionary of all the values of all the properties of a single class to be written to one file (so, in the example above, my_class.json would contain a JSON dictionary with one key, value pair). When accessing the property directly, it should first check to see if it already exists in the dictionary on disk. If it does, simply read and return that value. If it exists, but has a null value, then try to run the generation code (i.e. the code actually written in the property method) and see if you can find it now (if not, the method will return None and that will once again be written to file). If the dictionary exists and that property is not a key (my current code doesn't really make this possible, but better safe than sorry), run the generation code and add the key, value pair. If the dictionary doesn't exist (i.e. on the first instantiation of the class), run all generation code for all properties and create the JSON file. Ideally, the code would be able to update one property in the JSON file without rerunning all of the generation code (i.e. running _properties() again).
I know this is a bit peculiar, but I need the speed, human-readable content, and elegant code all together. I would really not to have to compromise on my goal. Hopefully, the description of what I want it clear enough. If not, let me know in a comment what doesn't make sense and I will try to clarify. But I do think that a Class Decorator could probably get me there (essentially by inserting the _properties() method into any class, running it on instantiation, and mapping its value to the properties attribute of the class).
Maybe I'm missing something, but it doesn't seem that your _properties method is specific to the properties that a given class has. I'd put that in a base class and have each of your classes with #stored_property methods subclass that. Then you don't need to duplicate the _properties method.
class PropertyBase(object):
def __init__(self, wf):
self.wf = wf
self.properties = self._properties()
def _properties(self):
# As before...
class MyClass(PropertyBase):
#stored_property
def expensive_to_calculate(self):
# Calculate it here
If for some reason you can't subclass PropertyBase directly (maybe you already need to have a different base class), you can probably use a mixin. Failing that, make _properties accept an instance/class and a workflow object and call it explicitly in __init__ for each class.

Python/Django OOP modify the following code to show get/set and constructor

Case. I want to modify and add the following behavior to the code below (it's a context processor):
After checking if a user is authenticated check the last time the balance was updated (cookie maybe) if it was updated in the last 5 mins do nothing, else get the new balance as normal.
def get_balance(request):
if request.user.is_authenticated():
balance = Account.objects.get(user=request.user).balance
else:
balance = 0
return {'account_balance': balance}
HOWEVER:
I want to learn a little more about OOP in Django/Python can some modify the example to achieve my goal include the use of:
Property: I come from Java, I want to set and get, it makes more sense to me. get balance if does not exist else create new one.
Constructor method: In Python I think I have to change this to a class and use init right?
UPDATE:
To use a construct I first think I need to create a class, I'm assuming this is ok using as a context processor in Django to do something like this:
class BalanceProcessor(request):
_balance = Account.objects.get(user=request.user).balance
#property
def get_balance(self):
return return {'account_balance': _balance}
#setter???
Python is not Java. In Python you don't create classes for no reason. Classes are for when you have data you want to encapsulate with code. In this case, there is no such thing: you simply get some data and return it. A class would be of no benefit here whatsoever.
In any case, even if you do create a class, once again Python is not Java, and you don't create getters and setters on properties unless you actually need to do some processing when you get and set. If you just want to access an instance attribute, then you simply access it.
Finally, your proposed code will not work for two reasons. Firstly, you are trying to inherit from request. That makes no sense: you should inherit from object unless you are subclassing something. Secondly, how are you expecting your class to be instantiated? Context processors are usually functions, and that means Django is expecting a callable. If you give the class as the context processor, then calling it will instantiate it: but then there's nothing that will call the get_balance method. And your code will fail because Django will pass the request into the instantation (as it is expecting to do with a function) and your __init__ doesn't expect that parameter.
It's fine to experiment with classes in Python, but a context processor is not the place for it.

Categories

Resources