Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are python object functions singletons?

Tags:

python

In Java, objects are instantiated with a complete copy of the functions from the class linked to them. This is one of the reasons that the Spring Framework has been so successful. Spring helps you cut down on the memory that the Java VM uses if you create many temporary data objects, and other service objects that are served up by Spring as singletons that effectively carry all the functions.

I was just wondering if this true in Python? It seems like it isn't. But that means that if you mess with dict for an object, you are changing all that function for all copies of that class, right?

For example:

class MyObj:
  a = 23

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

  def __add__(self, c):
     return self.b + c

If I create an array of MyObj, is there one instantiation of __add__ for each, or just one for all of them?

like image 611
shigeta Avatar asked Dec 27 '22 14:12

shigeta


2 Answers

There is just one instance of the function, which is stored inside the class. The function gets called with a reference to a class instance as the first argument, which is traditionally called self.

So, here are three equivalent ways to call the .__add__() method function:

>>> x = MyObj(2)
>>> MyObj.__add__(x, 3)  # we pass in a reference to x explicitly
5
>>> x.__add__(3)  # method call implicitly passes reference to x
5
>>> x + 3  # overloaded operator implicitly passes reference to x
5

Also, in Python, the "type signature" of a function is not part of the function's name. The only .__add__() method you get is the one you declared. In Python you would simply write your code to make the one function do all the different jobs you might want it to do. For example, if you wanted .__add__() to convert a string into an integer to make x + "3" return 5, you would do it like so:

class MyObj(object): # see note 0
    a = 23
    def __init__(self, b):
        self.b = b
    def __add__(self, other):
        return self.b + int(other)  # see note 1
    def a_add(self, other):
        return MyObj.a + other

Note 0: It is best to declare your class as inheriting from object. In Python 3.x, your class will inherit from object whether you declare it this way or not, but in Python 2.x you will get an "old-style class" unless you declare it with (object) after the class name.

Note 1: we don't bother to check the type of the other argument; we just try to coerce it to an int value. This is "Duck Typing" in action. Now not only will a string be coerced to an integer, but anything that can successfully be coerced to an int will work.

Sometimes, to make one function do multiple jobs, you may need extra arguments. You can make them optional so that you don't need to specify them every time you call the function. You can read more about this in a Python tutorial; here's one place: http://diveintopython.net/power_of_introspection/optional_arguments.html

Finally, in Python, you need to explicitly use MyObj.a to refer to that class variable a. If you just use a in a member function, you will get the usual name resolution rules, which will look in the global namespace; the object namespace isn't special.

You could also get to a class variable like so:

def a_add(self, other):
    cls = type(self)  # cls will be set to the class
    return cls.a + other

But this will only work if it is a new-style class that inherits from object!

like image 83
steveha Avatar answered Dec 30 '22 03:12

steveha


Java does not create a new function for each instance. In fact, Java has no function objects to begin with.

Python on the other hand has first class functions. Getting a function object from a instance of a class wraps the class function in a simple object called a bound method. When a bound method is invoked, the underlaying class function is called with the instance object as the first argument.

You could say that given this object

class Obj:
    def f(self):
        print self
o = Obj()

when you do

f = o.f # bound method

under the hood this happens

import types
_func = Obj.__dict__['f'] # get the raw function
f = types.MethodType(_func, o, Obj) # create a "bound method"

As you can see, for each instance of Obj both _func and Obj remain the same, the only thing different is the instance used.

Try this:

>>> o.f
<bound method Obj.f of <__main__.Obj instance at 0x0222CAD0>>
>>> Obj.f
<unbound method Obj.f>
>>> o.f() # this is equivalent to:
>>> Obj.f(o)
like image 28
Jochen Ritzel Avatar answered Dec 30 '22 04:12

Jochen Ritzel