Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Primitives class variables of metaclass not shared between subclasses in python, but objects are, why?

consider these two pieces of code:

class M(type):
    hey = []

class S(metaclass=M):
    pass

class N(metaclass=M):
    pass

print(S.hey)  # output []
print(N.hey)  # output []
S.hey.append(6)
print(S.hey)  # output [6]
print(N.hey)  # output [6]

As you can see, changing hey list on S also modifies the hey that N has. So one can assume they are "sharing" this object whose reference is a class variable in their shared metaclass.

Now consider this version:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

class M(type):
    hey = 5

class S(metaclass=M):
    pass

class N(metaclass=M):
    pass

print(S.hey)  # output 5
print(N.hey)  # output 5
S.hey = 6
print(S.hey)  # output 6
print(N.hey)  # output 5

Here we see that modifying hey for S does not affect its value when accessed through H! I have to assume that primitives behave this way.

My question is: This seems weird to me, so is there some kind of documentation on these topics that I can read to learn the "rules"? I skimmed through the official Python documentation but there is very little about the OOP side of the language.
Also, why is python designed this way? What is the design deliberation behind this behavior?

EDIT: also, what do you call S and N in my code examples with respect to M? I inaccurately called them subclasses for M, but what is the correct term for them?

I was expecting consistent behavior between primitives and objects but the behavior changes

>Solution :

You don’t need classes and metaclasses to see this principle.

a = []
b = a
c = a
a.append(3)

If you now print a, b or c, you’ll see [3]. That snippet contains exactly ONE list. It just has three different names. But if I do:

b = 77

That binds a new integer object to the name b. a and c are still bound to that original list, but b now has a different lifetime.

This is also the cause of a very common error:

a, b, c = [[]] * 3

It is usually a surprise to people to learn that this snippet ALSO contains exactly one list. To initialize three different lists, you need something like:

a, b, c = ([] for _ in range(3))
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading