Home > Software design >  How to preserve the value of class properties
How to preserve the value of class properties

Time:08-28

class A:
    p = 1
    def __init__(self, p=None, **kwargs):
        self.p = p

class B(A):
    p = 2

a = A()
print(a.p)
b = B()
print(b.p)

In the examples above, I would like 1 and 2 be printed. However, it prints None for both. What is the solution to preserve the default value of classes?

One solution I can think of is:

class A:
    p = 1
    def __init__(self, p=None, **kwargs):
        if p: self.p = p
        if q: self.q = q
        ...

and if I have many attributes I should do that for all of them? I would like to know what is the standard solution, and whether my solution is correct or not? One problem is that the user can't pass None to the class init.

Another solution could be like:

class A:
    p = 1
    def __init__(self, p=1, **kwargs):
        self.p = p
        self.q = q
        ...

However this time if one instantiate b like:

b = B()

the value of b.p would be also 1, while I expect it keep 2.

I use overriding classes attributes much, but I just don't know how to preserve them from be overwritten by default values of the same or parent class.

CodePudding user response:

UPDATE:

If you really absolutely need both your class and your instances to have this attribute, and also want to use the class attribute as the default for an instance, I would say the correct way is like this:

_sentinel = object()


class A:
    p = 1

    def __init__(self, p=_sentinel):
        if p is not _sentinel:
            self.p = p


class B(A):
    p = 2


a = A()
print(a.p)  # prints 1
b = B()
print(b.p)  # prints 2
b2 = B(p=None)
print(b2.p)  # prints None

The sentinel object is for when you do want to be able to pass None to the constructor for whatever reason. Since we compare identity in the __init__ method, it is (practically) guaranteed that if any value is passed, it will be assigned to the instance attribute, even if that value is None.

Original answer:

The problem seems to stem from a misunderstanding of how (class-)attribute work in Python.

When you do this:

class A:
    p = 1

You define a class attribute. Instances of that class will automatically have that same attribute upon initialization, unless you overwrite it, which is exactly what you do here:

    def __init__(self, p=None, **kwargs):
        self.p = p

This overwrites the instance's attribute .p with the value p it receives in the __init__ method. In this case, since you defined a default value None and called the constructor without passing an argument, that is what was assigned to the instance's attribute.

If you want, you can simply omit the self.p assignment in the constructor. Then your instances will have the class' default upon initialization.

EDIT:

Depending on how you want to handle it, you can simply assign the value after initialization. But I doubt that is what you want. You probably don't need class attributes at all. Instead you may just want to define the default values in your __init__ method signature and assign them there.

If you really need that class attribute as well, you can do what you did, but more precisely by testing for if p is not None:.

CodePudding user response:

I would set the default value of the p argument to the value that you want:

class A:
    def __init__(self, p=1, **kwargs):
        self.p = p

class B(A):
    def __init__(self, p=2, **kwargs):
        super().__init__(p, **kwargs)

a = A()
print(a.p)
b = B()
print(b.p)

Then from the constructor of B you can call the one from A by using super().__init__

CodePudding user response:

You can use class properties from the class:

class A:
    p = 1

class B(A):
    p = 2

a = A()
print(a.p)
b = B()
print(b.p)

prints 1 and 2, like you wanted.

It is clearer to access them from the class directly, though:

print(A.p)
print(B.p)

You can set the instance one, without changing what is associated in the class.

class B(A):
    def change(self, x):
        self.p = x

b.change(3)
print(B.p) #2
print(b.p) #3
  • Related