Paul Orland
Paul Orland

Reputation: 552

python static fields of own type

I notice python won't let you add an instance of a class to itself as a static member at class definition.

>>> class Foo:
...     A = Foo()
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in Foo
NameError: name 'Foo' is not defined

However either of the following work:

>>> class Foo:
...     pass
... 
>>> class Foo:
...     A = Foo()
... 
>>> Foo.A
<__main__.Foo instance at 0x100854440>

or

>>> class Foo:
...     pass
... 
>>> Foo.A = Foo()
>>> 
>>> Foo.A
<__main__.Foo instance at 0x105843440>

I can't find any enlightening code examples or explanations. Why does python treat the first case differently? Where is A going in each of the two subsequent cases?

Upvotes: 3

Views: 896

Answers (4)

Perkins
Perkins

Reputation: 2517

At class definition time, the class itself doesn't exist yet, the contents of the indented block are executed and any names are resolved and functions are called. At that point, the name of the class, the base classes and a dictionary containing the names and values to store in the class are passed off to the metaclass which creates the actual class and binds it to the name specified. You can create a 'static' instance of a class at class initialization time via the use of a metaclass. This executes the code inside the class definition, passes the name, bases and dict to a metaclass function, which creates the class and creates an instance of the class inside it then binds it to the name. Consider:

def staticInstanceMetaclass(name, bases, dict_):
    ret=type(name, bases, dict_)
    for i in dict_:
      if dict_[i]=='STATICINSTANCE':
        setattr(ret,i,ret())
    return ret

class ClassWithStaticInstance(object):
    __metaclass__=staticInstanceMetaclass
    myStaticInstance='STATICINSTANCE'
    myFirstVar=5

print ClassWithStaticInstance.myStaticInstance

Note that in the above example, dict_ contains

{'myStaticInstance': 'STATICINSTANCE', '__module__': '__main__', '__metaclass__': <function staticInstanceMetaclass at 0x1213668>, 'myFirstVar': 5}

That's about as close as you get to pre-creating static instances in python. For more info on metaclasses, and how the affect class creation, see this question.

Upvotes: 0

Miklos Aubert
Miklos Aubert

Reputation: 4585

(EDIT : look at this question for examples of self-referencing classes in Python.)

I think this will spell it out :

>>> class Test:
...     a = 3
...
>>> class Test:
...     m = Test()
...
>>>
>>> t = Test()
>>> t.m
<__main__.Test object at 0x01E73690>
>>> t.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'a'
>>> t.m.a
3

Amazingly, that gives you two different classes with the same name. But I guess the first class is "lost" and only accessible through the second one.

Upvotes: 3

glglgl
glglgl

Reputation: 91119

Creating a class works by first evaluating its body and then, at the end, creating a class object.

So

class Foo:
    A = Foo() # here class Foo doesn't exist yet
# but here it exists.

(Here is how creating a class object works.)

Your 2nd example creates a class and then creates another class with the same name, not related to the first one in any way. Thus,

>>> isinstance(Foo.A, Foo)
False

Your 3rd example uses the class after its creation to extend it.

So,

>>> isinstance(Foo.A, Foo)
True

Why does python treat the first case differently?

Because the identifier doesn't exist yet.

Where is A going in each of the two subsequent cases?

What does that mean, where is it going?

Upvotes: 1

TerryA
TerryA

Reputation: 60014

Your first example doesn't work because you haven't created the class Foo yet. You're in the process of doing so (hence the NameError)

Your second example works because you have a class called Foo(). You override it, but you still keep a copy of it. Take a look at this:

>>> class Foo:
...     def __init__(self):
...             print 'hi'
... 
>>> class Foo:
...     A = Foo()
... 
hi
>>> Foo.A
<__main__.Foo instance at 0x101019950>
>>> Foo.A.__init__
<bound method Foo.__init__ of <__main__.Foo instance at 0x101019950>>

A is an attribute that has the value of a class you overrode.

As for your third example, you're simply making an attribute of a class that is an instance of the class.

Upvotes: 6

Related Questions