Reputation: 52235
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'
Upvotes: 1
Views: 458
Reputation: 70994
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.
Upvotes: 2
Reputation:
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!
Upvotes: 2