def test_errors(self): class MyMeta(type): pass with self.assertRaises(TypeError): class MyClass(metaclass=MyMeta, otherarg=1): pass with self.assertRaises(TypeError): types.new_class("MyClass", (object,), dict(metaclass=MyMeta, otherarg=1)) types.prepare_class("MyClass", (object,), dict(metaclass=MyMeta, otherarg=1)) class MyMeta(type): def __init__(self, name, bases, namespace, otherarg): super().__init__(name, bases, namespace) with self.assertRaises(TypeError): class MyClass(metaclass=MyMeta, otherarg=1): pass class MyMeta(type): def __new__(cls, name, bases, namespace, otherarg): return super().__new__(cls, name, bases, namespace) def __init__(self, name, bases, namespace, otherarg): super().__init__(name, bases, namespace) self.otherarg = otherarg class MyClass(metaclass=MyMeta, otherarg=1): pass self.assertEqual(MyClass.otherarg, 1)
def create_class( name: str, bases: Iterable = (), attrs: dict = {}, metaclass: Optional[Callable[..., Type[T]]] = None, **kwargs: Any ) -> Type[T]: """ Creates a new class. This is similar to :func:`types.new_class`, except it calls :func:`resolve_bases` even in python versions <= 3.7. (And it has a different interface.) :param name: The name of the new class :param bases: An iterable of bases classes :param attrs: A dict of class attributes :param metaclass: The metaclass, or ``None`` :param kwargs: Keyword arguments to pass to the metaclass :return: A new class """ if metaclass is not None: kwargs.setdefault('metaclass', metaclass) resolved_bases = resolve_bases(bases) meta, ns, kwds = types.prepare_class(name, resolved_bases, kwargs) ns.update(attrs) # Note: In types.new_class this is "is not" rather than "!=" if resolved_bases != bases: ns['__orig_bases__'] = bases return meta(name, resolved_bases, ns, **kwds)
def new_class(name, bases=(), kwds=None, exec_body=None): """Create a class object dynamically using the appropriate metaclass. """ import sys meta, ns, kwds = prepare_class(name, bases, kwds) if exec_body is not None: exec_body(ns) if sys.version_info >= (3, 0): return meta(name, bases, ns, **kwds) else: return meta(name, bases, ns)
def test_prepare_class(self): # Basic test of metaclass derivation expected_ns = {} class A(type): def __new__(*args, **kwargs): return type.__new__(*args, **kwargs) def __prepare__(*args): return expected_ns B = types.new_class("B", (object,)) C = types.new_class("C", (object,), {"metaclass": A}) # The most derived metaclass of D is A rather than type. meta, ns, kwds = types.prepare_class("D", (B, C), {"metaclass": type}) self.assertIs(meta, A) self.assertIs(ns, expected_ns) self.assertEqual(len(kwds), 0)
def prepare_class(): # Basic test of metaclass derivation expected_ns = {} class A(type): def __new__(*args, **kwargs): return type.__new__(*args, **kwargs) def __prepare__(*args): return expected_ns B = types.new_class("B", (object, )) C = types.new_class("C", (object, ), {"metaclass": A}) meta, ns, kwds = types.prepare_class("D", (B, C), {"metaclass": type}) print(meta is A) print(ns is expected_ns) print(len(kwds) == 0)
Stock.__module__ = __name__ print(type(Stock)) # <class 'abc.ABCMeta'> Stock = collections.namedtuple('Stock', ['name', 'shares', 'price']) print(Stock) # <class '__main__.Stock'> 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 metaclass, kwargs, ns = types.prepare_class('Stock', (), {'metaclass': type})
def update_event(self, inp=-1): self.set_output_val( 0, types.prepare_class(self.input(0), self.input(1), self.input(2)))
print(p.x) # 4 print(p.y) # 5 # p.x = 2 # AttributeError: can't set attribute print('%s %s' % p) # 4 5 """ 这项技术一个很重要的方面是它对于元类的正确使用。你可能像通过直接实例化一 个元类来直接创建一个类: Stock = type('Stock', (), cls_dict) 这种方法的问题在于它忽略了一些关键步骤,比如对于元类中 prepare () 方法 的调用。通过使用 types.new class() ,你可以保证所有的必要初始化步骤都能得到 执行。比如,types.new class() 第四个参数的回调函数接受 prepare () 方法返回 的映射对象。 如果你仅仅只是想执行准备步骤,可以使用 types.prepare class() 。例如: """ import types metaclass, kwargs, ns = types.prepare_class('Stock', (), {'metaclass':type}) """ 它会查找合适的元类并调用它的 prepare () 方法。然后这个元类保存它的关键 字参数,准备命名空间后被返回。 """
def __new__(cls, *args): if len(args) != len(field_names): raise TypeError(f'Expected {len(field_names)} arguments') return tuple.__new__(cls, args) cls_dict['__new__'] = __new__ # Make the class cls = types.new_class(class_name, (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']) print(f'point object: {Point}') p = Point(5, 8) print(f'p length = {len(p)}') print(f'p.x = {p.x}') print(f'p.y = {p.y}') # p.x = 3 print(f'p is: {p}') Course = type('Course', (), cls_dict) import types metaclass, kwargs, ns = types.prepare_class('Course', (), {'metaclass': type})