1

In Python, I currently have instances of a class like MyClass('name1'), MyClass('name2') and so on.

I want to make it so that each instance has its own superclass, i.e., I want MyClass('name1') to be an instance of Name1MyClass and MyClass('name2') to be an instance of Name2MyClass. Name1MyClass and Name2MyClass would be dynamically generated subclasses of MyClass. I can't figure out how to do this, because it seems that Python always makes whatever is returned from __new__ an instance of that class. It isn't clear to me how to do it in a metaclass either.

The reason I want to do this is that I want to define __doc__ docstrings on the instances. But it seems that help completely ignores __doc__ on instances; it only looks on classes. So to put a different docstring on each instance, I need to make each instance have its own custom class.

1 Answer 1

2

I could be wrong, but I don't think you want a metaclass here. __metaclass__es are used when the class is created, not when you call the class to construct a new instance of the class (or something else).

Here's an answer using __new__ without a metaclass. It feels a bit hacky, but it seems to work:

_sentinel = Ellipsis
class MyClass(object):
    def __new__(cls, name):
        if name is _sentinel:
            return object.__new__(cls)
        else:
            instance = type(name + cls.__name__, (MyClass,), {})(_sentinel)
            # Initialization goes here.
            return instance

print type(MyClass('name1'))
print type(MyClass('name2'))

There's a catch here -- All the business logic of initializing then new instance must be done in __new__. Since __new__ is returning a different type than the class it is bound to, __init__ won't get called.


Another option is to create a class factory:

class MyClass(object):
    pass

def class_factory(name):
    new_cls = type(name + MyClass.__name__, (MyClass,), {})
    return new_cls() # Or pass whatever you want in here...

print type(class_factory('name1'))
print type(class_factory('name2'))

Finally, you could even create a non-__new__ class method:

class MyClass(object):
    @classmethod
    def class_factory(cls, name):
        new_cls = type(name + cls.__name__, (cls,), {})
        return new_cls() # Or pass whatever you want in here...

print type(MyClass.class_factory('name1'))
print type(MyClass.class_factory('name2'))
Sign up to request clarification or add additional context in comments.

7 Comments

I think instead of checking for a sentinel, you can just check which class cls is.
Furthermore, to work with subclasses, you should use (cls,) as the bases argument to type, not (MyClass,).
@asmeurer -- Sure, you can do it however you want. I don't know your use-case, so I'm not sure how to best design my answer. But I think that all the pieces are there for you to build it out however you want.
Well it doesn't really matter what my use-case is. You should always write classes in such a way that subclasses work.
@asmeurer -- Fair point, but it's unclear how you want subclasses to work. Since your class is technically returning instances of a different type of class, It's hard to say what the actual subclassed behavior should be. In any event, the code above isn't meant to be production ready anyway -- Hard-coding the class in my mind conveys the idea of what is happening and de-mystifies it to some extent. My purpose is to write clear easy to understand code to convey an idea -- Not to write production ready code.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.