3

I'm trying to understand how classes work a bit better "under the hood" of python.

If I create a class Foo like so

class Foo:
    bar = True

Foo is then directly accessible, such as print(Foo) or print(Foo.bar)

However, if I dynamically create create a class and don't set it to a variable like so

type('Foo',(),{'bar':True})

If done in the interpreter it shows <class '__main__.Foo'>. However, when I try to print Foo it's undefined...NameError: name 'Foo' is not defined

Does this mean that when a class is created the "traditional" way (the first Foo class above), that python automatically sets a variable for the class of the same name? Sort of like this

# I realize this is not valid, just to convey the idea
Foo = class Foo:
    bar = True

If so, then why doesn't python also create a variable named Foo set to class Foo when using type() to create it?

7
  • type() does not create a class Commented Jan 27, 2018 at 23:14
  • 1
    @roganjosh then why does type('Foo',(),{'bar':True}) result in <class '__main__.Foo'>? Commented Jan 27, 2018 at 23:15
  • if you are familiar with anonymous inner classes in java, then it is the same thing, if you don't reference the newly created object (the class in this case) it is unfortunately lost. Commented Jan 27, 2018 at 23:45
  • 1
    @roganjosh type can and absolutely does create classes, though it it not its most common use case. Commented Jan 27, 2018 at 23:57
  • 1
    @user1104854 Yes, the class definition is equivalent to Foo = type('Foo',(),{'bar':True}). With regards to why it doesn't create the class in your namespace. type is just a function, and you wouldn't expect any other function to create new variable in your namespace, so why should type be special? Commented Jan 27, 2018 at 23:58

4 Answers 4

1

let's compare your problem with function statements and lambdas (because they play the same role here), consider this function f :

def f ():
  return 1

the above snippet of code is not an expression at all, it is a python statement that creates a function named f returning 1 upon calling it.

let's now do the same thing, but in a different way :

f = lambda : 1

the above snippet of code is a python expression (an assignment) that assigns the symbol f to the lambda expression (which is our function) lambda : 1. if we didn't do the assignment, the lambda expression would be lost, it is the same as writing >>> 1 in the python REPL and then trying after that to reference it.

Sign up to request clarification or add additional context in comments.

4 Comments

I see what you mean, but your example is based on returning a literal, not a variable. I'm not disagreeing, because frankly I don't know the details, but using type('X', (object,), dict(a=1)) returns a class with name Foo. Does that make a difference at all? I guess my confusion lies as to why using the traditional class Foo:... creates a class sets it to a variable, but just using type will create a class but let it get picked up by the garbage collector.
consider this function call type('X', (object,), dict(a=1)) as creating a new object (in thise case, the object represents a newly created type which we named X), if we don't give this object a name (a reference to it), it is simply lost.
Thanks. So basically we're given direct access to python's way of creating classes, but technically requires more involvement from us because we're now required to set it to a variable? Just seems weird that I can create a class called Bar, but all references to it must be using Foo, using Foo = type('Bar',(),{})
type('Bar',(),{}) defines a new type whose name property is set to 'Bar', in your example Foo is just a name (a variable) pointing to the newly created type whose name again is 'Bar'.
1

Using type with 3 argument is analogous to using the lambda to create a function. Without assignment the evaluated expression is garbage collected.

However, just you can still create an instance of the class, just like you can immediately call a lambda function.

>>> lambda x: True
<function <lambda> at 0x0000022FF95AB598>
>>> type('Test', (), {'x': True})
<class '__main__.Test'>

You can also create an instance of the class, just like you can immediately call a function

>>> t = type('Test', (), {'x': True})()
>>> t.x
True
>>> type('Test2', (), {'y': 123})().y
123
>>> (lambda x: True)(1000)  # any input returns True
True

Comments

0

From documentation

class type(name, bases, dict)

With three arguments, return a new type object. This is essentially a dynamic form of the class statement. 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. For example, the following two statements create identical type objects:

class X(object):
    a = 1
X = type('X', (object,), dict(a=1))

So yes, I think you have the right idea. type() does create a class but a dynamic form.

2 Comments

the type X is created upon calling the function type, not when assigning the result of the function type to some name (in this example X)
Yes, sorry my bad
0

I think you're making this too complicated. If you don't assign a value / object to a symbol, it is always "lost". Doesn't matter if the value / object is a class or something else. Example:

x = 2 + 2

That assigns the value 4 to the symbol x. Compare to:

2 + 2

The operation is carried out but the result 4 isn't assigned to a symbol.

Exact situation you have with classes.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.