-2

How would you write these 2 classes as dictionaries?

class Base:
   x = 10

class Derived(Base):
   y = 20

For the first class the equivalent dictionary is:

Base = {"x": 10}

What about the second one?

Guy
  • 46,488
  • 10
  • 44
  • 88
Tudor.Mano
  • 33
  • 5
  • 2
    In the dictionary `Base` is just a name, it's not connected to the class. Please explain better what you are trying to do. – Guy Jan 26 '20 at 10:21
  • if you like that kind of analogy, then it could be something like `Derived = Base.update({'y': 20})` – Charnel Jan 26 '20 at 10:23
  • If you're looking for a dictionary-like base class you should check out this: https://docs.python.org/3/library/collections.abc.html#collections.abc.Collection – gstukelj Jan 26 '20 at 10:25
  • Dictionaries are not equivalent to classes. There is no direct equivalent to inheritance. Of course, you could re-implement such a thing, but why would you want to? – juanpa.arrivillaga Jan 26 '20 at 10:34
  • There's not really an "equivalent" in a straightforward sense. It's likely very possible to implement whatever behaviour you are after, but it's not clear exactly what that is because your question rests on a false assumption that classes and data objects like dicts are commonly convoluted in this way. If you can describe in greater detail what behavior you expect from "dictionary inheritence", that would help in answering – Hymns For Disco Jan 26 '20 at 10:51
  • Example: class A: def __init__(self): self.x = 10 a = A() print(a.x) can be written as: def __init__(obj): obj["x"] = 10 A = {"__init__": __init__} a = dict(A) a["__init__"](a) print(a["x"]) Classes in python are just dictionaries. I've written the above class as a dictionary that behaves in the same way. I want to do the same for my inheritance example, but I don't know how. I hope it's clear now. – Tudor.Mano Jan 26 '20 at 11:02

1 Answers1

1

If by "dictionary equivalent" you mean that classes and instances attributes have to be stored in a mapping, you are right. If you think that Python Data Model is simple as a dictionary, you are wrong.

There is a special attribute __dict__ for the mapping:

object.__dict__

A dictionary or other mapping object used to store an object’s (writable) attributes.

But you have to differentiate between class attributes and instance attributes:

>>> class A:
...    class_attr = 5
...    def __init__(self): self.instance_attr = 10
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', 'class_attr': 5, '__init__': <function A.__init__ at ...>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
>>> a = A()
>>> a.__dict__
{'instance_attr': 10}

As you see, class_attr is in the mapping of the class A, whereas instance_attr is in the mapping of the instance a.

You can go up from the instance to its class:

>>> a.__class__.__dict__
mappingproxy({'__module__': '__main__', 'class_attr': 5, '__init__': <function A.__init__ at ...>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})

But Python can resolve class attributes calls from instance, even if the attribute is not in the mapping of the instance:

>>> a.class_attr
5

This is black magic!

Let's try your example:

>>> class Base:
...     x = 10
...
>>> class Derived(Base):
...     y = 20
...
>>> Base.__dict__
mappingproxy({'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'Base' objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})
>>> Derived.__dict__
mappingproxy({'__module__': '__main__', 'y': 20, '__doc__': None})

As you can see, the dicts are orthogonal because x and y are class instances. Thus, x is not in the mapping of Derived. Let's answer the question you asked: the Derived equivalent dictionary is:

Derived: {"y": 10}

But:

>>> Derived.x
10

More black magic!

Let's try with instance attributes:

>>> class Base:
...     def __init__(self):
...         self.x = 10
...
>>> class Derived(Base):
...     def __init__(self):
...         self.y = 20
...
>>> b = Base()
>>> b.__dict__
{'x': 10}
>>> d = Derived()
>>> d.__dict__
{'y': 20}

But this time, it won't work as above:

>>> d.x
Traceback (most recent call last):
...
AttributeError: 'Derived' object has no attribute 'x'

You need to fix the Derived.__init__ method with an explicit call to its parent initializer:

>>> class Derived(Base):
...     def __init__(self):
...         super(Derived, self).__init__()
...         self.y = 20
...

You get:

>>> d = Derived()
>>> d.__dict__
{'x': 10, 'y': 20}
>>> d.x
10

I hope you are convinced that instances, classes and dictionaries are quite different, although they share the concept of mapping. The main differences (that is the "black magic") are explained in the Python Data Model documentation and you can find some useful questions/answers on SO: What is getattr() exactly and how do I use it? for instance.


Bonus: you wrote in a comment that

class A:
    def __init__(self):
        self.x = 10

a = A()
print(a.x)

can be written as:

def init(obj):
    obj["x"] = 10

A = {"init": init}
a = dict(A)
a["init"](a)
print(a["x"])

But that's a confusion between class and instance attribute, because init is not an attribute of the instance a, but of the class A. You should have written:

A = {"init": init}
a = {}
A["init"](a)
print(a["x"])

Note that the line a = {} has no visible equivalent in the A.__init__ method. When you write self.x = 10, self is already initialized. This is the job of the A.__new__ method:

>>> class A:
...     def __new__(cls):
...         o = super().__new__(cls) # this is the equivalent of a = {}
...         cls.last_created = o # store the object for test below
...         return o
...
...     def __init__(self):
...         self.x = 10
...
>>> A.last_created
Traceback (most recent call last):
...
AttributeError: type object 'A' has no attribute 'last_created'
>>> a = A()
>>> a == A.last_created
True
Community
  • 1
  • 1
jferard
  • 7,835
  • 2
  • 22
  • 35