Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between self.arg = arg and self.__dict__['arg'] = arg in Python

When I make a class definition I always go

Class A(object):
    def __init__(self, arg):
        self.arg = arg
    def print_arg(self):
        print(self.arg)

a = A('hello')

print a.arg

'hello'

But what I found in line 133 and 134 of https://github.com/Pylons/webob/blob/master/src/webob/request.py made me think what is the difference between what I did in Class A with:

Class B(object):
    def __init__(self, arg):
        self.__dict__['arg'] = arg
    def print_arg(self):
            print(self.arg)

b = B('goodbye')

print b.arg

'goodbye'

like image 771
Rodrigo E. Principe Avatar asked Dec 29 '17 15:12

Rodrigo E. Principe


People also ask

What is the self argument in Python?

The self parameter is a reference to the current instance of the class, and is used to access variables that belongs to the class.

Is self in Python the same as this?

Technically both self and this are used for the same thing. They are used to access the variable associated with the current instance. Only difference is, you have to include self explicitly as first parameter to an instance method in Python, whereas this is not the case with Java.

What is self and init in Python?

The self in keyword in Python is used to all the instances in a class. By using the self keyword, one can easily access all the instances defined within a class, including its methods and attributes. init. __init__ is one of the reserved methods in Python. In object oriented programming, it is known as a constructor.

What does self mean in Python function?

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.


2 Answers

There are several major implications:

  1. Using self.__dict__ to add an attribute circumvents __setattr__, which might be overloaded with a certain behaviour that you might want to avoid in some places.

    In [15]: class Test(object):
        ...:     def __init__(self, a, b):
        ...:         self.a = a
        ...:         self.__dict__['b'] = b
        ...:     def __setattr__(self, name, value):
        ...:         print('Setting attribute "{}" to {}'.format(name, value))
        ...:         super(Test, self).__setattr__(name, value)
        ...:               
    
    In [16]: t = Test(1, 2)
    Setting attribute "a" to 1
    

    You can see that nothing was printed for attribute b.

  2. It is less flexible in some cases

    In [9]: class WithSlots(object):
       ...:     __slots__ = ('a',)
       ...:     def __init__(self, a):
       ...:         self.__dict__['a'] = a
       ...:         
    
    In [10]: instance = WithSlots(1)
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-10-c717fcc835a7> in <module>()
    ----> 1 instance = WithSlots(1)
    
    <ipython-input-9-2d23b670e0fc> in __init__(self, a)
          2     __slots__ = ('a',)
          3     def __init__(self, a):
    ----> 4         self.__dict__['a'] = a
          5 
    
    AttributeError: 'WithSlots' object has no attribute '__dict__'
    
    In [11]: class WithSlots(object):
        ...:     __slots__ = ('a',)
        ...:     def __init__(self, a):
        ...:         self.a = a
        ...:         
        ...:         
    
    In [12]: instance = WithSlots(1) # -> works fine
    
  3. You can't do that outside a class definition.

like image 185
Eli Korvigo Avatar answered Sep 27 '22 21:09

Eli Korvigo


The overall purpose is to circumvent the default manner in which Python sets a variable. A particular use-case for this technique is to hide property values. Compare the following two classes:

class Exposed:
    def __init__(self, x):
        self._x = x
    @property
    def x(self):
        rerurn self._x

class Hidden:
    def __init__(self, x):
        self.__dict__['x'] = x
    @property
    def x(self):
        return self.__dict__['x']

Both classes define a read-only property x. However, the first one ends up with an extra _x attribute that is directly modifiable by the user, while the second does not. While nothing is truly private in Python, the second class creates a much better approximation of a true read-only value, and it doesn't proliferate unnecessarily visible attributes.

like image 27
Mad Physicist Avatar answered Sep 27 '22 21:09

Mad Physicist