# 手动创建类

# 概述

我们可以使用types.new_class​和type​两种方式手动创建类,但是type​相较于new_class​的形式会忽略一些关键步骤,需要注意。

type​​接收三个参数:对象名、基类元组以及属性空间。

class type(object)
 |  type(object_or_name, bases, dict)
 |  type(object) -> the object's type
 |  type(name, bases, dict) -> a new type

types.new_class​接收四个参数:对象名、基类元组、属性空间以及fn​四个参数。

new_class(name, bases=(), kwds=None, exec_body=None)
    Create a class object dynamically using the appropriate metaclass.

可以注意到types.new_class​可以传入一个函数,这个函数可以接收到__prepare__​的结果并接着处理,这是type​所不行的。

此外,types​还提供了types.prepare_class()​来仅仅执行准备阶段,因此关于手动创建类的部分可以多多研究types​模块。

import types
metaclass, kwargs, ns = types.prepare_class('Stock', (), {'metaclass': type})

# types.new_class

# 手动实现namedtuple

import operator
import types
import sys

def named_tuple(classname, fieldnames):
    # Populate a dictionary of field property accessors
    cls_dict = { name: property(operator.itemgetter(n))
                for n, name in enumerate(fieldnames) }

    # Make a __new__ function and add to the class dict
    def __new__(cls, *args):
        if len(args) != len(fieldnames):
            raise TypeError('Expected {} arguments'.format(len(fieldnames)))
        return tuple.__new__(cls, args)

    cls_dict['__new__'] = __new__

    # Make the class
    cls = types.new_class(classname, (tuple,), {},
                        lambda ns: ns.update(cls_dict))

    # Set the module to that of the caller
    cls.__module__ = sys._getframe(1).f_globals['__name__']
    return cls

"""
>>> Point = named_tuple('Point', ['x', 'y'])
>>> Point
<class '__main__.Point'>
>>> p = Point(4, 5)
>>> len(p)
2
>>> p.x
4
>>> p.y
5
>>> p.x = 2
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> print('%s %s' % p)
4 5
>>>
"""

# type

Stock = type('Stock', (), cls_dict)