python / cpython

The Python programming language
https://www.python.org
Other
62.35k stars 29.94k forks source link

Fix 3.3.3.1 Metaclasses Documentation #81128

Closed f62406a5-1c0d-4f0c-8788-796f13394ab1 closed 4 years ago

f62406a5-1c0d-4f0c-8788-796f13394ab1 commented 5 years ago
BPO 36947
Nosy @serhiy-storchaka, @matrixise, @MojoVampire, @DeVil2O, @idomic
PRs
  • python/cpython#15319
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields: ```python assignee = None closed_at = created_at = labels = ['3.7', 'type-bug', 'library', 'docs'] title = 'Fix 3.3.3.1 Metaclasses Documentation' updated_at = user = 'https://bugs.python.org/NicholasMatthews' ``` bugs.python.org fields: ```python activity = actor = 'serhiy.storchaka' assignee = 'docs@python' closed = True closed_date = closer = 'serhiy.storchaka' components = ['Documentation', 'Library (Lib)'] creation = creator = 'Nicholas Matthews' dependencies = [] files = [] hgrepos = [] issue_num = 36947 keywords = [] message_count = 14.0 messages = ['342723', '342724', '342725', '342726', '342727', '342728', '342730', '342731', '342780', '344105', '352972', '353002', '353134', '353149'] nosy_count = 7.0 nosy_names = ['docs@python', 'serhiy.storchaka', 'matrixise', 'josh.r', 'codevil_2o', 'Nicholas Matthews', 'Ido Michael'] pr_nums = ['15319'] priority = 'normal' resolution = 'fixed' stage = 'resolved' status = 'closed' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue36947' versions = ['Python 3.7'] ```

    f62406a5-1c0d-4f0c-8788-796f13394ab1 commented 5 years ago

    Currently the final sentence of the second paragraph reads: "In the following example, both MyClass and MySubclass are instances of Meta:" It should read something like: "In the following example, both MyClass and MySubclass have the metaclass Meta, and new instances will be created using Meta:"

    Classes are created by their metaclass, but cannot be said to be instances of their metaclass, correct?

    matrixise commented 5 years ago

    Thank you for your report

    matrixise commented 5 years ago

    just one question, I don't find the paragraph with this text in the code. Could you share the link of this paragraph? Thank you

    matrixise commented 5 years ago

    Found here: https://docs.python.org/3/reference/datamodel.html#metaclasses

    Thank you

    matrixise commented 5 years ago

    If you want to modify the text, please create a PR for this file: https://github.com/python/cpython/blob/master/Doc/reference/datamodel.rst#metaclasses

    Don't forget to sign the CLA,

    Thank you

    f62406a5-1c0d-4f0c-8788-796f13394ab1 commented 5 years ago

    Ok, I will create a PR soon and update the issue.

    99ffcaa5-b43b-4e8e-a35e-9c890007b9cd commented 5 years ago

    Clarification is fine, but "MyClass and MySubclass are instances of Meta:" is 100% true. Declaring a class to have a metaclass (or inheriting from a class with a metaclass) means that the class itself is an instance of the metaclass.

    New instances of the classes with metaclass Meta are not "created using Meta"; Meta modifies the creation of the classes themselves, not instances of the classes.

    Point is, your suggested change is half wrong (new instances of MyClass and MySubclass aren't directly created using Meta), and half misunderstanding the current documentation ("MyClass is an instance of Meta" already means "MyClass has the metaclass Meta").

    f62406a5-1c0d-4f0c-8788-796f13394ab1 commented 5 years ago

    Thanks for the clarification. For the first point on the correctness of the original text, that makes sense, could you link me to any relevant documentation for further reading?

    On the second point "Meta modifies the creation of the classes themselves, not instances of the classes." I think this is not 100% correct. When you create an instance of a class via "instance = MyClass()", the __call__ method of the metaclass is invoked, so metaclasses can control both class definition and instance creation.

    99ffcaa5-b43b-4e8e-a35e-9c890007b9cd commented 5 years ago

    Ah, you're right on __call; I've never bothered to override it on a metaclass, but yes, since a class using a metaclass is an instance of the metaclass, like all instances, calling it invokes the __call of its type (it's just that the default metaclass, type, has a __call that turns around and calls the __new and __init__ of the "instance", which is a class). The nomenclature is hard here.

    In any event, yes, overriding __call will let you hook into the construction of each individual instance of the classes using the metaclass. That's not generally true of all uses of metaclasses (if __call is inherited from type, then while new instances are technically created using the metaclass, the metaclass is just letting the normal __new/init__ calls take place without interference).

    There is very little in the way of Python official documentation on metaclasses; the line you proposed to change is one of the few places it's mentioned (most references to metaclasses are on that Data Model page). There are a couple of mentions in the PEPs, and a lot of off-site tutorials, but it's a poorly documented feature in general.

    It's pretty easy to demonstrate the current wording is correct though:

    >>> class Meta(type):
    ...     pass
    ...
    >>> class MyMeta(metaclass=Meta):
    ...     pass
    ...
    >>> isinstance(MyMeta, Meta)
    True

    Note that we're using isinstance, not issubclass, and we're not constructing a MyMeta instance. MyMeta itself is an instance of Meta.

    I really think the problem here is that the documentation is correct, but so bare it's easy to miss the implications of "MyClass and MySubclass are instances of Meta"; since the classes are instances of another class, the metaclass has the same power over them that normal classes have over their instances. That's why __call can hook the creation of instances, __new can hook the creation of the class itself, __getitem__ can be used to perform lookups on the child class (in at least of the few iterations of the typing framework, that's how List[int] and the like worked; not sure if it's still that way), and properties defined on the metaclass can be accessed on classes that use it, but not their instances. It's enormously powerful, but so complex that the Python docs tend to encourage simpler, more targeted ways of tweaking classes (e.g. decorators).

    0c3e473d-2946-4e31-b3c1-78c7a31f8193 commented 5 years ago

    It should be written like "In the following example, both MyClass and MySubclass are instances of Meta and the type of MyClass is of metaclass Meta and type of MySubclass is MyClass:"

    473000d8-0a7e-46d6-a90b-8ac238938f6c commented 4 years ago

    Hey,

    Is someone working on this issue? Can I take it?

    Ido

    serhiy-storchaka commented 4 years ago

    It is not good first issue. The referred documentation is complex and needs deep knowledge of Python for changing.

    The original report was wrong. The original documentation is correct. Josh, do you think the documentation needs other changes, and if yes, do you mind to create a PR? If no, I am inclined to close this issue.

    99ffcaa5-b43b-4e8e-a35e-9c890007b9cd commented 4 years ago

    The existing documentation is correct, just hard to understand if you don't already understand the point of metaclasses (metaclasses are hard, the language to describe them will be inherently a little klunky).

    At some point, it might be nice to write a proper metaclass tutorial, even if it's only targeted at advanced users (the only people who should really be considering writing their own metaclasses or even directly using existing ones; everyone else should be using more targeted tools and/or inheriting from classes that already implement the desired metaclass).

    The Data model docs aren't concerned with tutorials and examples though; they're just dry description, and they're doing their job here, so I think this issue can be closed.

    serhiy-storchaka commented 4 years ago

    Thank you Josh.