Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python, how come we can create class variables that were not defined in class creation?

Let's say we have this simple Python code

class MyClass(object):
    class_var = 1

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

Correct me if I get any of this wrong:

Class_Var is a class variable that is the same for all instances of MyClass object. I_Var is an instance variable that only exists in instances of the MyClass object

foo = MyClass(2)
bar = MyClass(3)

foo.class_var, foo.i_var
## 1, 2
bar.class_var, bar.i_var
## 1, 3

Class variables are also properties of the class itself.

MyClass.class_var ##
## 1

MyClass.I_var should error out, correct?

Does that mean that class variables can be considered like instance variables of the class object itself (since all classes are objects) ?

MyClass.new_attribute = 'foo'
print(hasattr(ObjectCreator, 'new_attribute'))

That should return true. And

print (MyClass.new_attribute)

should return foo.

How come we can create a new class variable that was not defined in the original definition for that class?

Is

MyClass.new_attribute = 'foo'

the exact same thing as creating that class attribute in the original definition?

class MyClass(object):
    class_var = 1
    new_attribute = 'foo'

So we can create new class attributes at runtime? How does that not interfere with the init constructor that creates the class object and has those class variables as instance variables of the class object?

like image 346
user7959439 Avatar asked Dec 08 '25 08:12

user7959439


1 Answers

A class object is just an instance of yet another type, usually type (though you can change this using the metaclass parameter to the class statement).

Like most other instances, you can add arbitrary instance attributes to a class object at any time.

Class attributes and instance attributes are wholly separate; the former are stored on the class object, the latter on instances of the class.

There's nothing particularly special about __init__; it's just another method that, among other things, can attached new attributes to an object. What is special is that __init__ is called automatically when you create a new instance of the class by calling the class. foo = MyClass(2) is equivalent to

foo = MyClass.__new__(MyClass, 2)
foo.__init__(2)

The class statement

class MyClass(object):
    class_var = 1

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

is roughly equivalent to

def my_class_init(self, i_var):
    self.i_var = i_var


MyClass = type('MyClass', (object,), {'class_var': 1, '__init__': my_class_init})

The 3-argument form of type lets you pass a dict that creates class attributes when you first create the class, but you can always assign attributes after the fact as well:

MyClass = type('MyClass', (object,), {})
MyClass.class_var = 1
MyClass.__init__ = my_class_init

Just to blow your mind a little bit more, the call to type can be thought of as

MyClass = type.__new__(type, 'MyClass', (object,), {...})
MyClass.__init__('MyClass', (object,), {...})

though unless you define a custom metaclass (by subclassing type), you never have to think about type itself having __new__ and __init__ methods.

like image 70
chepner Avatar answered Dec 10 '25 21:12

chepner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!