Creating a BGE staging branch.
[blender.git] / doc / python_api / rst / info_best_practice.rst
1 *************
2 Best Practice
3 *************
4
5 When writing your 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 your own work this is of course fine, but if you want to collaborate with others or have your work included with blender there are practices we encourage.
8
9
10 Style Conventions
11 =================
12
13 For Blender/Python development 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.image_settings.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 Some notes to keep in mind when writing UI layouts:
60
61 * UI code is quite simple. Layout declarations are there to easily create a decent layout. 
62
63   General rule here: If you need more code for the layout declaration, then for the actual properties, you do it wrong. 
64   
65 Example layouts:
66
67 * layout()
68
69   The basic layout is a simple Top -> Bottom layout. 
70   
71   .. code-block:: python
72
73          layout.prop()
74          layout.prop()
75
76 * layout.row()
77
78   Use row(), when you want more than 1 property in one line. 
79   
80   .. code-block:: python
81          
82          row = layout.row()
83          row.prop()
84          row.prop()
85
86 * layout.column()
87
88   Use column(), when you want your properties in a column.
89   
90   .. code-block:: python
91   
92          col = layout.column()
93          col.prop()
94          col.prop()
95
96 * layout.split()
97
98   This can be used to create more complex layouts. For example you can split the layout and create two column() layouts next to each other.
99   Don't use split, when you simply want two properties in a row. Use row() for that.
100   
101   .. code-block:: python
102   
103          split = layout.split()
104          
105          col = split.column()
106          col.prop()
107          col.prop()
108          
109          col = split.column()
110          col.prop()
111          col.prop()
112          
113 Declaration names:
114
115 Try to only use these variable names for layout declarations:
116
117 * row for a row() layout
118 * col for a column() layout
119 * split for a split() layout
120 * flow for a column_flow() layout
121 * sub for a sub layout (a column inside a column for example)
122
123
124 Script Efficiency
125 =================
126
127 List Manipulation (General Python Tips)
128 ---------------------------------------
129
130
131 Searching for list items
132 ^^^^^^^^^^^^^^^^^^^^^^^^
133
134 In Python there are some handy list functions that save you having to search through the list.
135
136 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.
137
138 .. code-block:: python
139
140    my_list.count(list_item)
141    my_list.index(list_item)
142    my_list.remove(list_item)
143    if list_item in my_list: ...
144
145
146 Modifying Lists
147 ^^^^^^^^^^^^^^^
148 In python we can add and remove from a list, this is slower when the list length is modified, 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.
149
150 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]``.
151
152 To use an index you can use ``my_list.insert(index, list_item)`` or ``list.pop(index)`` for list removal, but these are slower.
153
154 Sometimes its faster (but more memory hungry) to just rebuild the list.
155
156
157 Say you want to remove all triangular faces in a list.
158
159 Rather than...
160
161 .. code-block:: python
162
163    faces = mesh.tessfaces[:]  # make a list copy of the meshes faces
164    f_idx = len(faces)     # Loop backwards
165    while f_idx:           # while the value is not 0
166        f_idx -= 1
167
168        if len(faces[f_idx].vertices) == 3:
169            faces.pop(f_idx)  # remove the triangle
170
171
172 It's faster to build a new list with list comprehension.
173
174 .. code-block:: python
175
176    faces = [f for f in mesh.tessfaces if len(f.vertices) != 3]
177
178
179 Adding List Items
180 ^^^^^^^^^^^^^^^^^
181
182 If you have a list that you want to add onto another list, rather than...
183
184 .. code-block:: python
185
186    for l in some_list:
187        my_list.append(l)
188
189 Use...
190
191 .. code-block:: python
192
193    my_list.extend([a, b, c...])
194
195
196 Note that insert can be used when needed, but it is slower than append especially when inserting at the start of a long list.
197
198 This example shows a very sub-optimal way of making a reversed list.
199
200
201 .. code-block:: python
202
203    reverse_list = []
204    for list_item in some_list:
205        reverse_list.insert(0, list_item)
206
207
208 Python provides more convenient ways to reverse a list using the slice method, but you may want to time this before relying on it too much:
209
210
211 .. code-block:: python
212
213   some_reversed_list = some_list[::-1]
214
215
216 Removing List Items
217 ^^^^^^^^^^^^^^^^^^^
218
219 Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``
220
221 This requires you to have the index of the list item but is faster since ``remove()`` will search the list.
222
223 Here is an example of how to remove items in 1 loop, removing the last items first, which is faster (as explained above).
224
225 .. code-block:: python
226
227    list_index = len(my_list)
228
229    while list_index:
230        list_index -= 1
231        if my_list[list_index].some_test_attribute == 1:
232            my_list.pop(list_index)
233
234
235 This example shows a fast way of removing items, for use in cases 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.
236
237 .. code-block:: python
238
239    pop_index = 5
240
241    # swap so the pop_index is last.
242    my_list[-1], my_list[pop_index] = my_list[pop_index], my_list[-1]
243
244    # remove last item (pop_index)
245    my_list.pop()
246
247
248 When removing many items in a large list this can provide a good speedup.
249
250
251 Avoid Copying Lists
252 ^^^^^^^^^^^^^^^^^^^
253
254 When passing a list/dictionary to a function, it is faster to have the function modify the list rather than returning a new list so python doesn't have to duplicate the list in memory.
255
256 Functions that modify a list in-place are more efficient than functions that create new lists.
257
258
259 This is generally slower so only use for functions when it makes sense not to modify the list in place.
260
261 >>> my_list = some_list_func(my_list)
262
263
264 This is generally faster since there is no re-assignment and no list duplication.
265
266 >>> some_list_func(vec)
267
268
269 Also note that passing a sliced list makes a copy of the list in python memory
270
271 >>> foobar(my_list[:])
272
273 If my_list was a large array containing 10000's of items, a copy could use a lot of extra memory.
274
275
276 Writing Strings to a File (Python General)
277 ------------------------------------------
278
279 Here are 3 ways of joining multiple strings into 1 string for writing
280
281 This really applies to any area of your code that involves a lot of string joining.
282
283
284 Python’s string addition, *don't use if you can help it, especially when writing data in a loop.*
285
286 >>> file.write(str1 + " " + str2 + " " + str3 + "\n")
287
288
289 String formatting. Use this when you're writing string data from floats and ints
290
291 >>> file.write("%s %s %s\n" % (str1, str2, str3))
292
293
294 Python’s string joining function. To join a list of strings
295
296 >>> file.write(" ".join([str1, str2, str3, "\n"]))
297
298
299 join is fastest on many strings, `string formatting <http://docs.python.org/py3k/library/string.html#string-formatting>`_ is quite fast too (better for converting data types). String arithmetic is slowest.
300
301
302 Parsing Strings (Import/Exporting)
303 ----------------------------------
304
305 Since many file formats are ASCII, the way you parse/export strings can make a large difference in how fast your script runs.
306
307 There are a few ways to parse strings when importing them into Blender.
308
309 Parsing Numbers
310 ^^^^^^^^^^^^^^^
311
312 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 it's faster to read ints with int().
313
314 Checking String Start/End
315 ^^^^^^^^^^^^^^^^^^^^^^^^^
316
317 If you're checking the start of a string for a keyword, rather than...
318
319 >>> if line[0:5] == "vert ": ...
320
321 Use...
322
323 >>> if line.startswith("vert "):
324
325 Using ``startswith()`` is slightly faster (approx 5%) and also avoids a possible error with the slice length not matching the string length.
326
327 my_string.endswith("foo_bar") can be used for line endings too.
328
329 If you are unsure whether the text is upper or lower case use ``lower()`` or ``upper()`` string function.
330
331 >>> if line.lower().startswith("vert ")
332
333
334 Use try/except Sparingly
335 ------------------------
336
337 The **try** statement is useful to save time writing error checking code.
338
339 However **try** is significantly slower than 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.
340
341 There are cases where using **try** is faster than checking whether the condition will raise an error, so it is worth experimenting.
342
343
344 Value Comparison
345 ----------------
346
347 Python has two ways to compare values ``a == b`` and ``a is b``, the difference is that ``==`` may run the objects comparison function ``__cmp__()`` whereas ``is`` compares identity, that both variables reference the same item in memory. 
348
349 In cases where you know you are checking for the same value which is referenced from multiple places, ``is`` is faster.
350
351
352 Time Your Code
353 --------------
354
355 While developing a script it's good to time it to be aware of any changes in performance, this can be done simply.
356
357 .. code-block:: python
358
359    import time
360    time_start = time.time()
361
362    # do something...
363
364    print("My Script Finished: %.4f sec" % time.time() - time_start)