- Load All Method Results in Python Class to Dictionary
- 4 Answers 4
- Python object and dictionary convertion
- Convert from class to dictionary
- Convert from dictionary to class
- Kien Nguyen Trung
- Python – In python, how to cast a class object to a dict
- Best Solution
- Option 1:
- Option 1a:
- Option 2:
- Option 3:
- Option 4:
- Option 5:
- Option 6:
Load All Method Results in Python Class to Dictionary
If I have a class like that below (in reality, it has a lot more methods), and I want to load each method’s results into a dictionary, is there a faster way to do features_to_dict , that’s also modular if I add new methods?
from bs4 import BeautifulSoup class CraigsPage(): def __init__(self, page_file): self._page = open(page_file).read() self.soup = BeautifulSoup(self._page) self.title = self.soup.title.string self.body = str(self.soup.find('section', )) def get_title_char_count(self): return len(list(self.title.replace(' ', ''))) def get_title_word_count(self): return len(self.title.split()) def get_body_char_count(self): return len(list(self.body.replace(' ', ''))) def features_to_dict(self): feature_dict = <> feature_dict['title_char_count'] = self.get_title_char_count() feature_dict['title_word_count'] = self.get_title_word_count() feature_dict['body_char_count'] = self.get_body_char_count() return feature_dict
4 Answers 4
The inspect module is handy for this sort of stuff:
def features_to_dict(self): members = inspect.getmembers(self, inspect.ismethod) return
Python classes have __dict__ attribute, that stores all attributes for the class as a dictionary. The following snippet iterates over __dict__ trying to find functions beginning with get , and automatically runs them, storing the results to a dict:
class A(object): def get_a(self): return 1 def get_b(self): return 2 def features_to_dict(self): self.d = <> for f_name, f in A.__dict__.iteritems(): if 'get' in f_name: self.d[f_name[4:]] = f(self) a = A() a.features_to_dict() print a.d
Use dir() method instead of dict attribute.
class A(object): def method(self): return 123 def call_all(self): skip = dir(object) + ['call_all'] results = <> for key in dir(self): if key not in skip and callable(getattr(self, key)): try: resultsPython convert class to dict = getattr(self, key)() except Exception as e: resultsPython convert class to dict = e return results
Python object and dictionary convertion
When programing in Python, sometimes you want to convert an object to dictionary and vise versal. In this article, I share my expericences to do that.
Convert from class to dictionary
When you want to convert a class to dictionary just define class override from object (this is important) and then call method __dict__ :
>>> class Person(object): def __init__(self, age, name): self.age = age self.name = name >>> a = Person( 'kiennt', 24) >>> a.__dict__ 'name' : 'kiennt', 'age' : 24 >
Convert from dictionary to class
In contrast, if you want to convert a dictionary to object, use this method. I did not invent this method, it is borrowed from stackoverflow.com)
>>> class Struct(object): def __init__(self, **entries): self.__dict__.update(entries) >>> d = 'name' : 'kiennt', 'age' : 24 > >>> a = Struct(**d) >>> a.name "kiennt" >>> a.age 24
I change above methods like bellow to get recusive convertion dictionary:
class Struct(object): def __init__(self, adict): """Convert a dictionary to a class @param :adict Dictionary """ self.__dict__.update(adict) for k, v in adict.items(): if isinstance(v, dict): self.__dict__[k] = Struct(v) def get_object(adict): """Convert a dictionary to a class @param :adict Dictionary @return :class:Struct """ return Struct(adict)
That ’s it. Happy working with python!
Kien Nguyen Trung
A father, husband and Elixir lover.
Python – In python, how to cast a class object to a dict
What do I need to define for this to work? I tried substituting both __dict__ and dict for __what_goes_here__ , but dict(w) resulted in a TypeError: Wharrgarbl object is not iterable in both cases. I don’t think simply making the class iterable will solve the problem. I also attempted many googles with as many different wordings of «python cast object to dict» as I could think of but couldn’t find anything relevant :
Also! Notice how calling w.__dict__ won’t do what I want because it’s going to contain w.version and w.sum . I want to customize the cast to dict in the same way that I can customize the cast to int by using def int(self) .
I know that I could just do something like this
But I am assuming there is a pythonic way to make dict(w) work since it is the same type of thing as int(w) or str(w) . If there isn’t a more pythonic way, that’s fine too, just figured I’d ask. Oh! I guess since it matters, this is for python 2.7, but super bonus points for a 2.4 old and busted solution as well.
There is another question Overloading __dict__() on python class that is similar to this one but may be different enough to warrant this not being a duplicate. I believe that OP is asking how to cast all the data in his class objects as dictionaries. I’m looking for a more customized approach in that I don’t want everything in __dict__ included in the dictionary returned by dict() . Something like public vs private variables may suffice to explain what I’m looking for. The objects will be storing some values used in calculations and such that I don’t need/want to show up in the resulting dictionaries.
UPDATE:
I’ve chosen to go with the asdict route suggested but it was a tough choice selecting what I wanted to be the answer to the question. Both @RickTeachey and @jpmc26 provided the answer I’m going to roll with but the former had more info and options and landed on the same result as well and was upvoted more so I went with it. Upvotes all around though and thanks for the help. I’ve lurked long and hard on stackoverflow and I’m trying to get my toes in the water more.
Best Solution
There are at least five six ways. The preferred way depends on what your use case is.
Option 1:
Simply add an asdict() method.
Based on the problem description I would very much consider the asdict way of doing things suggested by other answers. This is because it does not appear that your object is really much of a collection:
class Wharrgarbl(object): . def asdict(self): return
Using the other options below could be confusing for others unless it is very obvious exactly which object members would and would not be iterated or specified as key-value pairs.
Option 1a:
Inherit your class from ‘typing.NamedTuple’ (or the mostly equivalent ‘collections.namedtuple’ ), and use the _asdict method provided for you.
from typing import NamedTuple class Wharrgarbl(NamedTuple): a: str b: str c: str sum: int = 6 version: str = 'old'
Using a named tuple is a very convenient way to add lots of functionality to your class with a minimum of effort, including an _asdict method. However, a limitation is that, as shown above, the NT will include all the members in its _asdict .
If there are members you don’t want to include in your dictionary, you’ll need to modify the _asdict result:
from typing import NamedTuple class Wharrgarbl(NamedTuple): a: str b: str c: str sum: int = 6 version: str = 'old' def _asdict(self): d = super()._asdict() del d['sum'] del d['version'] return d
Another limitation is that NT is read-only. This may or may not be desirable.
Option 2:
def __iter__(self): yield 'a', self.a yield 'b', self.b yield 'c', self.c
This works because the dict() constructor accepts an iterable of (key, value) pairs to construct a dictionary. Before doing this, ask yourself the question whether iterating the object as a series of key,value pairs in this manner- while convenient for creating a dict — might actually be surprising behavior in other contexts. E.g., ask yourself the question «what should the behavior of list(my_object) be. «
Additionally, note that accessing values directly using the get item obj[«a»] syntax will not work, and keyword argument unpacking won’t work. For those, you’d need to implement the mapping protocol.
Option 3:
Implement the mapping protocol. This allows access-by-key behavior, casting to a dict without using __iter__ , and also provides two types of unpacking behavior:
- mapping unpacking behavior:
- keyword unpacking behavior, but only if all the keys are strings: dict(**my_obj)
The mapping protocol requires that you provide (at minimum) two methods together: keys() and __getitem__ .
class MyKwargUnpackable: def keys(self): return list("abc") def __getitem__(self, key): return dict(zip("abc", "one two three".split()))Python convert class to dict
Now you can do things like:
>>> m=MyKwargUnpackable() >>> m["a"] 'one' >>> dict(m) # cast to dict directly >>> dict(**m) # unpack as kwargs
As mentioned above, if you are using a new enough version of python you can also unpack your mapping-protocol object into a dictionary comprehension like so (and in this case it is not required that your keys be strings):
Note that the mapping protocol takes precedence over the __iter__ method when casting an object to a dict directly (without using kwarg unpacking, i.e. dict(m) ). So it is possible- and might be sometimes convenient- to cause the object to have different behavior when used as an iterable (e.g., list(m) ) vs. when cast to a dict ( dict(m) ).
But note also that with regular dictionaries, if you cast to a list, it will give the KEYS back, and not the VALUES as you require. If you implement another nonstandard behavior for __iter__ (returning values instead of keys), it could be surprising for other people using your code unless it is very obvious why this would happen.
EMPHASIZED: Just because you CAN use the mapping protocol, does NOT mean that you SHOULD do so. Does it actually make sense for your object to be passed around as a set of key-value pairs, or as keyword arguments and values? Does accessing it by key- just like a dictionary- really make sense? Would you also expect your object to have other standard mapping methods such as items , values , get ? Do you want to support the in keyword and equality checks ( == )?
If the answer to these questions is yes, it’s probably a good idea to not stop here, and consider the next option instead.
Option 4:
Inheriting your class from ‘collections.abc.Mapping or ‘collections.abc.MutableMapping signals to other users that, for all intents and purposes, your class is a mapping * and can be expected to behave that way. It also provides the methods items , values , get and supports the in keyword and equality checks ( == ) «for free».
You can still cast your object to a dict just as you require, but there would probably be little reason to do so. Because of duck typing, bothering to cast your mapping object to a dict would just be an additional unnecessary step the majority of the time.
As noted in the comments below: it’s worth mentioning that doing this the abc way essentially turns your object class into a dict -like class (assuming you use MutableMapping and not the read-only Mapping base class). Everything you would be able to do with dict , you could do with your own class object. This may be, or may not be, desirable.
Also consider looking at the numerical abcs in the numbers module:
Since you’re also casting your object to an int , it might make more sense to essentially turn your class into a full fledged int so that casting isn’t necessary.
Option 5:
Look into using the dataclasses module (Python 3.7 only), which includes a convenient asdict() utility method.
from dataclasses import dataclass, asdict, field, InitVar @dataclass class Wharrgarbl(object): a: int b: int c: int sum: InitVar[int] # note: InitVar will exclude this from the dict version: InitVar[str] = "old" def __post_init__(self, sum, version): self.sum = 6 # this looks like an OP mistake? self.version = str(version)
Option 6:
NOTE: option 6 is likely NOT what the OP, or other readers based on the title of this question, are looking for. See additional comments below.
class Wharrgarbl(TypedDict): a: str b: str c: str
Using this option, the resulting object is a dict (emphasis: it is not a Wharrgarbl ). There is no reason at all to «cast» it to a dict (unless you are making a copy).
And since the object is a dict , the initialization signature is identical to that of dict and as such it only accepts keyword arguments or another dictionary.
>>> w = Wharrgarbl(a=1,b=2,b=3) >>> w >>> type(w)
Emphasized: the above «class» Wharrgarbl isn’t actually a new class at all. It is simply syntactic sugar for creating typed dict objects with fields of different types for the type checker.
As such this option can be pretty convenient for signaling to readers of your code (and also to a type checker such as mypy) that such a dict object is expected to have specific keys with specific value types.
But this means you cannot, for example, add other methods, although you can try:
class MyDict(TypedDict): def my_fancy_method(self): return "world changing result"
>>> MyDict().my_fancy_method() Traceback (most recent call last): File "", line 1, in AttributeError: 'dict' object has no attribute 'my_fancy_method'
* «Mapping» has become the standard «name» of the dict -like duck type