-
Notifications
You must be signed in to change notification settings - Fork 345
/
magicmethods.py
executable file
·204 lines (162 loc) · 6.14 KB
/
magicmethods.py
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"""
magicmethods.py
Want to try out the examples? Don't want to type them up yourself? Never worry,
magicmethods.py is a convenient Python module with all the class definitions
for the examples in the magic methods guide in it.
"""
# FileObject class, demonstrating __init__ and __del__
from os.path import join
class FileObject:
"""Wrapper for file objects to make sure the file gets closed on deletion."""
def __init__(self, filepath="~", filename="sample.txt"):
# open a file filename in filepath in read and write mode
self.file = open(join(filepath, filename), "r+")
def __del__(self):
self.file.close()
del self.file
# Word class, demonstrating __new__, comparisons
class Word(str):
"""Class for words, defining comparison based on word length."""
def __new__(cls, word):
# Note that we have to use __new__. This is because str is an immutable
# type, so we have to initialize it early (at creation)
if " " in word:
print "Value contains spaces. Truncating to first space."
word = word[:word.index(" ")] # Word is now all chars before first space
return str.__new__(cls, word)
def __gt__(self, other):
return len(self) > len(other)
def __lt__(self, other):
return len(self) < len(other)
def __ge__(self, other):
return len(self) >= len(other)
def __le__(self, other):
return len(self) <= len(other)
# AccessCounter class, demonstrating __setattr__, __getattr__, and __delattr__
class AccessCounter:
"""A class that contains a value and implements an access counter.
The counter increments each time the value is changed."""
def __init__(self, val):
self.__dict__["counter"] = 0
self.__dict__["value"] = val
def __setattr__(self, name, value):
if name == "value":
self.__dict__["counter"] += 1
self.__dict__["value"] = value
def __delattr__(self, name):
if name == "value":
self.__dict__["counter"] += 1
del self.__dict__["value"]
# FunctionalList class, demonstrating __len__, __getitem__, __setitem__, __delitem__,
# __iter__, and __reversed__
class FunctionalList:
"""A class wrapping a list with some extra functional magic, like head,
tail, init, last, drop, and take."""
def __init__(self, values=None):
if values is None:
self.values = []
else:
self.values = values
def __len__(self):
return len(self.values)
def __getitem__(self, key):
# if key is of invalid type or value, the list values will raise the error
return self.values[key]
def __setitem__(self, key, value):
self.values[key] = value
def __delitem__(self, key):
del self.values[key]
def __iter__(self):
return iter(self.values)
def __reversed__(self):
return reversed(self.values)
def append(self, value):
self.values.append(value)
def head(self):
# get the first element
return self.values[0]
def tail(self):
# get all elements after the first
return self.values[1:]
def init(self):
# get elements up to the last
return self.values[:-1]
def last(self):
# get last element
return self.values[-1]
def drop(self, n):
# get all elements except first n
return self.values[n:]
def take(self, n):
# get first n elements
return self.values[:n]
# Entity class demonstrating __call__
class Entity:
"""Class to represent an entity. Callable to update the entity"s position."""
def __init__(self, size, x, y):
self.x, self.y = x, y
self.size = size
def __call__(self, x, y):
"""Change the position of the entity."""
self.x, self.y = x, y
# snip...
# Wrapper class to close an object in a with statement
class Closer:
"""A context manager to automatically close an object with a close method
in a with statement."""
def __init__(self, obj):
self.obj = obj
def __enter__(self):
return self.obj # bound to target
def __exit__(self, exception_type, exception_val, trace):
try:
self.obj.close()
except AttributeError: # obj isn"t closable
print "Not closable."
return True # exception handled successfully
# Classes to represent descriptors and their use
class Meter(object):
"""Descriptor for a meter."""
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Foot(object):
"""Descriptor for a foot."""
def __get__(self, instance, owner):
return instance.meter * 3.2808
def __set__(self, instance, value):
instance.meter = float(value) / 3.2808
class Distance(object):
"""Class to represent distance holding two descriptors for feet and
meters."""
meter = Meter()
foot = Foot()
# Class to demo fine-tuning pickling
import time
class Slate:
"""Class to store a string and a changelog, and forget its value when
pickled."""
def __init__(self, value):
self.value = value
self.last_change = time.asctime()
self.history = {}
def change(self, new_value):
# Change the value. Commit last value to history
self.history[self.last_change] = self.value
self.value = new_value
self.last_change = time.asctime()
def print_changes(self):
print "Changelog for Slate object:"
for k, v in self.history.items():
print "%s\t %s" % (k, v)
def __getstate__(self):
# Deliberately do not return self.value or self.last_change.
# We want to have a "blank slate" when we unpickle.
return self.history
def __setstate__(self, state):
# Make self.history = state and last_change and value undefined
self.history = state
self.value, self.last_change = None, None