0

I have the following situation in Python 3:

class A:
    d = {}

class B(A):  pass
class C(A):  pass

I work with the classes only, no instances get created. When I access B.d I will get a shared reference to A.d. That's not what I want. I would like to have each class which inherits A have its own d which is set to a dictionary. d is an implementation detail to A. All access to d is done in the code of A. Just B's d should not be identical to A's d.

With instances, I would create that dictionary in the __init__() function. But in my case I work with the classes only.

Is there a standard way to achieve this (EDIT: without changing the implementation of the subclasses B and C)? Is there something analog to __init__() which gets called for (or in) each derived class at the time of deriving?

4
  • Well, you should be able to define d in the other classes which would overwrite the d variable from the base class for B and C. Or is that not working? Commented Jun 1, 2018 at 8:31
  • 2
    Looks like a design issue. Why are you not using instances of your classes, and all your problems go away? Commented Jun 1, 2018 at 8:54
  • @timgeb Because the "and all problems go away" is not correct. It raises other problems I did not address in my question. Using classes is the "right way" in some cases; let's just assume I have such a case. Commented Jun 1, 2018 at 9:03
  • @meissner_ The existence of d in A is an implementation detail of A. When deriving A I want to avoid having to mention d at all. Commented Jun 1, 2018 at 9:18

2 Answers 2

1

I found a solution myself using a metaclass:

class M(type):
  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.d = {}

class A(metaclass=M): pass

Then one can create subclasses of A which all have their own d attribute:

class B(A): pass

B.d is A.d
False
Sign up to request clarification or add additional context in comments.

Comments

0

Simply testing @meissner_ comment: you can easily replace the d inside your subcalsses by other ones.

class BaseA():
    """Provides basic initialization for dicts used in A() and deriveds."""
    @classmethod
    def InitDefaults(cls):
        return { 1: "this", 2:"are", 3:"some", 4:"defaults"}

class A():
    d = BaseA.InitDefaults()

class B(A):
    d = BaseA.InitDefaults()

class C(A):
    d = BaseA.InitDefaults()


print(A.d)
print(B.d)
print(C.d)

print("\n")
B.d[99] = "Not a default"
C.d[42] = "Not THE answer"

print(A.d)
print(B.d)
print(C.d)

Output:

{1: 'this', 2: 'are', 3: 'some', 4: 'defaults'}
{1: 'this', 2: 'are', 3: 'some', 4: 'defaults'}
{1: 'this', 2: 'are', 3: 'some', 4: 'defaults'}


{1: 'this', 2: 'are', 3: 'some', 4: 'defaults'}
{1: 'this', 2: 'are', 3: 'some', 4: 'defaults', 99: 'Not a default'}
{1: 'this', 2: 'are', 3: 'some', 4: 'defaults', 42: 'Not THE answer'}

This is kinda ugly but would work. You have a central place for code modifying the "base-default" and A,B,C get distinct dicts - initted the same - that can develop into different directions.

Probably better would be to use Instances and init() though ...

6 Comments

Of course I want to avoid copying the creation into each derived class. That this thing d even exists is an implementation detail.
@Alfe If your dicts are partially the same, create a classmethod (in A) that returns a newly build dict with the defaults. Thats one is used by B and C to create it's initial dict once. Code creation placed once, seperate dicts afterwards. If you want diffenrent dicts you need to create them trice for three classes - or copy or w/e. Or use instances.
Sounds promising! :) Could you elaborate in your answer?
As I said, this doesn't solve my issue because it still mentions the implementation detail d in the subclasses. There are lots of inheriting classes which are not under my control (users of my library providing A) which are supposed not to change. So having to mention d in each subclass is no option.
You might be interested in the solution I found using a meta class.
|

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.