views:

60

answers:

2

I have the following code:

def f():
    class XYZ:
        # ...
    cls = type('XXX', (XYZ, ), {})
    # ...
    return cls

I am now using it as follows:

C1 = f()
C2 = f()

and it seems to work fine: C1 is C2 returns False, there's no conflict between the class attributes of the two classes, etc.

Question 1

Why is that? How is it possible that C1 and C2 are both shown as class

<'__main__.XXX'>

and yet not the same class?

Question 2

Is there some problem with the fact that I have two identical names for two different classes?

Question 3

I would like to be able to write instead:

f('C1')
f('C2')

with the same effect. Is it possible?

Question 4

If I want C1 to look like a regular class, not main.XXX, is it ok to say:

C1.__name__ = '__main__.C1'
+2  A: 

Question 3

To have cls.__name__ be anything you want, (with a nod to delnan's suggestion)

def f(clsname):
    class XYZ:
        # ...
    XYZ.__name__ = XYZ
    # ...
    return XYZ

Question 1

The reason that c1 is not c2 is that they are two different objects stored at two different locations in memory.

Question 4

Try an answer to question 1 and see how it works out for you

Question 2

It can complicate debugging that their class attributes __name__ share a common value and this is bad enough to take pains to avoid. (see question 3). I would maintain though that they don't have the same name. One is named C1 and the other is named C2 (at least in the scope you are showing. If you were to pass them to a function, then there name in that scope would be the same as the name of parameter that they were passed through)

In fact, I'm so sure that they don't have the same name that trying to tell me otherwise is likely to cause me to turn the music up louder and pretend I can't hear you.

In response to comment

It can be done but it's just wrong. I'll illustrate anyway because it's illuminating:

def f(clsname):
    class XYZ(object):
        pass
    XYZ.__name__ = clsname
    globals()[clsname] = XYZ

f('C1')
f('C2')

print C1
print C2

This just works by sticking the class in the globals dict keyed by clsname. But what's the point? You can stick it in the globals dict under any name in fact because this is just another assignment. You are best off just returning the class from the function and letting the caller decide what name to give the class in it's own scope. You still have the __name__ attribute of the class set to the string you pass to the function for debugging purposes.

aaronasterling
The code you proposed is what I had originally. The problem is that the clsname is not known at the global level unless I say C1 = f('C1'). Can I fix it, so just the mere invocation of f('C1') allows to refer to class C1 at the global level?Re names: I agree with you; I was referring just to the _ _ name _ _ attribute being the same.
max
Ah it seems that it's a bad idea to 'fix it' based on the answer from delnan.. Then I guess I'll just keep using C1 = f() approach, and not worry about the XXX inside being the same.
max
@max. See my updates. You can still control the `__name__` attribute that gets assigned to the class for debugging purposes!
aaronasterling
Got it. I just wish I didn't have to write the repetitive C1 = f('C1'); but it seems there's no way the debugger can infer the 'C1' from C1 = f() :(
max
+2  A: 

Actually, you don't need to the cls = ... line at all.

>>> def f():
...     class C:
...         pass
...     return C
... 
>>> f() is f()
False

Reason: class (as well as e.g. def) defines a new class each time it is encountered = each time the function is called.

As for cls.__name__, it's really no semantic difference. The name is useful for debugging (you don't expose it directly to the user, do you?) and introspection, but it shouldn't be an issue. But if you absolutely want to have different names, you can change cls.__name__ before returning (also note that after C.__name__ = 'foo', C.__name__ == '__main__.foo'!).

At question 3: It would be possible to inject it directly into global namespace... don't do this. It has no advantages, only disatvantages: nonobvious side effects, bad style, the fact it's a hack at all, etc!

delnan
Understood. So the original code has no problems, except a bit of a difficulty with debugging/introspection - which cannot be solved without a hack or a duplication in the usage pattern: C1 = f('C1'). Correct?
max