2

I'm trying to design a class structure where the bases of a class are determined at runtime. With a great many thanks to the answer to this question about metaclasses, it clicked that the type function can be made to do this by constructing the tuple of bases at runtime. Excellent.

Now that opens a possibility that I'm not sure I'm comfortable with-- through some sequence of operations I could wind up with the equivalent to:

X  = type('X', (object,), {})
Y  = type('Y', (X,), {})
Y2 = type('Y', (object,), {})

I now have two classes (Y and Y2) with different inheritance structures but both share the classname 'Y'.

c  =  y.__class__
c2 = y2.__class__

(c,c2)                      #returns  (__main__.Y, __main__.Y)
(c.__bases__,c2.__bases__)  #returns  ((__main__.X,), (object,))

It appears from this, that the classname is just an arbitrary string for human consumption, and Python takes the view that if I want to go and confuse myself, I'm welcome to.

Is that correct? Are there pitfalls awaiting me elsewhere in the bowels of Python if different classes are allowed to share the same name?

Community
  • 1
  • 1
Omegaman
  • 2,189
  • 2
  • 18
  • 30
  • I don't now why you are trying to do this, but it's almost certainly a #hugemistake. If there's even the slightest chance that someone else will have to read this code (e.g. your future self), it is a safe bet that you will confuse the crap out of them. – allyourcode Mar 16 '14 at 03:11
  • Not sure what you mean by "the bases are determined at runtime". Python is a interpreted language and *everything* is determined at runtime. – Joel Cornett Mar 16 '14 at 03:31
  • @allyourcode, the code sample is just to demonstrate the outcome. I can reach the same result in any situation where I allow classname and inheritance structure to vary independently. I'm just wondering if I need to guard against that for stability sake. – Omegaman Mar 16 '14 at 03:33
  • @JoelCornett, in most code that I write, I determine the base at edit time-- ie. `class Baz(Foo):pass`. The interpreter sets the base at runtime, sure, but until I edit the file, the base is essentially static. In this question, I'm asking what happens when I write code like `return type('Baz',(Foo,) if sunny else (Bar,),{})` where the base is determined by runtime conditions. – Omegaman Mar 16 '14 at 03:39

2 Answers2

4

I can't comment on why you'd want to do something like this, but if you do need such dynamic behavior, perhaps consider using a metaclass to accomplish your goal?

Regardless, if you're wondering whether using the same class name will cause problems later on (I'm assuming you mean problems other than maintainability - as others have said, this has a high potential of being confusing and unnecessarily complex), then no, it won't.

The documentation for type() is pretty clear about what the arguments are for:

The name string is the class name and becomes the __name__ attribute; the bases tuple itemizes the base classes and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body and becomes the __dict__ attribute

That description shows that the name is simply used to populate a __name__ field. It turns out that it's basically for the humans, as shown in another answer.

In fact, you don't even need to use type() to do this - you can do it with the simple class statement.

class X(object): pass
class Y(object): pass

def create_other_y():
    class Y(X): pass
    global Y2
    Y2 = Y

create_other_y()

(Y, Y2)                      # => (__main__.Y, __main__.Y)
(Y.__bases__, Y2.__bases__)  # => ((object,), (__main__.X,))
Community
  • 1
  • 1
voithos
  • 68,482
  • 12
  • 101
  • 116
3

The class name is not required to have any relation to the name of the variable you store it in. You can get the same behavior with a class statement and an assignment:

class Y(Base1): pass
Y2 = Y
class Y(Base2): pass

This happens in practice with things like decorators, if you don't update the returned object's metadata:

def decorate(klass):
    class DecoratedClass(klass):
        def stuff(self):
            do_whatever()
    return klass

@decorate
class Foo(object): pass

@decorate
class Bar(object): pass

After this code, the Foo and Bar variables refer to two classes named DecoratedClass with different inheritance hierarchies.

Python assigns no special significance to the class name. It's mostly there for humans to see. If you do this, Python will have no problems with it. The people who have to maintain the code might, though.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 2
    Note, pickle and other serialization methods will indeed get confused by this because they do use the names to try to reconstitute the classes on deserialization. You are unlikely to get satisfactory results with serialization anyways with dynamically defined classes, but it is a small consideration. – Max Mar 16 '14 at 04:27