1

I have a metaclass like this

class UpperMeta(type):
    def __new__(cls, clsname, bases, dct):
        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperMeta, cls).__new__(cls, clsname, bases, uppercase_attr)

    def echo(cls):
        return 'echo'

class B(object):
    __metaclass__ = UpperMeta

assert hasattr(B, 'echo')
assert B.echo() == 'echo'
assert not issubclass(B, UpperMeta)

My question is:

  1. Why does class B have an echo-method? If B is not a subclass of UpperMeta, it should not have the echo attr?
  2. Which attributes does a class get from a metaclass?
serv-inc
  • 35,772
  • 9
  • 166
  • 188
whbb
  • 520
  • 5
  • 13

2 Answers2

1

The class object B is of type UpperMeta.

This has the consequence that all classmethods of UpperMeta are available on the class B. The attribute is not on the class B, but gets proxied from B's class(B is the class, not an instance of B)

>>> print dir(B)
# General lack of echo()
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

It's here:

>>> print dir(B.__class__)
['__abstractmethods__', '__base__',  ..., 'echo', 'mro']

From the documentation:

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with a.dict['x'], then type(a).dict['x'], and continuing through the base classes of type(a) excluding metaclasses.


This is a little confusing towards the end "...excluding metaclasses..", which really means the metaclasses of type(a), therefore saying that if your metaclass UpperMeta has a metaclass TopMeta and TopMeta defines sos(), that one won't be looked up:

class TopMeta(type):
    def __new__(cls, clsname, bases, dct):
        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(TopMeta, cls).__new__(cls, clsname, bases, uppercase_attr)

    def sos(cls):
        return 'sos'

class UpperMeta(type):
    __metaclass__ = TopMeta
    def __new__(cls, clsname, bases, dct):
        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperMeta, cls).__new__(cls, clsname, bases, uppercase_attr)

class B(object):
    __metaclass__ = UpperMeta

assert not hasattr(B, 'sos')

The only talk that ever explained metaclasses properly: David Beazley - Python 3 Metaprogramming. You only have the first 80 minutes or so.

Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
1

why class B have method of echo, B is not subclass of UpperMeta, it should not have echo attr?

If you look at What is a metaclass in Python? or Customizing class creation, you see that (quotes from python docs)

if __metaclass__ is defined then the callable assigned to it will be called instead of type().

type()

is essentially a dynamic form of the class statement. The name string is the class name and becomes the __name__ attribute; the bases tuple itemizes the base classes and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body and becomes the __dict__ attribute. For example, the following two statements create identical type objects:

>>> class X(object):
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))

This is, it is "just like" class extension. This also answers

What attribute class get from metaclass?

Pretty much everything.

Please also be aware that

If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

yet in the video posted by @Sebastian, he says

Q: Can you have too much of [metaprogramming] A: No

So he thinks it important enough to learn.

Community
  • 1
  • 1
serv-inc
  • 35,772
  • 9
  • 166
  • 188