fix/update for credits script and bad formatting in sphinx docs.
[blender.git] / doc / python_api / rst / info_best_practice.rst
1 *************
2 Best Practice
3 *************
4
5 When writing you're own scripts python is great for new developers to pick up and become productive, but you can also pick up odd habits or at least write scripts that are not easy for others to understand.
6
7 For you're own work this is of course fine, but if you want to collaborate with others or have you're work included with blender there are practices we encourage.
8
9
10 Style Conventions
11 =================
12
13 For Blender 2.5 we have chosen to follow python suggested style guide to avoid mixing styles amongst our own scripts and make it easier to use python scripts from other projects.
14
15 Using our style guide for your own scripts makes it easier if you eventually want to contribute them to blender.
16
17 This style guide is known as pep8 and can be found `here <http://www.python.org/dev/peps/pep-0008>`_
18
19 A brief listing of pep8 criteria.
20
21 * camel caps for class names: MyClass
22
23 * all lower case underscore separated module names: my_module
24
25 * indentation of 4 spaces (no tabs)
26
27 * spaces around operators. ``1 + 1``, not ``1+1``
28
29 * only use explicit imports, (no importing '*')
30
31 * don't use single line: ``if val: body``, separate onto 2 lines instead.
32
33
34 As well as pep8 we have other conventions used for blender python scripts.
35
36 * Use single quotes for enums, and double quotes for strings.
37
38   Both are of course strings but in our internal API enums are unique items from a limited set. eg.
39
40   .. code-block:: python
41
42      bpy.context.scene.render.file_format = 'PNG'
43      bpy.context.scene.render.filepath = "//render_out"
44
45 * pep8 also defines that lines should not exceed 79 characters, we felt this is too restrictive so this is optional per script.
46
47 Periodically we run checks for pep8 compliance on blender scripts, for scripts to be included in this check add this line as a comment at the top of the script.
48
49 ``# <pep8 compliant>``
50
51 To enable line length checks use this instead.
52
53 ``# <pep8-80 compliant>``
54
55
56 User Interface Layout
57 =====================
58
59 TODO: Thomas
60
61
62 Script Efficiency
63 =================
64
65 List Manipulation (General Python Tips)
66 ---------------------------------------
67
68
69 Searching for list items
70 ^^^^^^^^^^^^^^^^^^^^^^^^
71
72 In Python there are some handy list functions that save you having to search through the list.
73
74 Even though you're not looping on the list data **python is**, so you need to be aware of functions that will slow down your script by searching the whole list.
75
76 .. code-block:: python
77
78    my_list.count(list_item)
79    my_list.index(list_item)
80    my_list.remove(list_item)
81    if list_item in my_list: ...
82
83
84 Modifying Lists
85 ^^^^^^^^^^^^^^^
86 In python we can add and remove from a list, This is slower when the list length is modifier, especially at the start of the list, since all the data after the index of modification needs to be moved up or down 1 place.
87
88 The most simple way to add onto the end of the list is to use ``my_list.append(list_item)`` or ``my_list.extend(some_list)`` and the fastest way to remove an item is ``my_list.pop()`` or ``del my_list[-1]``.
89
90 To use an index you can use ``my_list.insert(index, list_item)`` or ``list.pop(index)`` for list removal, but these are slower.
91
92 Sometimes its faster (but more memory hungry) to just rebuild the list.
93
94
95 Say you want to remove all triangle faces in a list.
96
97 Rather than...
98
99 .. code-block:: python
100
101    faces = mesh.faces[:]  # make a list copy of the meshes faces
102    f_idx = len(faces)     # Loop backwards
103    while f_idx:           # while the value is not 0
104        f_idx -= 1
105
106        if len(faces[f_idx].vertices) == 3:
107            faces.pop(f_idx)  # remove the triangle
108
109
110 It's faster to build a new list with list comprehension.
111
112 .. code-block:: python
113
114    faces = [f for f in mesh.faces if len(f.vertices) != 3]
115
116
117 Adding List Items
118 ^^^^^^^^^^^^^^^^^
119
120 If you have a list that you want to add onto another list, rather then...
121
122 .. code-block:: python
123
124    for l in some_list:
125        my_list.append(l)
126
127 Use...
128
129 .. code-block:: python
130
131    my_list.extend([a, b, c...])
132
133
134 Note that insert can be used when needed, but it is slower than append especially when inserting at the start of a long list.
135
136 This example shows a very sub-optimal way of making a reversed list.
137
138
139 .. code-block:: python
140
141    reverse_list = []
142    for list_item in some_list:
143        reverse_list.insert(0, list_item)
144
145
146 Removing List Items
147 ^^^^^^^^^^^^^^^^^^^
148
149 Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``
150
151 This requires you to have the index of the list item but is faster since ``remove()`` will search the list.
152
153 Here is an example of how to remove items in 1 loop, removing the last items first, which is faster (as explained above).
154
155 .. code-block:: python
156
157    list_index = len(my_list)
158
159    while list_index:
160        list_index -= 1
161        if my_list[list_index].some_test_attribute == 1:
162            my_list.pop(list_index)
163
164
165 This example shows a fast way of removing items, for use in cases were where you can alter the list order without breaking the scripts functionality. This works by swapping 2 list items, so the item you remove is always last.
166
167 .. code-block:: python
168
169    pop_index = 5
170
171    # swap so the pop_index is last.
172    my_list[-1], my_list[pop_index] = my_list[pop_index], my_list[-1]
173
174    # remove last item (pop_index)
175    my_list.pop()
176
177
178 When removing many items in a large list this can provide a good speedup.
179
180
181 Avoid Copying Lists
182 ^^^^^^^^^^^^^^^^^^^
183
184 When passing a list/dictionary to a function, it is faster to have the function modify the list rather then returning a new list so python dosn't have tp duplicate the list in memory.
185
186 Functions that modify a list in-place are more efficient then functions that create new lists.
187
188
189 This is generally slower so only use for functions when it makes sense not to modify the list in place.
190
191 >>> my_list = some_list_func(my_list)
192
193
194 This is generally faster since there is no re-assignment and no list duplication.
195
196 >>> some_list_func(vec)
197
198
199 Also note that passing a sliced list makes a copy of the list in python memory
200
201 >>> foobar(my_list[:])
202
203 If my_list was a large array containing 10000's of items, a copy could use a lot of extra memory.
204
205
206 Writing Strings to a File (Python General)
207 ------------------------------------------
208
209 Here are 3 ways of joining multiple strings into 1 string for writing
210
211 This really applies to any area of your code that involves a lot of string joining.
212
213
214 Pythons string addition, *don't use if you can help it, especially when writing data in a loop.*
215
216 >>> file.write(str1 + " " + str2 + " " + str3 + "\n")
217
218
219 String formatting. Use this when you're writing string data from floats and int's
220
221 >>> file.write("%s %s %s\n" % (str1, str2, str3))
222
223
224 Pythons string joining function. To join a list of strings
225
226 >>> file.write(" ".join([str1, str2, str3, "\n"]))
227
228
229 join is fastest on many strings, string formatting is quite fast too (better for converting data types). String arithmetic is slowest.
230
231
232 Parsing Strings (Import/Exporting)
233 ----------------------------------
234
235 Since many file formats are ASCII, the way you parse/export strings can make a large difference in how fast your script runs.
236
237 When importing strings to make into blender there are a few ways to parse the string.
238
239 Parsing Numbers
240 ^^^^^^^^^^^^^^^
241
242 Use ``float(string)`` rather than ``eval(string)``, if you know the value will be an int then ``int(string)``,  float() will work for an int too but its faster to read ints with int().
243
244 Checking String Start/End
245 ^^^^^^^^^^^^^^^^^^^^^^^^^
246
247 If your checking the start of a string for a keyword, rather than...
248
249 >>> if line[0:5] == "vert ": ...
250
251 Use...
252
253 >>> if line.startswith("vert "):
254
255 Using ``startswith()`` is slightly faster (approx 5%) and also avoids a possible error with the slice length not matching the string length.
256
257 my_string.endswith("foo_bar") can be used for line endings too.
258
259 if your unsure whether the text is upper or lower case use lower or upper string function.
260
261 >>> if line.lower().startswith("vert ")
262
263
264 Use try/except Sparingly
265 ------------------------
266
267 The **try** statement useful to save time writing error checking code.
268
269 However **try** is significantly slower then an **if** since an exception has to be set each time, so avoid using **try** in areas of your code that execute in a loop and runs many times.
270
271 There are cases where using **try** is faster than checking weather the condition will raise an error, so it is worth experimenting.
272
273
274 Value Comparison
275 ----------------
276
277 Python has two ways to compare values ``a == b`` and ``a is b``, The difference is that ``==`` may run the objects comparison function ``__cmp__()`` where as ``is`` compares identity, that both variables reference the same item in memory. 
278
279 In cases where you know you are checking for the same value which is referenced from multiple places, ``is`` is faster.
280
281
282 Time You're Code
283 ----------------
284
285 While developing a script its good to time it to be aware of any changes in performance, this can be done simply.
286
287 .. code-block:: python
288
289    import time
290    time_start = time.time()
291
292    # do something...
293
294    print("My Script Finished: %.4f sec" % time.time() - time_start)