How to convert Object with Properties to JSON without "_" in Python 3? - python

I would like to convert an Python object into JSON-format.
The private attributes of the class User are defined using properties. The method to_Json() I have found here
class User:
def __init__(self):
self._name = None
self._gender = None
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
#property
def gender(self):
return self._gender
#gender.setter
def gender(self, gender):
self._gender = gender
def to_Json(self):
return json.dumps(self, default=lambda o: o.__dict__, allow_nan=False, sort_keys=False, indent=4)
The output using this class and method is:
{
"_name": "Peter",
"_age": 26
}
What is the easiest way to get rid of the underscores in the JSON-format? (I want "name" instead of "_name") Removing the underscore in the class is not an option since I get an error (max. recursion depth). I think renaming the methods of the attributes would solve this problem, but is this the best solution here?
Renaming all keys before the json.dumbs (see here) is not a practical approach because I my class is more complex than the above example.
So, what is the best practice to convert a Python object into JSON-format as fast as possible?

If the example code you posted mirrors your real code, there really isn't any reason for the properties at all. You could just do:
class User(object):
def __init__(self):
self.name = None
self.age = None
since you're not really hiding anything from the user behind the underscores and properties anyway.
If you do need to do the transformation, I like to do it in a custom encoder:
class MyEncoder(json.JSONEncoder):
def default(self, o):
return {k.lstrip('_'): v for k, v in vars(o).items()}
json_encoded_user = json.dumps(some_user, cls=MyEncoder)

In Python, you'd normally not use properties for basic attributes. You'd leave name and age to be directly accessible attributes. There is no need to wrap those in property objects unless you need to transform the data when getting or setting.
If you have good reasons to use attributes with underscores but reflect them as JSON dictionaries, you can transform your dictionary when converting to a dictionary:
object_dict = lambda o: {key.lstrip('_'): value for key, value in o.__dict__.items()}
return json.dumps(self, default=object_dict, allow_nan=False, sort_keys=False, indent=4)
Note that this does nothing to prevent collisions. If you have both a _name and a name attribute on your instance, you'll clobber one or the other.

I had a similar problem, but I had private fields with two underscore characters.
class User:
def __init__(self, id, name):
self.id = id
self.name = name
#property
def id(self):
return self.__id
#id.setter
def id(self, id):
self.__id = id
#property
def name(self):
return self.__name
#name.setter
def name(self, name):
self.__name = name
Therefore, my json encoder is slightly different
from json import JSONEncoder
def beautify_key(str):
index = str.index('__')
if index <= 0:
return str
return str[index + 2:]
class JsonEncoder(JSONEncoder):
def default(self, o):
return {beautify_key(k): v for k, v in vars(o).items()}
Full answer is here.

Related

What is the difference between readable property method and a callable function that is just returns the data as a property can?

I have a property that returns list of names with "ash" in it
class BaseClass(object):
def __init__(self):
self.filter_key = ""
self.name = ""
def filter_names(self, filter_key):
self.filter_key = filter_key
#property
def student_names(self):
return self.names
def callable_function_names(self):
return names
and then student class that inherits BaseClass
class StudentClass(BaseClass):
#property
def student_names(self):
names = super(StudentClass, self).student_names
return [name for name in names if self.filter_students in name]
#property
def filter_key(self):
"""Gets """
return self.filter_key
#slot_key.setter
def filter_key(self, key):
"""Sets name filter"""
self.filter_names(key)
# or by doing :
def callable_function_names(self):
names = super(StudentClass, self).callable_function_names()
return [name for name in names if self.filter_students in name]
So if I create obj of the student class.
studentclsObj = StudentClass()
studentclsObj.filter_key = "ash"
print studentclsObj.student_names
print studentclsObj.callable_function_names()
I can achieve the same result with both above prints, is there any difference and what is preferred and right way to do ?
One use case of properties is not breaking API. This is one of main strengths of python IMO. You can take a function, make transform it in a callable object, add new functionality without breaking old code, now the property
I see three main uses of properties over attributes,
Read only attributes
Is easy to create read only attributes with properties. They are non verbose, self documenting and simple
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
Validation on writable properties
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, val):
if valid(val):
self._bar = val
This is a kind of defensive programming
Keep API compatibility
Imagine that you have a class for a bank account, with
a balance property
class BankAccount:
def __init__(self):
self.balance = 0
You have this code and it works fine. But know your client
says, I need you to log every balance lookup. You can replace
the attribute by a property without breaking old code
class BankAccount:
def __init__(self):
self._balance = 0
#property
def balance(self):
self.log_balance_read()
return self._balance
There is no difference between a property and a method which return the same value. Go for the simpler, use method for actions and state changes and attributes for real attributes, if you need to add logic to attribute lookup, python will let you do it

Python class objectivity convention

Is it a good style to create classes like that ? I read the PEP8 document but I didn't saw any good example. If not how is it a proper way ? Thanks for any answers.
class Zone:
def __init__(self, index=None, name=None):
self._index = index
self._name = name
#property
def index(self):
return self._index
#property
def name(self):
return self._name
#index.setter
def index(self, index):
self._index = index
#name.setter
def name(self, name):
self._name = name
Your setters and getters don't do anything. With your implementation, the user of this class does this:
z = Zone()
z.name = 'foo'
print(z.name)
Compare to this implementation:
class Zone:
def __init__(self, index=None, name=None):
self.index = index
self.name = name
z = Zone()
z.name = 'foo'
print(z.name)
It works exactly the same with a lot less code.
Unless you do anything in your setters and/or getters, you don't need them.
If what you intend doing is encapsulating your data and setting it with setters and getting it with getters, then what you did will not be helpful. you declared the _name and _index as protected, it does not mean it cannot be accessed by extenal functions, so functions outside the class can easily access and change them, making your getter and setter to be useless.
However, you can declare them as private by using one additional underscore in front, so that your property class will be removed and then the setters class will be useful, it will no longer be accessed by external functions.
class Zone:
def __init__(self,index=None,name=None):
self.__index = index
self.__name = name
def index(self, index):
self.__index = index
def name(self, name):
self.__name = name
def get_name(self):
return self.__name
zone=Zone()
zone.name('ben')
print(zone.get_name())
>>>ben
print(zone.__name)
>>> AttributeError: 'Zone' object has no attribute '__name'

Python property setting does not assign the right instance attributes (leading underscores) when assigning via __setattr__

I can't set the right properties of an instance when setting their attributes via setattr in a factory method.
Given the following code where data is a simple dict containing e.g. { "age": "64", ...}
def factory(data):
obj = MyClass()
for k, v in data.items():
setattr(obj, k, v)
return obj
class MyClass(object):
def __init__(self):
self._age = None
# more...
#property
def age(self):
return self._age
#age.setter
def age(self, value):
some_validation(value)
self._age = value
def __setattr__(self, name, value):
object.__setattr__(self, name, value)
def __getitem__(self, item):
return self.__dict__.get(item, None)
def __getattr__(self, item):
self.__dict__[item] = None
return None
def __str__(self):
return json.dumps(self, default=lambda o: o.__dict__)
c = factory(data)
print(c)
I always get the following output when printing the created object:
{"_age": "64", ...}
But I need to have
{"age": "64", ...}
Why does the setattr method assign the leading underscore?
Some of the things you are trying to achieve get mixed up, like wanting to print __dict__ for a readable representation, but using private attributes for properties. Let's start from scratch and see how we can implement your class correctly.
You are trying to implement a class which attributes can be accessed both as keys and attributes. That is fine and can be accomplished in a more concise way.
class MyClass:
...
def __getitem__(self, item):
return self.__getattribute__(item)
def __setitem__(self, key, value):
return self.__setattr__(key, value)
You also want None to be returned when an attribute does not exist. This is covered by __getattr__ which is called exactly when an attribute does not exist.
def __getattr__(self, _):
return None
Then you want to add some validation to some attributes with property. It is indeed the correct way to proceed.
#property
def age(self):
return self._age
#age.setter
def age(self, value):
# some validation here
self._age = value
And finally you want to be able to have a nice string representation of your instance. We have to be careful for that since we had to add some private attributes that we do not want to print.
What we are going to do is implement a method keys to allow casting to dict. This method will only return keys for attributes which are not private nor methods.
def keys(self):
return [k for k in dir(self) if not k.startswith('_') and not callable(self[k])]
def __str__(self):
return json.dumps(dict(self))
This does the right thing.
obj = MyClass()
obj.age = 3
print(obj)
# prints: {"age": 3}

Writing a function to define class properties

I was recently writing a definition for a pretty basic data class in Python and I came up with the following:
class A:
def __init__(self, **kwargs):
self.__a1 = kwargs.get('some_value', -1)
#property
def a1(self):
return self.__a1
#a1.setter
def a1(self, new_a1):
self.__a1 = new_a1
And it goes on. In this case, the value -1 could be replaced with a variety of "null" values: -1, "", [], etc., and some_value comes from an Enum I defined earlier.
Because the class definition contains several of these property definitions, and they're all very "same-y", I'd like to write a function to do this for me. I'm pretty sure it's possible in Python but I've never tried it so I was hoping for some pointers.
Assuming you want to simplify the repetitive property definitions, you can use a generic descriptor to simplify this significantly:
class ProtectedAttribute(object):
"""Basic descriptor functionality for a protected attribute.
Args:
name (str): The name of the attribute to back the descriptor
(usually the name the descriptor is assigned to with a single
additional leading underscore).
"""
def __init__(self, name, **kwargs):
self.name = name
def __get__(self, obj, typ):
return getattr(obj, self.name)
def __set__(self, obj, value):
setattr(obj, self.name, value)
def __delete__(self, obj):
delattr(obj, self.name)
Now you can just do:
class A(object):
a1 = ProtectedAttribute('__a1')
def __init__(self, **kwargs):
self.a1 = kwargs.get("some_value", -1)
Note also the use of dict.get to simplify __init__.

How to list all class properties

I have class SomeClass with properties. For example id and name:
class SomeClass(object):
def __init__(self):
self.__id = None
self.__name = None
def get_id(self):
return self.__id
def set_id(self, value):
self.__id = value
def get_name(self):
return self.__name
def set_name(self, value):
self.__name = value
id = property(get_id, set_id)
name = property(get_name, set_name)
What is the easiest way to list properties? I need this for serialization.
property_names=[p for p in dir(SomeClass) if isinstance(getattr(SomeClass,p),property)]
import inspect
def isprop(v):
return isinstance(v, property)
propnames = [name for (name, value) in inspect.getmembers(SomeClass, isprop)]
inspect.getmembers gets inherited members as well (and selects members by a predicate, here we coded isprop because it's not among the many predefined ones in module inspect; you could also use a lambda, of course, if you prefer).

Categories

Resources