In Python 3 unbound methods were removed:
the "unbound method" type has entirely disappeared -- a method, until and unless it's bound, is just a function, without the weird "type-checking" unbound methods used to perform.
Example in Python 3
Python 3.2.3 (default, Oct 19 2012, 19:53:16) >>> class A(): ... def func(self): ... print('A.func called') ... >>> def func2(self): ... print('func2 called') ... >>> A.func <function func at 0x7f58342fdc00> >>> A.__dict__['func'] <function func at 0x7f58342fdc00> >>> A.__dict__['func'] is A.func # the same object? (functions are objects) True >>> a = A() # create an instance of class A >>> a.func is A.func # looks like these are different things! False >>> a.func # try to get the `func` method <bound method A.func of <__main__.A object at 0x7f58342f2b10>> >>> a.func is a.func # even stranger! False >>> f1 = a.func >>> f2 = a.func >>> f1, f2 (<bound method A.func of <__main__.A object at 0x7f58342f2b10>>, <bound method A.func of <__main__.A object at 0x7f58342f2b10>>) >>> id(f1), id(f2) # these are clearly two different objects
Some explanations on descriptors
How could that be that `a.f is not a.f`?
Each time you try to access a method using a dot (`a.func`) Python will look into `__dict__` dictionary of object `a` for an attribute name `func` [1]. If it's not found there (in case of a method it's usually not there), Python will look into class's `__dict__` for the attribute `func`. `def` statement inside a class definition puts a function into class's `__dict__`, so it's there. So Python found the attribute `func` of function type. Then Python will see if the attribute is a descriptor. All functions on Python are objects implementing descriptor protocol - they are non-data descriptors, having `__get__` method. So when you access `a.func` Python will not return `func` at is is, but will return `A.__dict__['func'].__get__(a, A)` according to descriptor protocol. This means that `A.func is not a.func`, because `a.func` is a wrapper around `A.func`, and that `a.func is not a.func`, because each access to the method returns a new wrapper.
[1] This is true for methods, they being non-data descriptors:
Data and non-data descriptors differ in how overrides are calculated with respect to entries in an instance’s dictionary. If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance’s dictionary has an entry with the same name as a non-data descriptor, the dictionary entry takes precedence.More info here: http://docs.python.org/3/howto/descriptor.html#descriptor-protocol
Monkey-patching methods
The method access thing implies more issues. It means that if you want to replace a method with another implementation on runtime (aka "monkey-patching"), you can do it in two ways. You can assign `A.func` another function and this will call your new `func` implementation in all existing and new instances of class `A` if you are access it via dot (`self.func()` or `obj.func()`, i.e. you haven't stored reference to the old implementation somewhere) or you haven't monkey-patched method only of that particular instance.
>>> A.func.__get__ # every function is a descriptor, having `__get__` method <method-wrapper '__get__' of function object at 0x7f58342fdc00> >>> func2.__get__ <method-wrapper '__get__' of function object at 0x7f58342f3af0> >>> a2 = A() >>> a.func() A.func called >>> a2.func() A.func called >>> a2.func = func2.__get__(a2, A) # replace method of a particular instance >>> a2.func() # new implementation func2 called >>> a.func() # other instance have default old implementation A.func called >>> def func3(self): ... print('func3 called') ... >>> A.func = func3 # replace default implementation >>> a.func() # new implementation func3 called >>> a2.func() # but this instance was patched separately - has its own implementation func2 called
The code was highlighted using pygments
No comments:
Post a Comment