0

Running following code:

class NonMeta:
    def __new__(cls):
        x = super().__new__(cls)
        x.attr = 100
        return x


class Meta(type):
    def __new__(mcs, name, bases, dct):
        x = super().__new__(mcs, name, bases, dct)
        x.attr = 100
        return x


class WithMeta(metaclass=Meta):
    pass


print(WithMeta.attr)
print(NonMeta.attr)

results in

/usr/bin/python3.7 /home/lookash/PycharmProjects/PythonLearning/classes.py
100
Traceback (most recent call last):
  File "/home/lookash/PycharmProjects/PythonLearning/classes.py", line 20, in <module>
    print(NonMeta.attr)
AttributeError: type object 'NonMeta' has no attribute 'attr'

Why attr from the WithMeta class is a class variable while it is an instance variable in the NonMeta class?

3 Answers 3

2

Inside NonMeta.__new__, x is an instance of NonMeta, so the assignment to x.attr is creating an instance attribute for the instance x.

Inside Meta.__new__, x is an instance of Meta, namely a new class. In this case, x is the class WithMeta, so the assignment to x.attr creates a class attribute.

The class statement is, in some sense, syntactic sugar for a call to the metatype. That is,

class WithMeta(metaclass=Meta):
    pass

is equivalent to

WithMeta = Meta('WithMeta', (object,), {})
# == Meta.__new__(Meta, 'WithMeta', (object,), {})
Sign up to request clarification or add additional context in comments.

Comments

1

The instances of a metaclass are the classes that declare they will use it (and their subclasses). So when you do x.attr = 100 in Meta.__new__, x is a class (an instance of the metaclass, WithMeta in this case).

That's different from when you run the same line in NonMeta.__new__, where x is an instance of NonMeta, and the class itself doesn't ever get assigned an attr attribute.

For this reason, it might be a good idea to change the code in your __new__ methods to use different and better names than x. In NonMeta, a good name might be self, since that's the traditional name for an instance within a method. In Meta, you might use cls, since the instance is going to be a class.

Comments

1

The equivalent code without a metaclass to what you are doing with the metaclass is simply:

class NonMeta:
    x = 100

The __new__ method defined in classes is called only when an instance of the class is created - it won't run bedore that. And in this code:

class NonMeta:
    def __new__(cls):
        x = super().__new__(cls)
        x.attr = 100
        return x

What returns from super().__new__(cls) and is stored in x is just the instance itself - the same object that is passed with the name self to regular instance methods (including __init__).

The Metaclass, on the other hand, have its __new__ (and __init__) methods run when the class is created. In an ordinary module import, that takes place when a class statement, along with its body, is executed. At the end of the body, the Python runtime have all the information needed to create a new class, and the metaclass' __new__ is called. Whatever it returns becomes the class. And since its return value have the x attribute set, it is already visible on the class.

Comments

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.