@@ -44,6 +44,7 @@ So, here we go...
44
44
+ [ ▶ The sticky output function] ( #-the-sticky-output-function )
45
45
+ [ ▶ The chicken-egg problem * ] ( #-the-chicken-egg-problem- )
46
46
+ [ ▶ Subclass relationships] ( #-subclass-relationships )
47
+ + [ ▶ Methods equality and identity] ( #-methods-equality-and-identity )
47
48
+ [ ▶ All-true-ation * ] ( #-all-true-ation- )
48
49
+ [ ▶ The surprising comma] ( #-the-surprising-comma )
49
50
+ [ ▶ 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`
1122
1123
1123
1124
-- -
1124
1125
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
+
1125
1204
# ## ▶ All-true-ation *
1126
1205
1127
1206
< !-- Example ID : dfe6d845- e452- 48fe - a2da- 0ed3869a8042 -- >
0 commit comments