__class__
is set by type
to the first parameter of type.__new__
, which is why in class metaclasses we call type.__new__(cls, name, bases, dict)
(or super(Metaclass, cls).__new__(cls, ...)
). However, we can't do that if the metaclass is a function:
>>> def __metaclass__(name, base, dict):
>>>
print('inside __metaclass__(%r, %r, %r)' % (name, base, dict)) >>>
return type.__new__(__metaclass__, name, base, dict) >>>
class Foo(object):
>>>
__metaclass__ = __metaclass__
TypeError: Error when calling the metaclass bases
type.__new__(X): X is not a type object(function)
Similarly, if we try to set Foo.__class__
to your __metaclass__
it fails, as the __class__
attribute must be a class:
>>> Foo.__class__ = Foo.__metaclass__.__func__
TypeError: __class__ must be set to new - style class, not 'function'
object
This is because Python can only have one metaclass for a class. Here, class C can’t inherit from two metaclasses, which results in ambiguity. In most cases, we don’t need to go for a metaclass, normal code will fit with the class and object. The pointless use of metaclasses increases the complexity of coding. , If you use a function as the metaclass then subclasses won't inherit your function metaclass, but the type of whatever that function returned. One advantage compared to class decorators is the fact that subclasses inherit the metaclass. ,__class__ is set by type to the first parameter of type.__new__, which is why in class metaclasses we call type.__new__(cls, name, bases, dict) (or super(Metaclass, cls).__new__(cls, ...)). However, we can't do that if the metaclass is a function:,The Python docs say that the metaclass of a class can be any callable. All the examples I see use a class. Why not use a function? It's callable, and fairly simple to define. But it isn't working, and I don't understand why.
class Foo(object): def __metaclass__(name, base, dict): print('inside __metaclass__(%r, ...)' % name) return type(name, base, dict) print(Foo.__metaclass__) class Bar(Foo): pass print(Bar.__metaclass__)
>>> def __metaclass__(name, base, dict): >>> print('inside __metaclass__(%r, %r, %r)' % (name, base, dict)) >>>
return type.__new__(__metaclass__, name, base, dict) >>> class Foo(object): >>> __metaclass__ = __metaclass__ TypeError: Error when calling the metaclass bases type.__new__(X): X is not a type object(function)
class Foo(object): def __metaclass__(name, base, dict): print('inside __metaclass__(%r, ...)' % name) return type(name, base, dict) print(Foo.__metaclass__) class Bar(Foo): pass print(Bar.__metaclass__)
inside __metaclass__('Foo', ...) <unbound method Foo.__metaclass__>
<unbound method Bar.__metaclass__>
>>> def __metaclass__(name, base, dict): >>> print('inside __metaclass__(%r, %r, %r)' % (name, base, dict)) >>>
return type.__new__(__metaclass__, name, base, dict) >>> class Foo(object): >>> __metaclass__ = __metaclass__ TypeError: Error when calling the metaclass basestype.__new__(X): X is not a type object(function)
>>> Foo.__class__ = Foo.__metaclass__.__func__ TypeError: __class__ must be set to new - style class, not 'function'
object
The get method won't be called when the property is accessed as a class attribute (C.x) instead of as an instance attribute (C().x). If you want to override the __get__ operation for properties when used as a class attribute, you can subclass property - it is a new-style type itself - to extend its __get__ method, or you can define a descriptor type from scratch by creating a new-style class that defines __get__, __set__ and __delete__ methods. ,There's a new way of overriding attribute access. The __getattr__ hook, if defined, works the same way as it does for classic classes: it is only called if the regular way of searching for the attribute doesn't find it. But you can now also override __getattribute__, a new operation that is called for all attribute references. ,Here's an example of overriding __getattribute__ (really extending it, since the overriding method calls the base class method): ,dir() on an instance (classic or new-style) shows the instance variables as well as the methods and class attributes defined by the instance's class and all its base classes.
class A:
def foo(self): pass
class B(A): pass
class C(A):
def foo(self):
B.foo(self)
Currently, customising class creation requires the use of a custom metaclass. This custom metaclass then persists for the entire lifecycle of the class, creating the potential for spurious metaclass conflicts.,This becomes most evident if the class in question is designed as a mixin: it is very unlikely that the code of the mixin is to be executed for the mixin class itself, as it is not supposed to be a complete class on its own.,Metaclasses are a powerful tool to customize class creation. They have, however, the problem that there is no automatic way to combine metaclasses. If one wants to use two metaclasses for a class, a new metaclass combining those two needs to be created, typically manually.,One of the big issues that makes library authors reluctant to use metaclasses (even when they would be appropriate) is the risk of metaclass conflicts. These occur whenever two unrelated metaclasses are used by the desired parents of a class definition. This risk also makes it very difficult to add a metaclass to a class that has previously been published without one.
As an example, the first use case looks as follows:
>>> class QuestBase:
...# this is implicitly a @classmethod(see below
for motivation)
...def __init_subclass__(cls, swallow, ** kwargs):
...cls.swallow = swallow
...super().__init_subclass__( ** kwargs)
>>>
class Quest(QuestBase, swallow = "african"):
...pass
>>>
Quest.swallow 'african'
To give an example of its usage, imagine a descriptor representing weak referenced values:
import weakref
class WeakAttribute:
def __get__(self, instance, owner):
return instance.__dict__[self.name]()
def __set__(self, instance, value):
instance.__dict__[self.name] = weakref.ref(value)
# this is the new initializer:
def __set_name__(self, owner, name):
self.name = name
Such a WeakAttribute may, for example, be used in a tree structure where one wants to avoid cyclic references via the parent:
class TreeNode:
parent = WeakAttribute()
def __init__(self, parent):
self.parent = parent
Especially when writing a plugin system, one likes to register new subclasses of a plugin baseclass. This can be done as follows:
class PluginBase:
subclasses = []
def __init_subclass__(cls, ** kwargs):
super().__init_subclass__( ** kwargs)
cls.subclasses.append(cls)
There are many designs of Python descriptors in the wild which, for example, check boundaries of values. Often those "traits" need some support of a metaclass to work. This is how this would look like with this PEP:
class Trait:
def __init__(self, minimum, maximum):
self.minimum = minimum
self.maximum = maximum
def __get__(self, instance, owner):
return instance.__dict__[self.key]
def __set__(self, instance, value):
if self.minimum < value < self.maximum:
instance.__dict__[self.key] = value
else:
raise ValueError("value not in range")
def __set_name__(self, owner, name):
self.key = name
For readers who prefer reading Python over English, this PEP proposes to replace the current type and object with the following:
class NewType(type):
def __new__(cls, * args, ** kwargs):
if len(args) != 3:
return super().__new__(cls, * args)
name, bases, ns = args
init = ns.get('__init_subclass__')
if isinstance(init, types.FunctionType):
ns['__init_subclass__'] = classmethod(init)
self = super().__new__(cls, name, bases, ns)
for k, v in self.__dict__.items():
func = getattr(v, '__set_name__', None)
if func is not None:
func(self, k)
super(self, self).__init_subclass__( ** kwargs)
return self
def __init__(self, name, bases, ns, ** kwargs):
super().__init__(name, bases, ns)
class NewObject(object):
@classmethod
def __init_subclass__(cls):
pass
The following class definitions (except the one defining the metaclass) continue to fail with a TypeError as superfluous class arguments are passed:
class MyMeta(type):
pass
class MyClass(metaclass = MyMeta, otherarg = 1):
pass
MyMeta("MyClass", (), otherargs = 1)
import types
types.new_class("MyClass", (), dict(metaclass = MyMeta, otherarg = 1))
types.prepare_class("MyClass", (), dict(metaclass = MyMeta, otherarg = 1))
A metaclass defining only a __new__ method which is interested in keyword arguments now does not need to define an __init__ method anymore, as the default type.__init__ ignores keyword arguments. This is nicely in line with the recommendation to override __new__ in metaclasses instead of __init__. The following code does not fail anymore:
class MyMeta(type):
def __new__(cls, name, bases, namespace, otherarg):
return super().__new__(cls, name, bases, namespace)
class MyClass(metaclass = MyMeta, otherarg = 1):
pass
Only defining an __init__ method in a metaclass continues to fail with TypeError if keyword arguments are given:
class MyMeta(type):
def __init__(self, name, bases, namespace, otherarg):
super().__init__(name, bases, namespace)
class MyClass(metaclass = MyMeta, otherarg = 1):
pass
Last Updated : 11 Oct, 2021,GATE CS 2021 Syllabus
Output:
Type of num is: <class 'int'>
Type of lst is: <class 'list'>
Type of name is: <class 'str'>
09 February 2015 (updated 19 November 2020)
>>> class Foobar:
... pass
...
>>> type(Foobar)
<class 'type'>
>>> foo = Foobar()
>>> type(foo)
<class '__main__.Foobar'>
>>> isinstance(foo, Foobar) True >>> isinstance(Foobar, type) True
>>> MyClass = type('MyClass', (), {})
>>> MyClass
<class '__main__.MyClass'>
>>> class Meta(type):
...pass
>>> class Complex(metaclass=Meta):
... pass
>>> type(Complex)
<class '__main__.Meta'>
>>> class Funky:
...def __call__(self):
...print("Look at me, I work like a function!") >>>
f = Funky() >>>
f()
Look at me, I work like a
function !