[Tutor] method conflict?

Steven D'Aprano steve at pearwood.info
Fri Jul 3 04:00:54 CEST 2015


On Thu, Jul 02, 2015 at 05:39:12PM -0700, Jim Mooney Py3.4.3winXP wrote:

> Okay, it appears the method in a class has its own ID, but all
> instantiations of that method have identical IDs.

I'm not sure what you mean by this. *All* objects have their own ID, and 
instantiations of methods *don't* have identical IDs, although it may 
appear so at first glance (see further below).

py> class C:
...     def method(self):
...             return 23
...
py> c = C()
py> x = c.method  # grab a reference to the method
py> y = c.method  # and another one
py> print(id(x), id(y))
3082036364 3082100428

*But* that's actually an implementation detail. It just so happens that 
the way CPython currently works, each time you look up a method you get 
a new instance. Technical details are technical, and involve the 
descriptor protocol, which you don't need to understand. I'm happy to go 
into detail if you like, but for now let's just skip over it and just 
call it "magic".

There's nothing in the language specification (that I know of) which 
*promises* that x and y must be separate objects with different IDs.

The underlying function object, retrieved directly from the class 
__dict__, *is* guaranteed to always give the same object:

py> a = C.__dict__['method']
py> b = C.__dict__['method']
py> print(id(a), id(b))
3083756356 3083756356

That's the way dictionaries work.


> But what happens if we
> have a huge number of instantiations trying to access the identical method
> at the same time?

What of it? I don't understand what you think the potential problem is. 
Regardless of whether you access the method wrapper, or the underlying 
function, calling it from multiple places is allowed. Each function call 
is independent of the others.

Naturally, the *action* of the function may not be safe to call twice. 
(It might, say, try to delete the same file twice, and the second time 
you get an error because the file no longer exists.) But the function 
calling infrastructure is safe to do multiple times:

len(a)  # Safe to call it once.
len(b)  # and still safe to call it twice
LEN = len; LEN(c)  # even if you use a different name




> class MyClass:
>     def setdata(self, data):
>         self.data = data
>     def getdata(self):
>         print(self.data)
> 
> 
> >>> MyClass.setdata
> <function MyClass.setdata at 0x026CACD8>

In Python 3, looking up MyClass.setdata returns the underlying function 
object. But in Python 2, it actually returns an "unbound method" object, 
which is a wrapper around the underlying function. I mention this only 
for completion, in practice it makes little or no difference.


> >>> id(MyClass.setdata)
> 40676568
> >>> f = MyClass()
> >>> g = MyClass()
> >>> id(f.setdata)
> 43576616
> >>> id(g.setdata)
> 43576616
> >>> d = MyClass()
> >>> id(d.setdata)
> 43576616

You are misinterpreting what you are seeing. Python is free to reuse 
IDs. CPython does re-use them, IronPython and Jython do not. 

When you run this line of code:

    id(f.setdata)

at least five things happen:

- Python does an attribute search for "setdata" starting with f;

- it finds the *function* f inside the class __dict__;

- because functions are descriptors ("magic"), Python creates a *method* 
  wrapper around the function, and returns that;

- the method gets passed to the id() function, which determines the ID 
  (in this case 43576616) and returns that;

- the method object is then garbage collected, which frees up the ID to 
  be re-used the next time you create a method.


If you did the same thing in (say) Jython, you would get very different 
results, something like this:

 >>> id(f.setdata)
 43
 >>> id(g.setdata)
 44
 >>> id(d.setdata)
 45

(the actual IDs may vary).

But *most of the time* none of this has the least bit of importance to 
your code. Whether you get the same method object or different method 
objects is more or less irrelevant to how your code works. The exception 
is when you start looking at introspection functions like id(), or 
testing for object identity ("is this the same object?") using `is`.

 

-- 
Steve


More information about the Tutor mailing list