2

I am trying to calculate a radius-like quantity from 3 lists containing Cartesian coordinates x, y & z. Below is my minimal code example to reproduce the issue I am facing; The child-class calculates the radius-quantity but returns a zero-value. What is the reason for this, and how can it be fixed?

Script:

# -*- coding: utf-8 -*-

from dataclasses import dataclass, field
from typing import List


@dataclass
class LoadHalo:
    x: List = field(default_factory=list)
    y: List = field(default_factory=list)
    z: List = field(default_factory=list)

    def __post_init__(self):
        self.x = [1, 2, 3]
        self.y = [1, 3, 5]
        self.z = [1, 4, 7]


@dataclass
class BinHalo(LoadHalo):
    r: List = field(default_factory=list)

    def __post_init__(self):
        self.r = self.modulus(self.x, self.y, self.z)

    def modulus(self, *args):
        """Modulus of vector of arbitrary size."""
        return sum([i ** 2 for containers in args for i in containers]) ** .5


halo = BinHalo()
print(f"halo.x: {halo.x}")
print(f"halo.r: {halo.r}")

Which outputs the following values for x and r:

halo.x: []
halo.r: 0.0
1
  • 1
    add super().__post_init__() before self.r = self.modulus(self.x, self.y, self.z) Commented Dec 3, 2019 at 9:52

1 Answer 1

4

Because you are overriding __post_init__, so the lists are still empty because they never got initialized with anything other than the empty list default. You must call the super-class method that you are overriding if you want its behavior as well.

What you want is the following:

def __post_init__(self):
    super().__post_init__()
    self.r = self.modulus(self.x, self.y, self.z)

Note a couple of things with your type-hints: you probably wanted float for the r field in the subclass, and also for the return type of modulus:

def modulus(self, *args) -> float:
    ...

also, you should initialize the default lists with float objects so you can write:

x: List[float]

For your list fields, since presumably you want to use floating point math.

So all-in all, I'd define everything like:

from dataclasses import dataclass, field
from typing import List


@dataclass
class LoadHalo:
    x: List[float] = field(default_factory=list)
    y: List[float] = field(default_factory=list)
    z: List[float] = field(default_factory=list)

    def __post_init__(self) -> None:
        self.x = [1.0, 2.0, 3.0]
        self.y = [1.0, 3.0, 5.0]
        self.z = [1.0, 4.0, 7.0]


@dataclass
class BinHalo(LoadHalo):
    r: float = 0.0 # or whatever is suitable

    def __post_init__(self) -> None:
        super().__post_init__()
        self.r = self.modulus(self.x, self.y, self.z)

    def modulus(self, *args: List[float]) -> float:
        """Modulus of vector of arbitrary size."""
        return sum([i ** 2 for containers in args for i in containers]) ** .5
Sign up to request clarification or add additional context in comments.

2 Comments

Very clear working example. Just what I was looking for. Thank you @juanpa.arrivillaga
@GustavRasmussen no worries, you should consider using mypy to type-check your code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.