Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Using defaultdict to make dictionary of custom objects

I have created the following class. Package, website and comments are all strings and distroDict is a (string, list) dictionary.

class TableEntry(object):

    def __init__(self, package, website, distroDict, comments):
        self.package = package
        self.website = website
        self.distroDict = distroDict
        self.comments = comments

I want to use defaultdict(TableEntry) to make a (string, TableEntry) custom dictionary.

tableDict = defaultdict(TableEntry)
entry = TableEntry(package, website, distroDict, comments)
tableDict[package].append(entry)

I want the package to be the key and the entry object to be the value. I can use values within the entry object but if I try to append it to tableDict I receive the following error.

Traceback (most recent call last):
  File "wiki.py", line 151, in <module>
    printMetaData(lines, f)
  File "wiki.py", line 73, in printMetaData
    tableDict[package].append(entry)
TypeError: __init__() takes exactly 5 arguments (1 given)

I have also tried the following:

tableDict[package].append(TableEntry(package, website, distroDict, comments))

And receive the same error essentially:

Traceback (most recent call last):
  File "wiki.py", line 150, in <module>
    printMetaData(lines, f)
  File "wiki.py", line 73, in printMetaData
    tableDict[package].append(TableEntry(package, website, distroDict, comments))
TypeError: __init__() takes exactly 5 arguments (1 given)
like image 506
javakid1993 Avatar asked Dec 17 '15 03:12

javakid1993


2 Answers

When you try to get something from a defaultdict it returns an instance of the default_factory you instantiated it with, which in your case is TableEntry that has no method append. But that's actually not why you are getting the error. This happens because when an instance is returned it is also evaluated, and since TableEntry doesn't have the arguments it needs, the Python interpreter will complain. But even if TableEntry was given arguments in the defaultdict call, you would probably get a TypeError because defaultdict needs to be called with a callable (and not an instance of a class, as it would be).

In summary, you cant instantiate a defaultdict with an non-callable unless you use subclassing. Instead you should do something like this:

class TableEntry(object):
    def add(self, package, website, distroDict, comments):
        self.package = package
        self.website = website
        self.distroDict = distroDict
        self.comments = comments

tableDict = defaultdict(TableEntry)
entry = ("package", "website", "distroDict", "comments")
tableDict["package"].add(*entry)

Then you can do e.g. tableDict["package"]["website"] to get the string you wrote to self.website.

like image 196
Ulf Aslak Avatar answered Oct 19 '22 20:10

Ulf Aslak


Another approach is to make the arguments optional for the __init__ method, and assign the entry object to tableDict directly.

class TableEntry:
    def __init__(self, package=None, website=None, distroDict=None, comments=None):
        self.package = package
        self.website = website
        self.distroDict = distroDict
        self.comments = comments

tableDict = defaultdict(TableEntry)
tableDict["package"] = TableEntry("package", "website", "distroDict", "comments")
like image 1
Peter Wu Avatar answered Oct 19 '22 20:10

Peter Wu