Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion in accessing class attribute without self - python

I know that we can access class attributes with self or the class name.

But I am bit confused why the following also works

class Crazy(object):
  VERSION = 1
  def __init__(self, version=VERSION):
    print version

But this doesn't

class Crazy(object):
  VERSION = 1
  def __init__(self):
    print VERSION
like image 897
mittal Avatar asked Jul 03 '19 11:07

mittal


People also ask

Can a class attribute be used without instance in Python?

while you can access class attributes using an instance it's not safe to do so. In python, the instance of a class is referred to by the keyword self. Using this keyword you can access not only all instance attributes but also the class attributes.

How do you access the attributes of a class in Python?

Attributes of a class can also be accessed using the following built-in methods and functions : getattr() – This function is used to access the attribute of object. hasattr() – This function is used to check if an attribute exist or not. setattr() – This function is used to set an attribute.

Why do Python class methods need self?

self represents the instance of the class. By using the “self” we can access the attributes and methods of the class in python. It binds the attributes with the given arguments. The reason you need to use self. is because Python does not use the @ syntax to refer to instance attributes.

Can class method access self in Python?

Class methods don't need a class instance. They can't access the instance ( self ) but they have access to the class itself via cls . Static methods don't have access to cls or self .


1 Answers

A class definition, i.e. the block inside class ...:, is evaluated like any regular Python code block. Just at the end of the class block, every local name which was defined inside that block becomes part of the class' __dict__. The class syntax is more or less just syntactic sugar over:

Crazy = type('Crazy', (object,), {'VERSION': 1, ...})

See https://docs.python.org/3/library/functions.html#type.

Given this, you'd expect this to work, right?

VERSION = 1

def foo(bar=VERSION):
    print(bar)

(Also see this for more explanation on its behaviour.)

Inside a class block this works exactly the same way, with the only special behaviour that you're not creating global names, but names which will become the class' __dict__.

The relevant paragraph in the documentation is here:

The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved. A class object is then created using the inheritance list for the base classes and the saved local namespace for the attribute dictionary. The class name is bound to this class object in the original local namespace.

https://docs.python.org/3/reference/compound_stmts.html#class-definitions

After the class is defined though, that implicit namespace doesn't exist anymore, so this doesn't work:

def __init__(self):
    print(VERSION)

The scoping rules follow the regular lookup chain:

  • is it a local variable inside the same function?
  • is it a nonlocal variable in a surrounding function?
  • is it a global variable?

None of these are true, since VERSION is just an attribute of Crazy at this point, so is only accessible as Crazy.VERSION or self.VERSION, the latter of which actually also doesn't exist and falls back onto its own lookup chain and traverses up to Crazy.VERSION.

like image 92
deceze Avatar answered Sep 30 '22 10:09

deceze