-3

this Code snippet is namedtuple function of collections module in python.when I see it ,I don't understand it. class_definition is formated string, namespace is dict, exec could code ogject or string and so on, exec class_definition in namespace how to effect namespace, is that exec generate what?

1 Answers1

2

I am doing this on Python 3, but the principle is the same on Python 2.

Say you are executing

FooBarBaz = namedtuple('FooBarBaz', 'foo bar baz')

In this case, this code

class_definition = _class_template.format(
    typename = typename,
    field_names = tuple(field_names),
    num_fields = len(field_names),
    arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
    repr_fmt = ', '.join(_repr_template.format(name=name)
                         for name in field_names),
    field_defs = '\n'.join(_field_template.format(index=index, name=name)
                           for index, name in enumerate(field_names))
)

uses str.format to fill in the class template, setting the class_definition into a string, whose contents are:

class FooBarBaz(tuple):
    'FooBarBaz(foo, bar, baz)'

    __slots__ = ()

    _fields = ('foo', 'bar', 'baz')

    def __new__(_cls, foo, bar, baz):
        'Create new instance of FooBarBaz(foo, bar, baz)'
        return _tuple.__new__(_cls, (foo, bar, baz))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new FooBarBaz object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 3:
            raise TypeError('Expected 3 arguments, got %d' % len(result))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        return 'FooBarBaz(foo=%r, bar=%r, baz=%r)' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values'
        return OrderedDict(zip(self._fields, self))

    def _replace(_self, **kwds):
        'Return a new FooBarBaz object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, ('foo', 'bar', 'baz'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % kwds.keys())
        return result

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    __dict__ = _property(_asdict)

    def __getstate__(self):
        'Exclude the OrderedDict from pickling'
        pass

    foo = _property(_itemgetter(0), doc='Alias for field number 0')

    bar = _property(_itemgetter(1), doc='Alias for field number 1')

    baz = _property(_itemgetter(2), doc='Alias for field number 2')

Now after that the code creates a new dictionary that is used as the global namespace for exec:

namespace = dict(__name__='namedtuple_%s' % typename)

The reason why we don't use an empty dictionary is that if there is any tracing program that would print out the __name__ of the current module, then it would find __name__ set to namedtuple_FooBarBaz instead of it not existing.

After that, we execute the class definition, from strings, with global scope as this dictionary.

exec(class_definition, namespace)

Basically this executes the class definition above, which defines a new module global variable FooBarBaz, which is stored into the namespace dictionary, which in turn can be fetched there by:

result = namespace[typename]   # namespace['FooBarBaz']

now result is our newly created class; then some wizardry is done for it to survive pickling, after which the class is returned...

and this code again can assign it to the variable FooBarBaz:

FooBarBaz = namedtuple('FooBarBaz', 'foo bar baz')