python / cpython

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

documentation on metaclasses is incomplete and misleading #46075

Closed 040a6c67-9f49-475c-a0fb-294ef5661fed closed 16 years ago

040a6c67-9f49-475c-a0fb-294ef5661fed commented 16 years ago
BPO 1734
Nosy @birkenfeld, @amauryfa

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 = 'https://github.com/birkenfeld' closed_at = created_at = labels = ['interpreter-core'] title = 'documentation on metaclasses is incomplete and misleading' updated_at = user = 'https://bugs.python.org/lpd' ``` bugs.python.org fields: ```python activity = actor = 'georg.brandl' assignee = 'georg.brandl' closed = True closed_date = closer = 'georg.brandl' components = ['Interpreter Core'] creation = creator = 'lpd' dependencies = [] files = [] hgrepos = [] issue_num = 1734 keywords = [] message_count = 6.0 messages = ['59216', '59218', '59245', '59302', '59312', '59482'] nosy_count = 3.0 nosy_names = ['georg.brandl', 'lpd', 'amaury.forgeotdarc'] pr_nums = [] priority = 'normal' resolution = 'fixed' stage = None status = 'closed' superseder = None type = None url = 'https://bugs.python.org/issue1734' versions = ['Python 2.5'] ```

040a6c67-9f49-475c-a0fb-294ef5661fed commented 16 years ago

In the following, dir(Node) should include the name 'z', and Node.z should be 'Node'. However, dir(Node) does not include 'z', and Node.z is undefined (AttributeError). This is directly contrary to the Python documentation, which says "metaclasses can modify dict".

class MetaNode(type):
    def __init__(cls, name, bases, cdict):
        cdict['z'] = name
        type.__init__(name, bases, cdict)
class Node(object):
    __metaclass__ = MetaNode

print dir(Node) print Node.z

amauryfa commented 16 years ago

When designing a metaclass, you should override __new, not __init:

class MetaNode(type):
   def __new__(cls, name, bases, cdict):
       cdict['z'] = name
       return type.__new__(cls, name, bases, cdict)

class Node(object):
   __metaclass__ = MetaNode
040a6c67-9f49-475c-a0fb-294ef5661fed commented 16 years ago

Please reopen this issue as a documentation bug.

The documentation for __new__ in section 3.4.1 says:

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation.

The documentation for metaclasses in 3.4.3 says nothing about __new vs. __init. It says the metaclass will be "called" for class creation, and it says the metaclass can be any "callable". This would imply the use of __call rather than __new or __init__.

I think 3.4.1 should say:

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also used for custom metaclasses (q.v.).

I think 3.4.3 should be reviewed in its entirety to replace the misleading language about "called" and "callable" with language that explicitly mentions __new__.

birkenfeld commented 16 years ago

I'll look into it.

040a6c67-9f49-475c-a0fb-294ef5661fed commented 16 years ago

Actually, "called" and "callable" are OK, if the documentation says somewhere that the normal effect of "calling" a type object is to invoke __new. The places I looked first (sections 3.1, 3.3, and 3.4.1) do not say this. 5.3.4 does say that the result of calling a class object is a new instance of that class, but it doesn't mention __new. So perhaps it would OK to just add something like the following to 3.4.3:

Note that if a metaclass is a subclass of \<code>type\</code>, it should override \<code>new\</code>, not \<code>call\</code>.

birkenfeld commented 16 years ago

This should now be appropriately explained in the trunk, r59837. I also added an example of using __new__ in a metaclass.