Some questions about Python3 metaclass

94 Views Asked by At
class UpperAttrMetaclass(type):

    var = "test"

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):
        print("hello world")
        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)


class Hello(object):

    __metaclass__ = UpperAttrMetaclass

    bar = "test"

obj = Hello()
print(obj.BAR)  # obj has no attribute named BAR

Traceback (most recent call last):
File "E:\python\test.py", line 32, in
print(obj.BAR)
AttributeError: 'Hello' object has no attribute 'BAR'

Why metaclass UpperAttrMetaclass does not work?

1

There are 1 best solutions below

0
On

In Python3 the way to specify a metaclass has changed from Python2 in an incompatible way.

Since Python 3.0, the way to specify a metaclass is to use the metaclass name as if it were a Named parameter on the class statement itself.

Thus, in the above example, you shuld declare your Hello class as:

class Hello(metaclass=UpperAttrMetaclass):
    bar = "test"

Check the documentation at: https://docs.python.org/3.0/whatsnew/3.0.html#changed-syntax

Besides that, as you've noted, putting a __metaclass__ attribute in a c alss body is not an error, but it does nothing at all, but declaring an attribute with that name.

After a couple releases of Python3.x versions, this is the only syntactic change that is incompatible with Python 2 and can't be work-around in a straightforward way so that the code is both Python 2.x and Python 3.x compatible at the same time.

If you need the same code base to run on Python 2 and Python 3, the package named six brings the call with_metaclass which builds a dynamic class base with a syntax that is compatible with both versions. (https://pythonhosted.org/six/#syntax-compatibility)