Why doesn't Python allow referencing a class i

2019-06-09 20:17发布

Python (3 and 2) doesn't allow you to reference a class inside its body (except in methods):

class A:
    static_attribute = A()

This raises a NameError in the second line because 'A' is not defined, while this

class A:
    def method(self):
        return A('argument')

works fine. In other languages, for example Java, the former is no problem and it is advantageous in many situations, like implementing singletons.

Why isn't this possible in Python? What are the reasons for this decision?

EDIT: I edited my other question so it asks only for ways to "circumvent" this restriction, while this questions asks for its motivation / technical details.

5条回答
Deceive 欺骗
2楼-- · 2019-06-09 20:43

Essentially, a class does not exist until its entire definition is compiled in its entirety. This is similar to end blocks that are explicitly written in other languages, and Python utilizes implicit end blocks which are determined by indentation.

查看更多
【Aperson】
3楼-- · 2019-06-09 20:46

The other answers are great at explaining why you can't reference the class by name within the class, but you can use class methods to access the class.

The @classmethod decorator annotes a method that will be passed the class type, instead of the usual class instance (self). This is similar to Java's static method (there's also a @staticmethod decorator, which is a little different).

For a singleton, you can access a class instance to store an object instance (Attributes defined at the class level are the fields defined as static in a Java class):

class A(object):
    instance = None

    @classmethod
    def get_singleton(cls):
        if cls.instance is None:
            print "Creating new instance"
            cls.instance = cls()

        return cls.instance


>>> a1 = A.get_singleton()
Creating new instance

>>> a2 = A.get_singleton()

>>> print a1 is a2
True

You can also use class methods to make java-style "static" methods:

class Name(object):

    def __init__(self, name):
        self.name = name

    @classmethod
    def make_as_victoria(cls):
        return cls("Victoria")

    @classmethod
    def make_as_stephen(cls):
        return cls("Stephen")


>>> victoria = Name.make_as_victoria()
>>> stephen = Name.make_as_stephen()

>>> print victoria.name
Victoria
>>> print stephen.name 
Stephen
查看更多
Luminary・发光体
4楼-- · 2019-06-09 20:53

Python is a dynamically typed language, and executes statements as you import the module. There is no compiled definition of a class object, the object is created by executing the class statement.

Python essentially executes the class body like a function, taking the resulting local namespace to form the body. Thus the following code:

class Foo(object):
    bar = baz

translates roughly to:

def _Foo_body():
    bar = baz
    return locals()
Foo = type('Foo', (object,), _Foo_body())

As a result, the name for the class is not assigned to until the class statement has completed executing. You can't use the name inside the class statement until that statement has completed, in the same way that you can't use a function until the def statement has completed defining it.

This does mean you can dynamically create classes on the fly:

def class_with_base(base_class):
    class Foo(base_class):
        pass
    return Foo

You can store those classes in a list:

classes = [class_with_base(base) for base in list_of_bases]

Now you have a list of classes with no global names referring to them anywhere. Without a global name, I can't rely on such a name existing in a method either; return Foo won't work as there is no Foo global for that to refer to.

Next, Python supports a concept called a metaclass, which produces classes just like a class produces instances. The type() function above is the default metaclass, but you are free to supply your own for a class. A metaclass is free to produce whatever it likes really, even things that are bit classes! As such Python cannot, up front, know what kind of object a class statement will produce and can't make assumptions about what it'll end up binding the name used to. See What is a metaclass in Python?

All this is not something you can do in a statically typed language like Java.

查看更多
我命由我不由天
5楼-- · 2019-06-09 20:57

The answer is "just because".

It has nothing to do with the type system of Python, or it being dynamic. It has to do with the order in which a newly introduced type is initialized.

Some months ago I developed an object system for the language TXR, in which this works:

1> (defstruct foo nil (:static bar (new foo)))
#
2> (new foo)
#S(foo)
3> *2.bar
#S(foo)

Here, bar is a static slot ("class variable") in foo. It is initialized by an expression which constructs a foo.

Why that works can be understood from the function-based API for the instantiation of a new type, where the static class initialization is performed by a function which is passed in. The defstruct macro compiles a call to make-struct-type in which the (new foo) expression ends up in the body of the anonymous function that is passed for the static-initfun argument. This function is called after the type is registered under the foo symbol already.

We could easily patch the C implementation of make_struct_type so that this breaks. The last few lines of that function are:

    sethash(struct_type_hash, name, stype);

    if (super) {
      mpush(stype, mkloc(su->dvtypes, super));
      memcpy(st->stslot, su->stslot, sizeof (val) * su->nstslots);
    }

    call_stinitfun_chain(st, stype);

    return stype;
  }

The call_stinifun_chain does the initialization which ends up evaluating (new foo) and storing it in the bar static slot, and the sethash call is what registers the type under its name.

If we simply reverse the order in which these functions are called, the language and type system will still be the same, and almost everything will work as before. Yet, the (:static bar (new foo)) slot specifier will fail.

I put the calls in that order because I wanted the language-controlled aspects of the type to be as complete as possible before exposing it to the user-definable initializations.

I can't think of any reason for foo not to be known at the time when that struct type is being initialized, let alone a good reason. It is legitimate for static construction to create an instance. For example, we could use it to create a "singleton".

This looks like a bug in Python.

查看更多
啃猪蹄的小仙女
6楼-- · 2019-06-09 21:00

A class statement is executed just like any other statement. Your first example is (roughly) equivalent to

a = A()
A = type('A', (), {'static_attribute': a})

The first line obviously raises a NameError, because A isn't yet bound to anything.

In your second example, A isn't referenced until method is actually called, by which time A does refer to the class.

查看更多
登录 后发表回答