0

I have 2 classes:

class A:
  name = 'test'
  def __init__(self):
    pass
  @staticmethod
  def from_json(json: dict) -> object:
    obj = A()
    obj.name = json["name"]
    return obj

class B(A):
  description = "desc"
  def __init__(self):
    super().__init__(self) # I was originally doing: A.__init__(self) but online said to use super.
  @staticnmethod
  def from_json(json: dict) -> object:
    obj = A.from_json(json) # As seen above, A.from_json, this returns an instance of A.
    obj.description = json["description"]
    return obj

I know there isnt really any casting, but I want the returned class to be of type B, so it gains all the other new properties / methods.

How to i have B::from_json return type B? I was thinking there was a way to create something like:

b = B()

and then through some python magic pass all properties from A into B and then return b, but i wasnt sure if that is the right solution.

Here is now a functional test of the flaw:

x = A.from_json({'name': 'foo'})
z = B.from_json({ 'name': 'thor', 'description': 'god of thunder'})
type(x) == A  # <class '__main__.A'>
type(z) == B  # <class '__main__.A'>
Fallenreaper
  • 10,222
  • 12
  • 66
  • 129

1 Answers1

2

You should use classmethod here, not staticmethod. Then you can remove all the hardcoded classes references

class A:
    name = 'test'

    def __init__(self):
        pass

    @classmethod
    def from_json(cls, json: dict) -> object:
        obj = cls()
        obj.name = json["name"]
        return obj


class B(A):
    description = "desc"

    def __init__(self):
        super().__init__()

    @classmethod
    def from_json(cls, json: dict) -> object:
        obj = super().from_json(json)
        obj.description = json["description"]
        return obj


print(type(B.from_json({'name': 'name', 'description': 'description'})))

Outputs

<class '__main__.B'>

And your tests:

x = A.from_json({'name': 'foo'})
z = B.from_json({ 'name': 'thor', 'description': 'god of thunder'})
print(type(x) == A)
print(type(z) == B)

Outputs

True
True



Using classmethod is actually the recommended way in the official Python docs to create alternative "constructors" (which is what from_json essentially is). Otherwise, you don't have any access to the correct class (as you found out).

This works because (quoted from the docs):

If a class method is called for a derived class, the derived class object is passed as the implied first argument.

DeepSpace
  • 78,697
  • 11
  • 109
  • 154
  • Does this make sense though? I always thought that `from_json` would be static. If it is a class method, it needs self (you call it cls, and its not used). I thought the runtime would not like calling functions without instantiation? does `@classmethod` override this or something? – Fallenreaper Sep 19 '19 at 17:58
  • I was looking up another overflow question to help understand the differences between static and class methods: https://stackoverflow.com/questions/12179271/meaning-of-classmethod-and-staticmethod-for-beginner – Fallenreaper Sep 19 '19 at 18:02
  • 1
    Yes, it makes a lot of sense. This is actually how this should be done, I updated the answer with some resources – DeepSpace Sep 19 '19 at 18:03
  • Thanks! This has been a great help. It looks like Classmethod essentially injects the class reference into the first property incase it is needed to be used. I like it a lot actually. I just needed to understand the purpose. It looks like i should update all my static method from_json to do it this way. – Fallenreaper Sep 19 '19 at 18:04