-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHACKING
118 lines (79 loc) · 3.47 KB
/
HACKING
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
TESTING YOUR CHANGES
Normally, Python will prefer installed Py-notify copy, so be careful.
Your changes might seemingly have no effect simply because they are
not executed at all.
You can make Python import Py-notify from your working copy by putting
this in start-up Python file of your code:
import sys
sys.path.insert (0, PATH-TO-YOUR-WORKING-COPY)
Py-notify tests (in the `test' directory) do this automatically, so no
special steps are required to make them test what they need to.
Also, if you run Python interpreter from the working copy directory,
it should import that Py-notify copy, not the installed one.
NEW-STYLE OR OLD-STYLE CLASSES?
Always use new-style classes. Old-style will be even dropped from
Python 3.0.
SLOTS
Don't forget to specify __slots__ = () in case class should not
introduce new slots. Otherwise, instances will be created with a
dictionary and that is absolutely wrong for our case.
CALLING SUPERCLASS METHODS
Please always use super (..., self) idiom to call superclass methods
and don't specify explicit name. This is somewhat slower, but not
error-prone if you change inheritance tree. The only exception might
be multiple inheritance, but we currently don't use that at all.
OBJECT CONSTRUCTORS
Classes directly derived from `object' (and from anything else too)
and overriding __init__() function, must always explicitly call
super (..., self).__init__(). This may seem like an unneeded
requirement, but otherwise multiple inheritance may cease to work.
Consider this:
class C (object):
def __init__(self):
print "C"
# super (C, self).__init__()
class D (object):
def __init__(self):
print "D"
super (D, self).__init__()
class E (C, D):
def __init__(self):
print "E"
super (E, self).__init__()
E ()
This example never prints "D", because class `C' never calls
__init__() method of its superclass. If you uncomment the call,
initialization is back to normal.
So, while we currently don't use multiple inheritance at all, let's do
what is right and not track obscure bugs later. Besides, who knows if
a user of the library uses multiple inheritance for some reason?
WEAK REFERENCES
Properly handling weak references can be particularly difficult and
even more so to test. Especially on implementations like Jython
which, unlike CPython, do not delete objects with no references
immediately. Most problematic situation is where you change reference
field and need that cleanup code is run not more than once:
def __init__(self, some_object):
self.reference = weakref.ref (some_object, self.cleanup)
# Must be called at most once.
def cleanup (self, ignored = None):
self.reference = None
def remove_object (self):
if self.reference is not None:
self.cleanup ()
This code is buggy, because garbage collection can be done after the
test in remove_object(), but before it calls cleanup(). So, cleanup()
will be called twice: first from weak reference becoming dead then
from remove_object().
Proper fix to it looks this way:
def remove_object (self):
reference = self.reference
if reference is not None:
some_object = reference ()
if some_object is not None:
self.cleanup ()
If `some_object' above is not None then `reference' is not dead and
cleanup() cannot have been called. Note that both assignments are
necessary.
However, an easier fix (especially if you can call cleanup() from more
than one place) is to never change reference value.