开发者

Are python object functions singletons?

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?


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!


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)


There seem to be a couple of questions here. I'll try to answer paraphrased versions to make it more clear as to what I'm responding to.

[What sort of overhead is associated with the method attributes on instances compared with classes?]

In python, almost all kinds of container, from local variables to class attributes, are references to other objects. When you define a function, the function value is created once and a variable is initialized with a reference to the function. If a function is in the scope of a class definition, python adds it to the class's attributes, but that's still a reference to the original function.

So there's no real way for there to be any overhead to a function above and beyond the cost of references. Furthermore, attributes in a class are associated only indirectly (through the object's __class__ attribute), so the number of attributes on a class carries no extra cost on the instances.

[Can I modify the methods of one instance of a class? Can I modify the methods of all instances?]

Attributes on an object are in that object's __dict__ (as you've already noticed), and this goes for the attributes for a type (which is also an object). when resolving attributes, python takes a fairly consistent search pattern; first it checks for descriptors in the class, then it checks the instance's __dict__, and then the class __dict__; it picks the first of those that it finds. A fairly obvious example might look like so:

class Foo(object):
    bar = "apples"

my_foo = Foo()
your_foo = Foo()
your_foo.bar = "bananas"
my_foo.__class__.bar = "oranges"
# your_foo.bar is still "bananas"

There's an extra gotcha with methods, though. When you get a class attribute that happens to be a function, python dynamically turns it into an instancemethod, which binds the self parameter to the instance (or not, if you took the attribute from the class). This effect only happens when the attribute was taken from the class, not the instance. If you want to modify an instance with a new method, you will have to take care of turning the function into an instance method yourself. You can use types.MethodType, but it's my opinion that this case should be handled with functools.partial:

import functools
def my_method(self):
    "do something clever"

my_foo.baz = functools.partial(my_method, my_foo)

is there one instantiation of __add__ for each, or just one for all of them?

It was a long way getting to the actual question, But to summarize, the function for your __add__ method occurs only once. there is a single reference to it in the __dict__ of MyObj. There may be many instances of the instancemethod that wraps that function into a method; in fact there is one for each time you do the lookup; python always creates a new one.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜