25

Aside from types.new_class's capability to define the keyword arguments when creating a class. Are there any major differences between the two approaches?

import types

First = type('First',(object,),{'asd':99})
k = First()

Second = types.new_class('Second',(object,),{},lambda x:x)
x = Second()
Carlos Miguel Colanta
  • 2,685
  • 3
  • 31
  • 49

2 Answers2

16

Are there any major differences between the two approaches?

Yes. The answer involves a concept called "metaclasses".

[Metaclasses] are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). ⸺ Tim Peters, author of Zen of Python (source)

If you think you're in that 99%, then read no further, feel free to just use type. It's just as good as types.new_class except for in the rare situation that you're using metaclasses.

If you want to know more about metaclasses, I suggest you take a look at some of the high quality answers posted to "What are metaclasses in Python?". (I recommend this one.)

Once you understand what a metaclass is, the answer is fairly self-evident. Since type is a specific metaclass, it will only work if you want to create classes that use it as their metaclass.

However, if you want to use a non-default metaclass

class MySimpleMeta(type):
    pass

and a static class won't do

class MyStaticClass(object, metaclass=MySimpleMeta):
    pass

then you can use types.new_class

import types

MyStaticClass = types.new_class("MyStaticClass", (object,), {"metaclass": MySimpleMeta}, lambda ns: ns)
# this is equivalent to the above class.

(As a brief aside, the reason that it requires a callable (e.g. lambda ns: ns) instead of a dictionary is because metaclasses are allowed to care about the order in which the attributes are defined. )

joseville
  • 685
  • 3
  • 16
timotree
  • 1,325
  • 9
  • 29
  • 2
    Just to add to this, you can already create a new class dynamically using a statically known metaclass (as you pointed out), e.g. `MyStaticClass = MySimpleMeta("MyStaticClass", (), {})`. The advantage of `new_class` is it allows the metaclass itself to be determined dynamically. – Ben Oct 27 '22 at 18:23
2

I realise this is late, and hence you've already likely answered this yourself.

Firstly, it seems that you misunderstand the kwds argument of types.new_class; it is the class keyword arguments, e.g

class MyMeta(type):
    def __new__(metacls, name, bases, attrs, **config):
        print(config)
        return super().__new__(metacls, name, bases, attrs)

    def __init__(cls, name, bases, attrs, **config):
        super().__init__(name, bases, attrs)

class SomeCls(metaclass=MyMeta, debug=True):
    pass

>> {'debug': True}

is analogous to (without the print)

SomeCls = types.new_class("SomeCls", (), {'debug':True})

These meta-arguments are useful when configuring meta-classes.

I am not really sure as to why new_class was designed to accept a callable vs a dict directly, but I suspect it was to avoid implicit shared state between "new" classes that did not inherit from one another.

Angus Hollands
  • 351
  • 2
  • 11