Skip to content Skip to sidebar Skip to footer

Dynamically Defining Instance Fields In Python Classes

I am new to Python having come from mainly Java programming. I am currently pondering over how classes in Python are instantiated. I understand that __init__(): is like the constr

Solution 1:

I understand that __init__(): is like the constructor in Java.

To be more precise, in Python __new__ is the constructor method, __init__ is the initializer. When you do SomeClass('foo', bar='baz'), the type.__call__ method basically does:

def__call__(cls, *args, **kwargs):
    instance = cls.__new__(*args, **kwargs)
    instance.__init__(*args, **kwargs)
    return instance

Generally, most classes will define an __init__ if necessary, while __new__ is more commonly used for immutable objects.

However, sometimes python classes do not have an init() method which in this case I assume there is a default constructor just like in Java?

I'm not sure about old-style classes, but this is the case for new-style ones:

>>>> object.__init__
<slot wrapper '__init__' of 'object' objects>

If no explicit __init__ is defined, the default will be called.

So to be clear, my question is in Python can we dynamically define new fields to a class during runtime like in this example

Yes.

>>>classA(object):...def__init__(self):...        self.one_attribute = 'one'...defadd_attr(self):...        self.new_attribute = 'new'...>>>a = A()>>>a.one_attribute
'one'
>>>a.new_attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'new_attribute'
>>>a.add_attr()>>>a.new_attribute
'new'

Attributes can be added to an instance at any time:

>>>a.third_attribute = 'three'>>>a.third_attribute
'three'

However, it's possible to restrict the instance attributes that can be added through the class attribute __slots__:

>>>classB(object):...    __slots__ = ['only_one_attribute']...def__init__(self):...        self.only_one_attribute = 'one'...defadd_attr(self):...        self.another_attribute = 'two'...>>>b = B()>>>b.add_attr()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in add_attr
AttributeError: 'B' object has no attribute 'another_attribute'

(It's probably important to note that __slots__ is primarily intended as a memory optimization - by not requiring an object have a dictionary for storing attributes - rather than as a form of run-time modification prevention.)

Solution 2:

Attributes of Python objects are generally stored in a dictionary, just like the ones you create with {}. Since you can add new items to a dictionary at any time, you can add attributes to an object at any time. And since any type of object can be stored in a dictionary without previous declaration of type, any type of object can be stored as an attribute of an object.

In short, my_object.abc = 42 is (often) just a shorthand for my_object.__dict__["abc"] = 42.

It is possible to define objects without a __dict__ by defining the __slots__ attribute, or to override certain special methods and store attributes in some other way, though most of the time you shouldn't do that.

Solution 3:

This answer pertains to new-style Python classes, which subclass object. New-style classes were added in 2.2, and they're the only kind of class available in PY3.

>>> printobject.__doc__
The most base type

The class itself is an instance of a metaclass, which is usually type:

>>> print type.__doc__
type(object) -> the object'stypetype(name, bases, dict) -> a new type

Per the above docstring, you can instantiate the metaclass directly to create a class:

>>>Test = type('Test', (object,), {'__doc__': 'Test class'})>>>isinstance(Test, type)
True
>>>issubclass(Test, object)
True
>>>print Test.__doc__
Test class

Calling a class is handled by the metaclass __call__ method, e.g. type.__call__. This in turn calls the class __new__ constructor (typically inherited) with the call arguments in order to create an instance. Then it calls __init__, which may set instance attributes.

Most objects have a __dict__ that allows setting and deleting attributes dynamically, such as self.value = 10 or del self.value. It's generally bad form to modify an object's __dict__ directly, and actually disallowed for a class (i.e. a class dict is wrapped to disable direct modification). If you need to access an attribute dynamically, use the built-in functionsgetattr, setattr, and delattr.

The data model defines the following special methods for customizing attribute access: __getattribute__, __getattr__, __setattr__, and __delattr__. A class can also define the descriptor protocol methods __get__, __set__, and __delete__ to determine how its instances behave as attributes. Refer to the descriptor guide.

When looking up an attribute, object.__getattribute__ first searches the object's class and base classes using the C3 method resolution order of the class:

>>> Test.__mro__
(<class'__main__.Test'>, <type'object'>)

Note that a data descriptor defined in the class (e.g. a property or a member for a slot) takes precedence over the instance dict. On the other hand, a non-data descriptor (e.g. a function) or a non-descriptor class attribute can be shadowed by an instance attribute. For example:

>>>Test.x = property(lambda self: 10)>>>inspect.isdatadescriptor(Test.x)
True
>>>t = Test()>>>t.x
10
>>>t.__dict__['x'] = 0>>>t.__dict__
{'x': 0}
>>>t.x
10

>>>Test.y = 'class string'>>>inspect.isdatadescriptor(Test.y)
False
>>>t.y = 'instance string'>>>t.y
'instance string'

Use super to proxy attribute access for the next class in the method resolution order. For example:

>>>classTest2(Test):...    x = property(lambda self: 20)...>>>t2 = Test2()>>>t2.x
20
>>>super(Test2, t2).x
10

Post a Comment for "Dynamically Defining Instance Fields In Python Classes"