diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1492,16 +1492,18 @@ class' :attr:`~object.__dict__`. Called to delete the attribute on an instance *instance* of the owner class. .. method:: object.__set_name__(self, owner, name) Called at the time the owning class *owner* is created. The descriptor has been assigned to *name*. + .. versionadded:: 3.6 + The attribute :attr:`__objclass__` is interpreted by the :mod:`inspect` module as specifying the class where this object was defined (setting this appropriately can assist in runtime introspection of dynamic class attributes). For callables, it may indicate that an instance of the given type (or a subclass) is expected or required as the first positional argument (for example, CPython sets this attribute for unbound methods that are implemented in C). @@ -1643,16 +1645,17 @@ Customizing class creation Whenever a class inherits from another class, *__init_subclass__* is called on that class. This way, it is possible to write classes which change the behavior of subclasses. This is closely related to class decorators, but where class decorators only affect the specific class they're applied to, ``__init_subclass__`` solely applies to future subclasses of the class defining the method. .. classmethod:: object.__init_subclass__(cls) + This method is called whenever the containing class is subclassed. *cls* is then the new subclass. If defined as a normal instance method, this method is implicitly converted to a class method. Keyword arguments which are given to a new class are passed to the parent's class ``__init_subclass__``. For compatibility with other classes using ``__init_subclass__``, one should take out the needed keyword arguments and pass the others over to the base @@ -1664,16 +1667,18 @@ class defining the method. cls.default_name = default_name class AustralianPhilosopher(Philosopher, default_name="Bruce"): pass The default implementation ``object.__init_subclass__`` does nothing, but raises an error if it is called with any arguments. + .. versionadded:: 3.6 + .. _metaclasses: Metaclasses ^^^^^^^^^^^ By default, classes are constructed using :func:`type`. The class body is executed in a new namespace and the class name is bound locally to the diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py --- a/Lib/test/test_subclassinit.py +++ b/Lib/test/test_subclassinit.py @@ -1,95 +1,95 @@ -from unittest import TestCase, main import sys import types +import unittest -class Test(TestCase): +class Test(unittest.TestCase): def test_init_subclass(self): - class A(object): + class A: initialized = False def __init_subclass__(cls): super().__init_subclass__() cls.initialized = True class B(A): pass self.assertFalse(A.initialized) self.assertTrue(B.initialized) def test_init_subclass_dict(self): - class A(dict, object): + class A(dict): initialized = False def __init_subclass__(cls): super().__init_subclass__() cls.initialized = True class B(A): pass self.assertFalse(A.initialized) self.assertTrue(B.initialized) def test_init_subclass_kwargs(self): - class A(object): + class A: def __init_subclass__(cls, **kwargs): cls.kwargs = kwargs class B(A, x=3): pass self.assertEqual(B.kwargs, dict(x=3)) def test_init_subclass_error(self): - class A(object): + class A: def __init_subclass__(cls): raise RuntimeError with self.assertRaises(RuntimeError): class B(A): pass def test_init_subclass_wrong(self): - class A(object): + class A: def __init_subclass__(cls, whatever): pass with self.assertRaises(TypeError): class B(A): pass def test_init_subclass_skipped(self): - class BaseWithInit(object): + class BaseWithInit: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.initialized = cls class BaseWithoutInit(BaseWithInit): pass class A(BaseWithoutInit): pass self.assertIs(A.initialized, A) self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit) def test_init_subclass_diamond(self): - class Base(object): + class Base: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.calls = [] class Left(Base): pass - class Middle(object): + class Middle: def __init_subclass__(cls, middle, **kwargs): super().__init_subclass__(**kwargs) cls.calls += [middle] class Right(Base): def __init_subclass__(cls, right="right", **kwargs): super().__init_subclass__(**kwargs) cls.calls += [right] @@ -102,71 +102,71 @@ class Test(TestCase): self.assertEqual(Right.calls, []) def test_set_name(self): class Descriptor: def __set_name__(self, owner, name): self.owner = owner self.name = name - class A(object): + class A: d = Descriptor() self.assertEqual(A.d.name, "d") self.assertIs(A.d.owner, A) def test_set_name_metaclass(self): class Meta(type): def __new__(cls, name, bases, ns): ret = super().__new__(cls, name, bases, ns) self.assertEqual(ret.d.name, "d") self.assertIs(ret.d.owner, ret) return 0 - class Descriptor(object): + class Descriptor: def __set_name__(self, owner, name): self.owner = owner self.name = name - class A(object, metaclass=Meta): + class A(metaclass=Meta): d = Descriptor() self.assertEqual(A, 0) def test_set_name_error(self): class Descriptor: def __set_name__(self, owner, name): raise RuntimeError with self.assertRaises(RuntimeError): - class A(object): + class A: d = Descriptor() def test_set_name_wrong(self): class Descriptor: def __set_name__(self): pass with self.assertRaises(TypeError): - class A(object): + class A: d = Descriptor() def test_set_name_init_subclass(self): class Descriptor: def __set_name__(self, owner, name): self.owner = owner self.name = name class Meta(type): def __new__(cls, name, bases, ns): self = super().__new__(cls, name, bases, ns) self.meta_owner = self.owner self.meta_name = self.name return self - class A(object): + class A: def __init_subclass__(cls): cls.owner = cls.d.owner cls.name = cls.d.name class B(A, metaclass=Meta): d = Descriptor() self.assertIs(B.owner, B) @@ -174,71 +174,71 @@ class Test(TestCase): self.assertIs(B.meta_owner, B) self.assertEqual(B.name, 'd') def test_errors(self): class MyMeta(type): pass with self.assertRaises(TypeError): - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass with self.assertRaises(TypeError): types.new_class("MyClass", (object,), dict(metaclass=MyMeta, otherarg=1)) types.prepare_class("MyClass", (object,), dict(metaclass=MyMeta, otherarg=1)) class MyMeta(type): def __init__(self, name, bases, namespace, otherarg): super().__init__(name, bases, namespace) with self.assertRaises(TypeError): - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass class MyMeta(type): def __new__(cls, name, bases, namespace, otherarg): return super().__new__(cls, name, bases, namespace) def __init__(self, name, bases, namespace, otherarg): super().__init__(name, bases, namespace) self.otherarg = otherarg - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass self.assertEqual(MyClass.otherarg, 1) def test_errors_changed_pep487(self): # These tests failed before Python 3.6, PEP 487 class MyMeta(type): def __new__(cls, name, bases, namespace): return super().__new__(cls, name=name, bases=bases, dict=namespace) with self.assertRaises(TypeError): - class MyClass(object, metaclass=MyMeta): + class MyClass(metaclass=MyMeta): pass class MyMeta(type): def __new__(cls, name, bases, namespace, otherarg): self = super().__new__(cls, name, bases, namespace) self.otherarg = otherarg return self - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass self.assertEqual(MyClass.otherarg, 1) def test_type(self): t = type('NewClass', (object,), {}) self.assertIsInstance(t, type) self.assertEqual(t.__name__, 'NewClass') with self.assertRaises(TypeError): type(name='NewClass', bases=(object,), dict={}) if __name__ == "__main__": - main() + unittest.main() diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4336,21 +4336,20 @@ PyDoc_STRVAR(object_subclasshook_doc, static PyObject * object_init_subclass(PyObject *cls, PyObject *arg) { Py_RETURN_NONE; } PyDoc_STRVAR(object_init_subclass_doc, -"This method is called when a class is subclassed\n" +"This method is called when a class is subclassed.\n" "\n" -"Whenever a class is subclassed, this method is called. The default\n" -"implementation does nothing. It may be overridden to extend\n" -"subclasses.\n"); +"The default implementation does nothing. It may be\n" +"overridden to extend subclasses.\n"); /* from PEP 3101, this code implements: class object: def __format__(self, format_spec): return format(str(self), format_spec) */