26

I am trying to create a function that is passed a parameter x and returns a new class C. C should be a subclass of a fixed base class A, with only one addition: a certain class attribute is added and is set to equal x.

In other words:

class C(A):
  C.p = x # x is the parameter passed to the factory function

Is this easy to do? Are there any issues I should be aware of?

max
  • 49,282
  • 56
  • 208
  • 355

2 Answers2

28

First off, note that the term "class factory" is somewhat obsolete in Python. It's used in languages like C++, for a function that returns a dynamically-typed instance of a class. It has a name because it stands out in C++; it's not rare, but it's uncommon enough that it's useful to give the pattern a name. In Python, however, this is done constantly--it's such a basic operation that nobody bothers giving it a special name anymore.

Also, note that a class factory returns instances of a class--not a class itself. (Again, that's because it's from languages like C++, which have no concept of returning a class--only objects.) However, you said you want to return "a new class", not a new instance of a class.

It's trivial to create a local class and return it:

def make_class(x):
    class C(A):
        p = x
    return C
Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • 1
    Thanks, I removed references to 'factory' from my question. With this function, would I have a problem with name conflicts or will `C` be forgotten by the time the function returns? – max Feb 01 '11 at 03:04
  • 1
    As I said in a comment above, there are *two* places where the name is recorded. In Glenn's example, the name "C" is both the internal name of the class (which is a permanent property of the class) as well as a local variable name "C". By the time the function returns, the local variable "C" is forgotten about, but the class retains its internal name "C". But as I said above, no name conflicts for internal class names, so this won't be a problem. – mgiuca Feb 01 '11 at 03:09
  • 1
    The class itself is simply an object; it's no different than saying `C = 1; return C`. (The name "C" is stored in the object, as `C.__name__`, but that's just for debugging and cosmetic purposes.) – Glenn Maynard Feb 01 '11 at 03:09
  • I don't see much difference between the `type` and the declaration approaches, but I find a class declaration much easier to read (especially if I did more than just add a single attribute). – max Feb 01 '11 at 04:15
  • Well, in the Zope community at least, we would definitely call that a class factory. But maybe, with a component architecture and all, we are a bit more in love with GoF. :) – Lennart Regebro Feb 01 '11 at 06:14
12

The type function has a 3-argument version which dynamically constructs a new class. Pass the name, bases and a dict containing the attributes and methods of the class.

In your case:

def class_factory(x):
    return type("C", (A,), {"p": x})

You can obviously dynamically set the name of the class, "C", but note that in order to make the class publicly accessible, you also need to assign the result of the function to a variable. You can do that dynamically using globals()["C"] = ..., or assign the classes to a dictionary, whatever.

mgiuca
  • 20,958
  • 7
  • 54
  • 70
  • 1
    Ick, no. If you want a class, just define a class. – Glenn Maynard Feb 01 '11 at 02:43
  • 1
    Wouldn't I run into name conflict when the function is called with different parameters? I probably need to add some extra text to the class name to avoid that? – max Feb 01 '11 at 02:50
  • @Glenn Maynard Interesting. I tried these out and they both seem to create pretty much the same thing. I can't say which is better -- my way seems cleaner since you are dynamically creating a class, you might as well call the function designed to dynamically create a class. One thing is that if you did want to dynamically customize the *name* of the class or which *base* class it is derived from, Glenn's method won't work, so mine is more general. Anyway, I'm just going by the manual (linked above), which uses precisely this example to show how "type" works. – mgiuca Feb 01 '11 at 02:57
  • 2
    @max No, there would be no name conflict. Note that there are really two separate "names" for a class: the name that is inside the class and the name of the variable it is assigned to. These two are usually the same, but if you wrote "`X = type("Y", ...)`" then Y would be the internal name and X would be the variable name. The variable names can conflict, but you can have as many classes as you like which internally think of themselves as "C". – mgiuca Feb 01 '11 at 03:00
  • 4
    All classes in Python are created dynamically; there's no reason to use a special low-level function to create a class here instead of declaring a class in the normal, instantly understandable Python way. This function is used where ordinary Python syntax can't be used; for example, if you have an array of base classes, or if the class's name is created programmatically. – Glenn Maynard Feb 01 '11 at 03:06
  • @Glenn Maynard Yes but most classes are created dynamically *once*, and *at module load time*. Your code might be construed as creating a single class and returning a reference to it multiple times (that's what I thought at first), whereas a call to `type` is well-documented in the library docs. Not that there is any real difference between the two examples, so it's a matter of preference. – mgiuca Feb 01 '11 at 03:11
  • Using `type` to declare a simple class that can be declared normally results in very unobvious code. It's akin to using `__import__` to import modules--it's a low-level function that shouldn't be used unless there's a specific reason to. – Glenn Maynard Feb 01 '11 at 03:26
  • Thank you for the explanation about internal name vs variable name! – max Feb 01 '11 at 04:15
  • like that approach if you want to have control over how your classes are named :) – drssdinblck Aug 28 '17 at 13:28