Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: eryk sun Newsgroups: comp.lang.python Subject: Re: Special attributes added to classes on creation Date: Sun, 3 Jul 2016 23:07:53 +0000 Lines: 151 Message-ID: References: <57793711$0$1611$c3e8da3$5496439d@news.astraweb.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Trace: news.uni-berlin.de fAwwnbQBD+QU4yXoZW0G+QZWz+LHGNNOdECamyMfIblg== Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'cpython': 0.05; 'defaults': 0.05; 'method.': 0.05; '__name__': 0.07; 'attributes': 0.07; 'defines': 0.07; 'implements': 0.07; 'method,': 0.07; 'present,': 0.07; 'remaining': 0.07; '__init__': 0.09; 'defined,': 0.09; 'dict': 0.09; 'namespace': 0.09; 'none.': 0.09; 'positional': 0.09; 'example:': 0.10; 'def': 0.13; 'skip:f 30': 0.15; 'variables': 0.15; "'c'": 0.16; '2016': 0.16; '@classmethod': 0.16; '__new__': 0.16; 'argument.': 0.16; 'arguments:': 0.16; 'attributes.': 0.16; 'bases,': 0.16; 'bytecode': 0.16; 'descriptors': 0.16; 'descriptors.': 0.16; 'globals.': 0.16; 'metaclass': 0.16; 'optionally': 0.16; 'preamble': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'set,': 0.16; 'when?': 0.16; 'wrote:': 0.16; 'documented': 0.18; '>>>': 0.20; '3.x': 0.22; 'arguments': 0.22; 'keys': 0.22; 'defined': 0.23; 'implemented': 0.24; 'written': 0.24; 'header:In- Reply-To:1': 0.24; 'rest': 0.26; 'message-id:@mail.gmail.com': 0.27; 'defining': 0.27; 'executing': 0.27; 'object,': 0.27; 'function': 0.28; "skip:' 10": 0.28; 'values': 0.28; 'initial': 0.28; 'arguments,': 0.29; 'initialized': 0.29; 'referenced': 0.29; 'instruction': 0.29; 'allows': 0.30; 'classes': 0.30; 'code': 0.30; "can't": 0.32; 'skip:_ 10': 0.32; 'statement': 0.32; 'class': 0.33; 'url:python': 0.33; "d'aprano": 0.33; 'steven': 0.33; 'add': 0.34; 'gets': 0.35; 'received:google.com': 0.35; 'mapping': 0.35; 'returning': 0.35; 'but': 0.36; 'should': 0.36; 'there': 0.36; 'url:org': 0.36; 'keyword': 0.36; 'skip:{ 10': 0.36; 'to:addr:python-list': 0.36; 'pm,': 0.36; 'subject:: ': 0.37; 'two': 0.37; 'method': 0.37; 'starting': 0.37; 'creation': 0.38; '(1)': 0.38; 'names': 0.38; 'skip:p 20': 0.38; 'whatever': 0.39; 'takes': 0.39; 'to:addr:python.org': 0.40; 'called': 0.40; 'body': 0.61; 'default': 0.61; 'bases': 0.63; 'respect': 0.65; 'statement,': 0.66; "they're": 0.66; 'url:6': 0.66; 'potentially': 0.67; 'jul': 0.72; 'present.': 0.72; 'body:': 0.84; 'compiles': 0.84; 'dict.': 0.84; 'etc,': 0.84; 'locals': 0.84; 'url:cpython': 0.84; 'subject:Special': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=3Sxq/nOU1jdZdyWJyx3se9g0t7nleh9fvo5DT3KKz7w=; b=dfZ3eY6ZDr6soNP1yKWjj3hTZ9T53aplrHj/Nme+efJ8bR8fSTX/nKFhZoLbUBqXMo bBtgtbJAK40+oxxE0llo0JBpF2hlco6sRe0HzKbV4UHk80AvXe0bMItdcRNr0mziU61o mt0ZXjQK1WcFAqjqhBA4x8NHOuh2IfjCnbQIqm8V9HvdGv8v2Oxte2GG2qlafsV/AFGl v9SCTOkpvOAKvSHbJeVDPLdYmuQ1Rc1POmgp3O8aLr96Jm/ZZ+EZxm/E2SkP0y7iibPr IVKcyneVoiKkaPoO/MYZZ/pEGBziQQtZR199RH2SZiINlwgW91bapm8rKrjHWgJaKSq5 NUAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=3Sxq/nOU1jdZdyWJyx3se9g0t7nleh9fvo5DT3KKz7w=; b=gVIDqGQ8PERddn/+x/0vGfg9l2IKqJ/T5Apn5ASd25KjfLrcMlpsQdNWfNtuubmPzn vvGlPk4MgVp8xQikb4Jf8SD2IypxRScaJrZrx58+/eAX1PWyvsQQaY11kXZKffx07kvV yjG5N/zIBycwJ0v3/xrAa6Dpsny+MnylwcC0/7NW4uJ3t8xodRbr1LJ2vEyw1ZYZdrdZ lb0SgtQGN7VeCbYKeCJN8GCk+gF6ZeS/jr40Og3/q0GYZZrYtK5OEK4UdRMS/CRhcgAQ fvJWR1GboiT9Mw6e2jVMyiv6l3KnmhKkLhrVkYncPLnPvHqFMg0I3SWgidY54qfq/j4F L2cA== X-Gm-Message-State: ALyK8tIW3IZd/te69F2bWTdZvqRdpRQvd/J5n1RDVjL1MTFfCRu08dAYis0u0GZ2FBUIV1Bl0ZqoUqLfJt/v8A== X-Received: by 10.36.141.196 with SMTP id w187mr6934063itd.87.1467587313479; Sun, 03 Jul 2016 16:08:33 -0700 (PDT) In-Reply-To: <57793711$0$1611$c3e8da3$5496439d@news.astraweb.com> X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Mailman-Original-Message-ID: X-Mailman-Original-References: <57793711$0$1611$c3e8da3$5496439d@news.astraweb.com> Xref: csiph.com comp.lang.python:111017 On Sun, Jul 3, 2016 at 4:02 PM, Steven D'Aprano wrote: > Is there any documentation for exactly what keys are added to classes when? It should be documented that the namespace that's passed to the metaclass contains __module__ and __qualname__, and optionally __doc__ if the class has a docstring. These names are also available as local variables while executing the class body: >>> class C: ... 'eggs and spam' ... print('__module__:', __module__) ... print('__qualname__:', __qualname__) ... print('__doc__:', __doc__) ... __module__: __main__ __qualname__: C __doc__: eggs and spam This is potentially an (unlikely) issue because the class body preamble overwrites whatever custom values of these attributes were added by the metaclass __prepare__ method. For example: class Meta(type): @classmethod def __prepare__(mcls, name, bases, **kwds): return {'__module__': 'spam', '__qualname__': 'eggs.spam.' + name, '__doc__': 'spam spam spam', 'x': 42} class C(metaclass=Meta): 'docstring' >>> C.__module__ '__main__' >>> C.__qualname__ 'C' >>> C.__doc__ 'docstring' >>> C.x 42 I don't think additional documentation is required for the __dict__ and __weakref__ descriptors. They're described with respect to __slots__. They can't be added until after the metaclass processes __slots__, if present. You can begin unraveling how CPython 3.x class creation works by disassembling the bytecode for the following example: def f(): class Q(Base, metaclass=Meta): 'example class' x = 1 def method(self): __class__ # or use super() The initial work of defining a class is implemented via builtins.__build_class__, which is referenced by the bytecode instruction LOAD_BUILD_CLASS. This built-in function takes two required positional arguments: the argumentless function that implements the class body and the class name. Optionally, the base class(es) are passed as the remaining positional arguments, and the metaclass is passed as a keyword argument. Additional keyword arguments can be passed in the class statement, but this requires a custom metaclass with __new__ and __init__ methods that accept the extra arguments. The metaclass __prepare__ method also gets passed the name, bases and extra keyword arguments. Here's the bytecode setup to call __build_class__ for the above example: >>> dis.dis(f) 2 0 LOAD_BUILD_CLASS 2 LOAD_CONST 1 () 4 LOAD_CONST 2 ('Q') 6 MAKE_FUNCTION 0 8 LOAD_CONST 2 ('Q') 10 LOAD_GLOBAL 0 (Base) 12 LOAD_CONST 3 ('metaclass') 14 LOAD_GLOBAL 1 (Meta) 16 EXTENDED_ARG 1 18 CALL_FUNCTION 259 (3 positional, 1 keyword pair) 20 STORE_FAST 0 (Q) 22 LOAD_CONST 0 (None) 24 RETURN_VALUE The locals mapping for the class body comes from the metaclass __prepare__ method, if defined, and is otherwise an empty dict. There's a default type.__prepare__, which returns an empty dict: >>> type.__prepare__(1, 2, 3, spam='whatever') {} The code for the class body defines the __module__, __qualname__, and __doc__ attributes. It also defines the __class__ closure when a class has methods that use super() or reference __class__. The return value of the class body is either the __class__ cell object, if defined, or None. Returning the __class__ closure cell allows __build_class__ to set the new class as the value of the cell. Here's the bytecode for the body of class Q: >>> dis.dis(f.__code__.co_consts[1]) 2 0 LOAD_NAME 0 (__name__) 2 STORE_NAME 1 (__module__) 4 LOAD_CONST 0 ('f..Q') 6 STORE_NAME 2 (__qualname__) 3 8 LOAD_CONST 1 ('example class') 10 STORE_NAME 3 (__doc__) 4 12 LOAD_CONST 2 (1) 14 STORE_NAME 4 (x) 5 16 LOAD_CLOSURE 0 (__class__) 18 BUILD_TUPLE 1 20 LOAD_CONST 3 () 22 LOAD_CONST 4 ('f..Q.method') 24 MAKE_FUNCTION 8 26 STORE_NAME 5 (method) 28 LOAD_CLOSURE 0 (__class__) 30 RETURN_VALUE >>> f.__code__.co_consts[1].co_cellvars ('__class__',) The rest of the process is all written in C. compiler_class, which compiles a class statement to the above bytecode: https://hg.python.org/cpython/file/v3.6.0a2/Python/compile.c#l1809 builtins.__build_class__: https://hg.python.org/cpython/file/v3.6.0a2/Python/bltinmodule.c#l53 type.__new__: https://hg.python.org/cpython/file/v3.6.0a2/Objects/typeobject.c#l2268 In type_new, the class dict gets initialized starting on line 2516. __module__, if not already set, is the value of __name__ in the current globals. __qualname__, if not already set, defaults to the class name. The getset descriptors for __dict__ and __wreakref__, if present, are added to the class on line 2625, but they're not added to the class dict until the type is made ready via PyType_Ready. On line 4886, add_getset is called to add them. The names "__dict__" and "__weakref__" are defined by subtype_getsets_full, etc, starting on line 2154. Also in PyType_Ready, on line 4937, __doc__ is added if it's not already present in the class dict.