Adding dynamic property to a python object - python

site = object()
mydict = {'name': 'My Site', 'location': 'Zhengjiang'}
for key, value in mydict.iteritems():
setattr(site, key, value)
print site.a # it doesn't work
The above code didn't work. Any suggestion?

The easiest way to populate one dict with another is the update() method, so if you extend object to ensure your object has a __dict__ you could try something like this:
>>> class Site(object):
... pass
...
>>> site = Site()
>>> site.__dict__.update(dict)
>>> site.a
Or possibly even:
>>> class Site(object):
... def __init__(self,dict):
... self.__dict__.update(dict)
...
>>> site = Site(dict)
>>> site.a

As docs say, object() returns featureless object, meaning it cannot have any attributes. It doesn't have __dict__.
What you could do is the following:
>>> site = type('A', (object,), {'a': 42})
>>> site.a
42

class site(object):
pass
for k,v in dict.iteritems():
setattr(site,k,v)
print site.a #it does works

Related

How do I update the dictionary of a mapped trait, after I've already constructed it?

I need to update the dictionary of a mapped trait some time after initial trait creation.
How do I do this?
The following code:
from traits.api import (HasTraits, Trait)
class bar(HasTraits):
zap = Trait("None", {"None": None})
def __init__(self):
# In reality, determined programmatically at runtime.
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
self.zap = Trait("None", new_dict)
theBar = bar()
yields:
Traceback (most recent call last):
File "tst_mapped_trait.py", line 13, in <module>
theBar = bar()
File "tst_mapped_trait.py", line 11, in __init__
self.zap = Trait("None", new_dict)
File "C:\Users\dbanas\Anaconda3\envs\pybert-dev\lib\site-packages\traits\trait_handlers.py", line 236, in error
object, name, self.full_info(object, name, value), value
traits.trait_errors.TraitError: The 'zap' trait of a bar instance must be 'None', but a value of <traits.traits.CTrait object at 0x00000000034AA9E8> <class 'traits.traits.CTrait'> was specified.```
Okay, the following code worked:
from traits.api import (HasTraits, Trait)
class bar(HasTraits):
zap = Trait("None", {"None": None})
def __init__(self):
# In reality, determined programmatically at runtime.
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
# self.zap = Trait("None", new_dict)
# self.zap.update(new_dict)
# self.trait_setq(zap=Trait("None", new_dict))
self.remove_trait("zap")
self.add_trait("zap", Trait("None", new_dict))
theBar = bar()
Note: The commented out lines are things I tried, which did not work.
I'm not sure I understand what you're after, but I can make a few recommendations:
Either is a good choice here if you allow both None and Dict.
Use dynamic initialization to create a value for a trait at runtime. It's preferred to using an __init__ method.
If you really need an __init__ method, you must call super inside of it for Traits to work properly, e.g. `super()init(*args, **kwargs)
Here's a version of your code that works and I think solves your problem.
from traits.api import (HasTraits, Either, Dict)
class bar(HasTraits):
zap = Either(None, Dict)
def _zap_default(self):
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
return new_dict
theBar = bar()
print(theBar.zap)
And here's some feedback on the code that didn't work. The line self.zap = Trait("None", new_dict) below doesn't work because it tries to create a Trait object but self.zap only accepts None or Dict. My recommendation is to use trait definitions only for typing, at the class-level. Within methods, use regular Python types.
from traits.api import (HasTraits, Trait)
class bar(HasTraits):
zap = Trait("None", {"None": None})
def __init__(self):
# In reality, determined programmatically at runtime.
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
self.zap = Trait("None", new_dict)
theBar = bar()
Here's second attempt at an answer given the original poster's comment
If you want the type of zap to be Dict and only Dict, then define it as such. You can also inline the initial value if it doesn't have to be computer at runtime:
>>> from traits.api import HasTraits, Dict
>>> class Bar(HasTraits):
... zap = Dict({5: 'e'})
...
>>> bar = Bar()
>>> bar.zap
{5: 'e'}
If it needs to be computed at runtime, then use dynamic initialization to initialize the value:
>>> class Bar(HasTraits):
... zap = Dict()
...
... def _zap_default(self):
... default = {}
... default[1] = 'a'
... return default
...
>>> bar_dynamic = Bar()
>>> bar_dynamic.zap
{1: 'a'}
Either way, the zap attribute on the Bar instance is a regular dictionary once the class has be instantiated (after bar = Bar()). You should use Trait types after instantiation, only regular Python objects. Traits is there to define and enforce types. The type() of the objects you're assigning to the typed traits (like zap here) are regular Python types.
Here's how you'd modify zap from outside of the class:
>>> bar.zap[2] = 'b'
>>> bar.zap
{5: 'e', 2: 'b'}
>>>
>>> bar_dynamic.zap[3] = 'c'
>>> bar_dynamic.zap
{1: 'a', 3: 'c'}
And now from inside the class, as a regular attribute on self:
>>> class Bar(HasTraits):
... zap = Dict()
...
... def _zap_default(self):
... default = {}
... default[1] = 'a'
... return default
...
... def add_pair(self, key, value):
... self.zap[key] = value
...
>>> bar_method = Bar()
>>> bar_method.zap
{1: 'a'}
>>> bar_method.add_pair(26, 'z')
>>> bar_method.zap
{1: 'a', 26: 'z'}

print out the class parameters on python

class FooTable(Base):
Id = Column(BIGINT, primary_key=True)
name = Column(VARCHAR(256), nullable=False)
username = Column(VARCHAR(256), nullable=False)
state = Column(VARCHAR(100), nullable=False)
city = Column(VARCHAR(256), nullable=False)
zip = Column(VARCHAR(100), nullable=False)
I want to print out the instantiation arguments of class Column. generally- python code to string. example:
for k, v in vars(FooTable).iteritems():
print k, get_class_attr(v)
>>> Id ["BIGINT", "primary_key=True"]
>>> name ["VARCHAR(256)", "nullable=False"]
...
I tried the inspect module, but found that it does not support it:
http://docs.python.org/library/inspect.html#inspect.getmembers
Thanks for any information
I'd suggest using __dict__ and then filterting its output as dir won't work with metaclasses either.
def filterFunction(field):
'''
This is a sample filtering function
'''
name, value = field
return not name.startswith("_")
for k, v in vars(FooTable).iteritems():
print k, filter(filterFunction, v.__dict__.items())
For that case for the following class
class X():
foo = "bar"
baz = True
Our filter
filter(filterFunction, X.__dict__.items())
would return
[('foo', 'bar'), ('baz', True)]
To get instantiation parameters, i'd check whether field name is in Column.__init__.im_self
Actually what you are asking for, doesn't make much sense. Burhan Khalid's answer is close, but not really. You want the actual arguments that the caller passed when instantiating
the class. Not the function's/constructor's signature.
But this is book keeping that the class' programmer should do on his own. And even then,
he wouldn't actually do that. He would configure each instance based on the passed
parameters.
e.g.
class Foo(object):
def __init__(self, *args, **kwargs):
self.is_cool = kwargs.get('whatever', False)
And then I would check the instance attribute:
f = Foo()
f.is_cool # False
and I would try to make sense of it, depending on what this means for my instance.
But I don't really care about the actual parameters passed. My instance will
configure itself based on what was passed.
You could of course write a class decorator that wraps any 3rd party Class and do
exactly what you need, but it is an overhead that doesn't seem justifiable in this case.
You can print the attributes of a class with:
print dir( Column )
This is if I correctly understand your question
attrs = [i for i in dir(obj) if not i.startswith('_')]
Keep in mind that attributes that begin with _ are generally considered "private". Here is a sample run:
>>> [i for i in dir(4) if not i.startswith('_')]
['conjugate', 'denominator', 'imag', 'numerator', 'real']
>>> [i for i in dir('') if not i.startswith('_')]
['capitalize', 'center', ... 'split', 'splitlines', ... 'upper', 'zfill']
>>> class Foo:
... a = 'Hello'
... def __init__(self):
... pass
... def hello(self):
... print 'Hello'
...
>>> [i for i in dir(Foo) if not i.startswith('_')]
['a', 'hello']
I think you are looking for this:
>>> class Foo:
... def bar(a,b,c):
... pass
...
>>> x = Foo()
>>> x.bar.func_code.co_varnames
('a', 'b', 'c')
Here is another attempt, and I think this does what you need; although its a bit convoluted.
>>> class Foo:
... a = 'b'
... def bar(y,z):
... pass
...
>>> funcs = [(i,getattr(Foo,i).func_code.co_varnames) for i in dir(Foo) \
... if hasattr(getattr(Foo,i),'func_code')]
>>>
>>> funcs
[('bar', ('y', 'z'))]
However if you want to know what arguments were passed to a class, then from the instance, use __dict__.
>>> class Foo:
... a = ''
... def __init__(self, b, c):
... self.a = b
... self.c = c
...
>>> funcs = [(i,getattr(Foo,i).func_code.co_varnames) for i in dir(Foo) if hasattr(getattr(Foo,i),'func_code')]
>>> funcs
[('__init__', ('self', 'b', 'c'))]
>>> i = Foo(1,2)
>>> i.__dict__
{'a': 1, 'c': 2}

Python's hasattr on list values of dictionaries always returns false?

I have a dictionary that sometimes receives calls for non-existent keys, so I try and use hasattr and getattr to handle these cases:
key_string = 'foo'
print "current info:", info
print hasattr(info, key_string)
print getattr(info, key_string, [])
if hasattr(info, key_string):
array = getattr(info, key_string, [])
array.append(integer)
info[key_string] = array
print "current info:", info
The first time this runs with integer = 1:
current info: {}
False
[]
current info: {'foo': [1]}
Running this code again with integer = 2:
instance.add_to_info("foo", 2)
current info: {'foo': [1]}
False
[]
current info: {'foo': [2]}
The first run is clearly successful ({'foo': [1]}), but hasattr returns false and getattr uses the default blank array the second time around, losing the value of 1 in the process! Why is this?
hasattr does not test for members of a dictionary. Use the in operator instead, or the .has_key method:
>>> example = dict(foo='bar')
>>> 'foo' in example
True
>>> example.has_key('foo')
True
>>> 'baz' in example
False
But note that dict.has_key() has been deprecated, is recommended against by the PEP 8 style guide and has been removed altogether in Python 3.
Incidentally, you'll run into problems by using a mutable class variable:
>>> class example(object):
... foo = dict()
...
>>> A = example()
>>> B = example()
>>> A.foo['bar'] = 'baz'
>>> B.foo
{'bar': 'baz'}
Initialize it in your __init__ instead:
class State(object):
info = None
def __init__(self):
self.info = {}
A dictionary key is not the same as an object attribute
thing1 = {'a', 123}
hasattr(thing1, 'a') # False
class c: pass
thing2 = c()
thing2.a = 123
hasattr(thing2, 'a') # True
To test for elements in a list/dictionary, use in. To use defaults, you can use dict.get:
def add_to_info(self, key_string, integer):
array = self.info.get(key_string, [])
array.append(integer)
self.info[key_string] = array
Or use defaultdict:
from collections import defaultdict
class State(object):
info = defaultdict(list)
def add_to_info(self, key_string, integer):
self.info[key_string].append(integer)
Looks like all you need is one line:
def add_to_info(self, key_string, integer):
self.info.setdefault(key_string, []).append(integer)
If you convert your dictionary to a dot dictionary you can use hasattr and getattr for it:
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
newdict = dotdict(olddict)
You may use .get() method on a dict type object. This method doesnt raise a key error if isnt defined. Also, as the getattr() for objects you may specify on it a default value.
>> {'name': 'Me'}.get('name1', 'StackOverflow')
>> 'StackOverflow'

Getting the class name of an instance

How do I find out the name of the class used to create an instance of an object in Python?
I'm not sure if I should use the inspect module or parse the __class__ attribute.
Have you tried the __name__ attribute of the class? ie type(x).__name__ will give you the name of the class, which I think is what you want.
>>> import itertools
>>> x = itertools.count(0)
>>> type(x).__name__
'count'
If you're still using Python 2, note that the above method works with new-style classes only (in Python 3+ all classes are "new-style" classes). Your code might use some old-style classes. The following works for both:
x.__class__.__name__
Do you want the name of the class as a string?
instance.__class__.__name__
type() ?
>>> class A:
... def whoami(self):
... print(type(self).__name__)
...
>>>
>>> class B(A):
... pass
...
>>>
>>>
>>> o = B()
>>> o.whoami()
'B'
>>>
class A:
pass
a = A()
str(a.__class__)
The sample code above (when input in the interactive interpreter) will produce '__main__.A' as opposed to 'A' which is produced if the __name__ attribute is invoked. By simply passing the result of A.__class__ to the str constructor the parsing is handled for you. However, you could also use the following code if you want something more explicit.
"{0}.{1}".format(a.__class__.__module__,a.__class__.__name__)
This behavior can be preferable if you have classes with the same name defined in separate modules.
The sample code provided above was tested in Python 2.7.5.
In Python 2,
type(instance).__name__ != instance.__class__.__name__
# if class A is defined like
class A():
...
type(instance) == instance.__class__
# if class A is defined like
class A(object):
...
Example:
>>> class aclass(object):
... pass
...
>>> a = aclass()
>>> type(a)
<class '__main__.aclass'>
>>> a.__class__
<class '__main__.aclass'>
>>>
>>> type(a).__name__
'aclass'
>>>
>>> a.__class__.__name__
'aclass'
>>>
>>> class bclass():
... pass
...
>>> b = bclass()
>>>
>>> type(b)
<type 'instance'>
>>> b.__class__
<class __main__.bclass at 0xb765047c>
>>> type(b).__name__
'instance'
>>>
>>> b.__class__.__name__
'bclass'
>>>
Alternatively you can use the classmethod decorator:
class A:
#classmethod
def get_classname(cls):
return cls.__name__
def use_classname(self):
return self.get_classname()
Usage:
>>> A.get_classname()
'A'
>>> a = A()
>>> a.get_classname()
'A'
>>> a.use_classname()
'A'
Good question.
Here's a simple example based on GHZ's which might help someone:
>>> class person(object):
def init(self,name):
self.name=name
def info(self)
print "My name is {0}, I am a {1}".format(self.name,self.__class__.__name__)
>>> bob = person(name='Robert')
>>> bob.info()
My name is Robert, I am a person
Apart from grabbing the special __name__ attribute, you might find yourself in need of the qualified name for a given class/function. This is done by grabbing the types __qualname__.
In most cases, these will be exactly the same, but, when dealing with nested classes/methods these differ in the output you get. For example:
class Spam:
def meth(self):
pass
class Bar:
pass
>>> s = Spam()
>>> type(s).__name__
'Spam'
>>> type(s).__qualname__
'Spam'
>>> type(s).Bar.__name__ # type not needed here
'Bar'
>>> type(s).Bar.__qualname__ # type not needed here
'Spam.Bar'
>>> type(s).meth.__name__
'meth'
>>> type(s).meth.__qualname__
'Spam.meth'
Since introspection is what you're after, this is always you might want to consider.
You can simply use __qualname__ which stands for qualified name of a function or class
Example:
>>> class C:
... class D:
... def meth(self):
... pass
...
>>> C.__qualname__
'C'
>>> C.D.__qualname__
'C.D'
>>> C.D.meth.__qualname__
'C.D.meth'
documentation link qualname
To get instance classname:
type(instance).__name__
or
instance.__class__.__name__
both are the same
You can first use type and then str to extract class name from it.
class foo:pass;
bar:foo=foo();
print(str(type(bar))[8:-2][len(str(type(bar).__module__))+1:]);
Result
foo
If you're looking to solve this for a list (or iterable collection) of objects, here's how I would solve:
from operator import attrgetter
# Will use a few data types to show a point
my_list = [1, "2", 3.0, [4], object(), type, None]
# I specifically want to create a generator
my_class_names = list(map(attrgetter("__name__"), map(type, my_list))))
# Result:
['int', 'str', 'float', 'list', 'object', 'type', 'NoneType']
# Alternatively, use a lambda
my_class_names = list(map(lambda x: type(x).__name__, my_list))

Python dictionary from an object's fields

Do you know if there is a built-in function to build a dictionary from an arbitrary object? I'd like to do something like this:
>>> class Foo:
... bar = 'hello'
... baz = 'world'
...
>>> f = Foo()
>>> props(f)
{ 'bar' : 'hello', 'baz' : 'world' }
NOTE: It should not include methods. Only fields.
Note that best practice in Python 2.7 is to use new-style classes (not needed with Python 3), i.e.
class Foo(object):
...
Also, there's a difference between an 'object' and a 'class'. To build a dictionary from an arbitrary object, it's sufficient to use __dict__. Usually, you'll declare your methods at class level and your attributes at instance level, so __dict__ should be fine. For example:
>>> class A(object):
... def __init__(self):
... self.b = 1
... self.c = 2
... def do_nothing(self):
... pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}
A better approach (suggested by robert in comments) is the builtin vars function:
>>> vars(a)
{'c': 2, 'b': 1}
Alternatively, depending on what you want to do, it might be nice to inherit from dict. Then your class is already a dictionary, and if you want you can override getattr and/or setattr to call through and set the dict. For example:
class Foo(dict):
def __init__(self):
pass
def __getattr__(self, attr):
return self[attr]
# etc...
Instead of x.__dict__, it's actually more pythonic to use vars(x).
The dir builtin will give you all the object's attributes, including special methods like __str__, __dict__ and a whole bunch of others which you probably don't want. But you can do something like:
>>> class Foo(object):
... bar = 'hello'
... baz = 'world'
...
>>> f = Foo()
>>> [name for name in dir(f) if not name.startswith('__')]
[ 'bar', 'baz' ]
>>> dict((name, getattr(f, name)) for name in dir(f) if not name.startswith('__'))
{ 'bar': 'hello', 'baz': 'world' }
So can extend this to only return data attributes and not methods, by defining your props function like this:
import inspect
def props(obj):
pr = {}
for name in dir(obj):
value = getattr(obj, name)
if not name.startswith('__') and not inspect.ismethod(value):
pr[name] = value
return pr
I've settled with a combination of both answers:
dict((key, value) for key, value in f.__dict__.iteritems()
if not callable(value) and not key.startswith('__'))
I thought I'd take some time to show you how you can translate an object to dict via dict(obj).
class A(object):
d = '4'
e = '5'
f = '6'
def __init__(self):
self.a = '1'
self.b = '2'
self.c = '3'
def __iter__(self):
# first start by grabbing the Class items
iters = dict((x,y) for x,y in A.__dict__.items() if x[:2] != '__')
# then update the class items with the instance items
iters.update(self.__dict__)
# now 'yield' through the items
for x,y in iters.items():
yield x,y
a = A()
print(dict(a))
# prints "{'a': '1', 'c': '3', 'b': '2', 'e': '5', 'd': '4', 'f': '6'}"
The key section of this code is the __iter__ function.
As the comments explain, the first thing we do is grab the Class items and prevent anything that starts with '__'.
Once you've created that dict, then you can use the update dict function and pass in the instance __dict__.
These will give you a complete class+instance dictionary of members. Now all that's left is to iterate over them and yield the returns.
Also, if you plan on using this a lot, you can create an #iterable class decorator.
def iterable(cls):
def iterfn(self):
iters = dict((x,y) for x,y in cls.__dict__.items() if x[:2] != '__')
iters.update(self.__dict__)
for x,y in iters.items():
yield x,y
cls.__iter__ = iterfn
return cls
#iterable
class B(object):
d = 'd'
e = 'e'
f = 'f'
def __init__(self):
self.a = 'a'
self.b = 'b'
self.c = 'c'
b = B()
print(dict(b))
A downside of using __dict__ is that it is shallow; it won't convert any subclasses to dictionaries.
If you're using Python3.5 or higher, you can use jsons:
>>> import jsons
>>> jsons.dump(f)
{'bar': 'hello', 'baz': 'world'}
To build a dictionary from an arbitrary object, it's sufficient to use __dict__.
This misses attributes that the object inherits from its class. For example,
class c(object):
x = 3
a = c()
hasattr(a, 'x') is true, but 'x' does not appear in a.__dict__
Python3.x
return dict((key, value) for key, value in f.__dict__.items() if not callable(value) and not key.startswith('__'))
Late answer but provided for completeness and the benefit of googlers:
def props(x):
return dict((key, getattr(x, key)) for key in dir(x) if key not in dir(x.__class__))
This will not show methods defined in the class, but it will still show fields including those assigned to lambdas or those which start with a double underscore.
vars() is great, but doesn't work for nested objects of objects
Convert nested object of objects to dict:
def to_dict(self):
return json.loads(json.dumps(self, default=lambda o: o.__dict__))
I think the easiest way is to create a getitem attribute for the class. If you need to write to the object, you can create a custom setattr . Here is an example for getitem:
class A(object):
def __init__(self):
self.b = 1
self.c = 2
def __getitem__(self, item):
return self.__dict__[item]
# Usage:
a = A()
a.__getitem__('b') # Outputs 1
a.__dict__ # Outputs {'c': 2, 'b': 1}
vars(a) # Outputs {'c': 2, 'b': 1}
dict generates the objects attributes into a dictionary and the dictionary object can be used to get the item you need.
In 2021, and for nested objects/dicts/json use pydantic BaseModel - will convert nested dicts and nested json objects to python objects and JSON and vice versa:
https://pydantic-docs.helpmanual.io/usage/models/
>>> class Foo(BaseModel):
... count: int
... size: float = None
...
>>>
>>> class Bar(BaseModel):
... apple = 'x'
... banana = 'y'
...
>>>
>>> class Spam(BaseModel):
... foo: Foo
... bars: List[Bar]
...
>>>
>>> m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])
Object to dict
>>> print(m.dict())
{'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}]}
Object to JSON
>>> print(m.json())
{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}
Dict to object
>>> spam = Spam.parse_obj({'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y2'}]})
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y2')])
JSON to object
>>> spam = Spam.parse_raw('{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}')
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')])
Dataclass(from Python 3.7) is another option which can be used for converting class properties to dict. asdict can be used along with dataclass objects
for the conversion.
Example:
#dataclass
class Point:
x: int
y: int
p = Point(10, 20)
asdict(p) # it returns {'x': 10, 'y': 20}
As mentioned in one of the comments above, vars currently isn't universal in that it doesn't work for objects with __slots__ instead of a normal __dict__. Moreover, some objecs (e.g., builtins like str or int) have neither a __dict__ nor __slots__.
For now, a more versatile solution could be this:
def instance_attributes(obj: Any) -> Dict[str, Any]:
"""Get a name-to-value dictionary of instance attributes of an arbitrary object."""
try:
return vars(obj)
except TypeError:
pass
# object doesn't have __dict__, try with __slots__
try:
slots = obj.__slots__
except AttributeError:
# doesn't have __dict__ nor __slots__, probably a builtin like str or int
return {}
# collect all slots attributes (some might not be present)
attrs = {}
for name in slots:
try:
attrs[name] = getattr(obj, name)
except AttributeError:
continue
return attrs
Example:
class Foo:
class_var = "spam"
class Bar:
class_var = "eggs"
__slots__ = ["a", "b"]
>>> foo = Foo()
>>> foo.a = 1
>>> foo.b = 2
>>> instance_attributes(foo)
{'a': 1, 'b': 2}
>>> bar = Bar()
>>> bar.a = 3
>>> instance_attributes(bar)
{'a': 3}
>>> instance_attributes("baz")
{}
Rant:
It's a pity that this isn't built into vars already. Many builtins in Python promise to be "the" solution to a problem but then there's always several special cases that aren't handled... And one just ends up having to write the code manually in any case.
If you want to list part of your attributes, override __dict__:
def __dict__(self):
d = {
'attr_1' : self.attr_1,
...
}
return d
# Call __dict__
d = instance.__dict__()
This helps a lot if your instance get some large block data and you want to push d to Redis like message queue.
PYTHON 3:
class DateTimeDecoder(json.JSONDecoder):
def __init__(self, *args, **kargs):
JSONDecoder.__init__(self, object_hook=self.dict_to_object,
*args, **kargs)
def dict_to_object(self, d):
if '__type__' not in d:
return d
type = d.pop('__type__')
try:
dateobj = datetime(**d)
return dateobj
except:
d['__type__'] = type
return d
def json_default_format(value):
try:
if isinstance(value, datetime):
return {
'__type__': 'datetime',
'year': value.year,
'month': value.month,
'day': value.day,
'hour': value.hour,
'minute': value.minute,
'second': value.second,
'microsecond': value.microsecond,
}
if isinstance(value, decimal.Decimal):
return float(value)
if isinstance(value, Enum):
return value.name
else:
return vars(value)
except Exception as e:
raise ValueError
Now you can use above code inside your own class :
class Foo():
def toJSON(self):
return json.loads(
json.dumps(self, sort_keys=True, indent=4, separators=(',', ': '), default=json_default_format), cls=DateTimeDecoder)
Foo().toJSON()
Try:
from pprint import pformat
a_dict = eval(pformat(an_obj))
Python 3.7+ in 2023
You can add the dataclass decorator to your class and define a custom JSON serializer, then json.dumps will work (and you can extend it to work with non-serializable attributes by providing a custom encoder to cls).
f=Foo()
json.dumps(f, cls=CustomJSONEncoder)
{"bar": "hello", "baz": "world", "modified": "2023-02-08T11:49:15.675837"}
A custom JSON serializer can be easily modified to make it compatible with any type that isn't natively JSON serializable.
from datetime import datetime
import dataclasses
import json
#dataclasses.dataclass # <<-- add this decorator
class Foo():
"""An example dataclass."""
bar: str = "hello"
baz: str = "world"
modified: datetime = Column(DateTime(timezone=True), default=datetime.utcnow)
class CustomJSONEncoder(json.JSONEncoder): # <<-- Add this custom encoder
"""Custom JSON encoder for the DB class."""
def default(self, o):
if dataclasses.is_dataclass(o): # this serializes anything dataclass can handle
return dataclasses.asdict(o)
if isinstance(o, datetime): # this adds support for datetime
return o.isoformat()
return super().default(o)
To further extend it for any non-serializable type, add another if statement to the custom encoder class that returns something serializable (e.g. str).

Categories