Skip to content

Commit f13b98e

Browse files
committed
Add section on methods equality and identity
Closes: satwikkansal#233
1 parent 902ca17 commit f13b98e

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

CONTRIBUTORS.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Following are the wonderful people (in no specific order) who have contributed t
2121
| Ghost account | N/A | [#96](https://github.com/satwikkansal/wtfpython/issues/96)
2222
| koddo | [koddo](https://github.com/koddo) | [#80](https://github.com/satwikkansal/wtfpython/issues/80), [#73](https://github.com/satwikkansal/wtfpython/issues/73) |
2323
| jab | [jab](https://github.com/jab) | [#77](https://github.com/satwikkansal/wtfpython/issues/77) |
24-
| Jongy | [Jongy](https://github.com/Jongy) | [#208](https://github.com/satwikkansal/wtfpython/issues/208), [#210](https://github.com/satwikkansal/wtfpython/issues/210) |
24+
| Jongy | [Jongy](https://github.com/Jongy) | [#208](https://github.com/satwikkansal/wtfpython/issues/208), [#210](https://github.com/satwikkansal/wtfpython/issues/210), [#233](https://github.com/satwikkansal/wtfpython/issues/233) |
2525
| Diptangsu Goswami | [diptangsu](https://github.com/diptangsu) | [#193](https://github.com/satwikkansal/wtfpython/issues/193) |
2626

2727
---

README.md

+79
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ So, here we go...
4444
+ [▶ The sticky output function](#-the-sticky-output-function)
4545
+ [▶ The chicken-egg problem *](#-the-chicken-egg-problem-)
4646
+ [▶ Subclass relationships](#-subclass-relationships)
47+
+ [▶ Methods equality and identity](#-methods-equality-and-identity)
4748
+ [▶ All-true-ation *](#-all-true-ation-)
4849
+ [▶ The surprising comma](#-the-surprising-comma)
4950
+ [▶ Strings and the backslashes](#-strings-and-the-backslashes)
@@ -1122,6 +1123,84 @@ The Subclass relationships were expected to be transitive, right? (i.e., if `A`
11221123
11231124
---
11241125
1126+
### ▶ Methods equality and identity
1127+
<!-- Example ID: 94802911-48fe-4242-defa-728ae893fa32 --->
1128+
1129+
1.
1130+
```py
1131+
class SomeClass:
1132+
def method(self):
1133+
pass
1134+
1135+
@classmethod
1136+
def classm(cls):
1137+
pass
1138+
1139+
@staticmethod
1140+
def staticm():
1141+
pass
1142+
```
1143+
1144+
**Output:**
1145+
```py
1146+
>>> print(SomeClass.method is SomeClass.method)
1147+
True
1148+
>>> print(SomeClass.classm is SomeClass.classm)
1149+
False
1150+
>>> print(SomeClass.classm == SomeClass.classm)
1151+
True
1152+
>>> print(SomeClass.staticm is SomeClass.staticm)
1153+
True
1154+
```
1155+
1156+
Accessing `classm` twice, we get an equal object, but not the *same* one? Let's see what happens
1157+
with instances of `SomeClass`:
1158+
1159+
2.
1160+
```py
1161+
o1 = SomeClass()
1162+
o2 = SomeClass()
1163+
```
1164+
1165+
**Output:**
1166+
```py
1167+
>>> print(o1.method == o2.method)
1168+
False
1169+
>>> print(o1.method == o1.method)
1170+
True
1171+
>>> print(o1.method is o1.method)
1172+
False
1173+
>>> print(o1.classm is o1.classm)
1174+
False
1175+
>>> print(o1.classm == o1.classm == o2.classm == SomeClass.classm)
1176+
True
1177+
>>> print(o1.staticm is o1.staticm is o2.staticm is SomeClass.staticm)
1178+
True
1179+
```
1180+
1181+
Accessing` classm` or `method` twice, creates equal but not *same* objects for the same instance of `SomeClass`.
1182+
1183+
#### 💡 Explanation
1184+
* Functions are [descriptors](https://docs.python.org/3/howto/descriptor.html). Whenever a function is accessed as an
1185+
attribute, the descriptor is invoked, creating a method object which "binds" the function with the object owning the
1186+
attribute. If called, the method calls the function, implicitly passing the bound object as the first argument
1187+
(this is how we get `self` as the first argument, despite not passing it explicitly).
1188+
* Accessing the attribute multiple times creates multiple method objects! Therefore `o1.method is o2.method` is
1189+
never truthy. Accessing functions as class attributes (as opposed to instance) does not create methods, however; so
1190+
`SomeClass.method is SomeClass.method` is truthy.
1191+
* `classmethod` transforms functions into class methods. Class methods are descriptors that, when accessed, create
1192+
a method object which binds the *class* (type) of the object, instead of the object itself.
1193+
* Unlike functions, `classmethod`s will create a method also when accessed as class attributes (in which case they
1194+
bind the class, not to the type of it). So `SomeClass.classm is SomeClass.classm` is falsy.
1195+
* A method object compares equal when both the functions are equal, and the bound objects are the same. So
1196+
`o1.method == o1.method` is truthy, although not the same object in memory.
1197+
* `staticmethod` transforms functions into a "no-op" descriptor, which returns the function as-is. No method
1198+
objects are ever created, so comparison with `is` is truthy.
1199+
* Having to create new "method" objects every time Python calls instance methods affected performance badly.
1200+
CPython 3.7 [solved it](https://bugs.python.org/issue26110) by introducing new opcodes that deal with calling methods
1201+
without creating the temporary method objects. This is used only when the accessed function is actually called, so the
1202+
snippets here are not affected, and still generate methods :)
1203+
11251204
### ▶ All-true-ation *
11261205
11271206
<!-- Example ID: dfe6d845-e452-48fe-a2da-0ed3869a8042 -->

0 commit comments

Comments
 (0)