1

Using WTForms form definition classes as an example:

class RegistrationForm(Form):
    username     = StringField('Username', [validators.Length(min=4, max=25)])
    email        = StringField('Email Address', [validators.Length(min=6, max=35)])
    accept_rules = BooleanField('I accept the site rules', [validators.InputRequired()])

and looking at the source of the library it seems WTForms allows a user to define a very simple class of a customised form structure (as above), which in turn then gets used to construct a new field class which is not instantiated when the class is generated.

I've read a number of tutorials about class factories and metaclasses and the general consensus is to avoid metaclasses and use things like class decorators instead. The problem is the tutorials either start importing extra libraries eg: import six, mix explanations of different Python versions together, use overly complex examples or advise not to use metaclasses at all.

Please can somebody provide a very simple explanation (for Python 3) of how to use a simple class definition (like the WTForms example above) along with metaclasses to customise a brand new construction of a class without actually instantiating the class when it's constructed.

Edit: Apologies for finding it difficult to explain what my end goal is but as I have gone through tutorials it has been unclear as to whether class decorators, metaclasses, magic methods (call, new, init) or a combination of such are what I needed to achieve what I visualised, or if what I was visualising was the wrong way of doing things. Unfortunately it seems impossible to judge if my goal was wrong without being able to understand the mechanisms needed to achieve it. I've realised metaclasses are the way to go and just need pointing in the right direction for a very simple metaclass example done the Python 3.x way.

Mozzie
  • 45
  • 6
  • 4
    Why did the tutorials you were reading tell you using metaclasses is bad practice? There are perfectly valid use cases for metaclasses. If that is all the tutorials said, IMO they are wrong. – Christian Dean May 30 '17 at 22:57
  • It would help if you were more specific about what you're trying to accomplish, references to what you read that suggested metaclasses are wrong for the purpose, etc. – pvg May 30 '17 at 23:04
  • 1
    Do you have an example link you can provide that explain why metaclasses should not be used? That doesn't seem right. – idjaw May 30 '17 at 23:14
  • 3
    I somewhat suspect what the tutorials actually said was something along the lines of 'before you reach for metaclasses, consider if your metaprogramming problem can't be solved with simpler, more constrained techniques such as decorators'. rather than 'don't use metaclasses to make calasses'. – pvg May 30 '17 at 23:18
  • Just one of the tutorials I've used: [Python Metaclasses](https://dougblack.io/words/metaclasses.html). I'm afraid I don't have an explanation as to why they can't be used, many tutorials just advise that class decorators and other alternatives are far simpler and cover 99% of cases. It's the reason I ask the question on here so I'm starting in the right place and heading in the right direction because so many of the tutorials use complex examples, third party libraries, confusing language or all the above. If I know metaclasses are the way to go I'll persevere in finding a decent guide. – Mozzie May 30 '17 at 23:59
  • Well, you haven't really told us what you're trying to do. The advice in the tutorial is more or less what I said - it just says to be wary of a industrial laser cutter level tool like metaclasses. In an ORM, where one of the core tasks really is type-mapping, it makes some sense you might want to dynamically create types. But that's a very specific case. – pvg May 31 '17 at 00:07
  • 1
    Also - I really put in check the "90%" use case covered by class decorators (or 99%, it is just made on the flight any way). I've had to create a few metaclasses in "real life projects" - and I think only one of these times a class decorator was enough. – jsbueno May 31 '17 at 08:25
  • @Christian Dean, if I knew why they said metaclasses are bad practice I wouldn't be asking on SO which direction to take, and probably would have just said I'm the 1% case where they are needed. It's not ALL each of the tutorials said, they provided a (complicated in my opinion) explanation and usually finish off with something like "So when do I use metaclasses? ...never" which, yes, makes following tutorials even more uncertain and why I try to find clear, concise answers on SO. – Mozzie May 31 '17 at 09:24

1 Answers1

3

You can create classes dynamically - with no custom metaclasses and no decorators with what looks to the programmer as simple function call.

Just make a call to Python's builtin type with three parameters: the name of the class, a tuple with its bases, and a mapping object with its namespace (i.e. a dictionary containing the attributes and methods you would ordinarily define on the class body).

def __init__(self):
    ...

namespace = {
   '__init__': init,
   'name': 'default name'
}

MyClass = type("MyClass", (object,), namespace)

You loose some features that are only possible due to the compiler doing a couple special things during building functions declared within a class body - mostly the ability to use paramterless super and name mangling of attributes starting with __, but that is it.

That said it should be noted this is not with "no metaclasses". "type" is itself a metaclass - the default Python metaclass for all objects - and is calling a metaclass that create a class. There is no other way to create a class. A "class decorator" is just a method that can makes changes to a class object after it is created.

Any function or method that yields a new, dynamic class, will have inside it to, at some point, call type or other metaclass. In the same mood, a "metaclass" does not create dynamic classes by itself - it needs do be either used in a class body declaration, or called with (at least) the same parameters used for calling type.

As for the recommendations for "class decorators" instead of metaclasses, I am not sure is that true (beyond the fact there is no way a "class decorator" can create classes dynamically by itself): their main drawback is that there is no ordinary way for subclasses of decorated classes to have the parent's class decorators applied to themselves automatically, while metaclasses are inherited.

In Python 3.6 you have the __init_subclass__ protocol which, yes, can avoid a lot of the traditional uses for a metaclass (but still, it won't "create classes dynamically" - calling type does that).

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 2
    Very clear answer, thank you so much. It's amazing how many guides use two paragraphs to explain 'type is a class of a class' etc. yet just the sentence 'type is itself a metaclass' helps a lot. It's certainly explained what I need to focus on learning and what I can leave until a later date. Thanks again @jsbueno – Mozzie May 31 '17 at 09:15