~openerp-chinese-team/openobject-doc/Chinese-init

« back to all changes in this revision

Viewing changes to i18n/zh_CN/source/contribute/15_guidelines/coding_guidelines_python.rst

  • Committer: JoshuaJan
  • Date: 2012-12-04 01:36:44 UTC
  • Revision ID: popkar77@gmail.com-20121204013644-k25kpyac672wxe22
Chinese initialization

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
.. i18n: .. sectnum::
 
3
.. i18n:     :start: 1
 
4
..
 
5
 
 
6
.. sectnum::
 
7
    :start: 1
 
8
 
 
9
.. i18n: Python style guide
 
10
.. i18n: ++++++++++++++++++
 
11
.. i18n: In additions to these guidelines, you may also find the following link
 
12
.. i18n: interesting:
 
13
..
 
14
 
 
15
Python style guide
 
16
++++++++++++++++++
 
17
In additions to these guidelines, you may also find the following link
 
18
interesting:
 
19
 
 
20
.. i18n:  * http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html (a little bit outdated, but quite relevant)
 
21
..
 
22
 
 
23
 * http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html (a little bit outdated, but quite relevant)
 
24
 
 
25
.. i18n: magic methods
 
26
.. i18n: -------------
 
27
.. i18n: magic methods (starting *and* ending with two underscores) should *not* have to be called directly unless you're overriding a method of the same name.
 
28
..
 
29
 
 
30
magic methods
 
31
-------------
 
32
magic methods (starting *and* ending with two underscores) should *not* have to be called directly unless you're overriding a method of the same name.
 
33
 
 
34
.. i18n: magic methods are used to implement specific protocols and are called for you, either due to operator access or due to some special operation using them:
 
35
..
 
36
 
 
37
magic methods are used to implement specific protocols and are called for you, either due to operator access or due to some special operation using them:
 
38
 
 
39
.. i18n: .. code-block:: python
 
40
.. i18n: 
 
41
.. i18n:         # bad
 
42
.. i18n:         levels.__contains__(name)
 
43
.. i18n:         # good
 
44
.. i18n:         name in levels
 
45
.. i18n:         # very bad
 
46
.. i18n:         kw.__setitem__('nodes',nodes)
 
47
.. i18n:         # much better
 
48
.. i18n:         kw['nodes'] = nodes
 
49
..
 
50
 
 
51
.. code-block:: python
 
52
 
 
53
        # bad
 
54
        levels.__contains__(name)
 
55
        # good
 
56
        name in levels
 
57
        # very bad
 
58
        kw.__setitem__('nodes',nodes)
 
59
        # much better
 
60
        kw['nodes'] = nodes
 
61
 
 
62
.. i18n: .clone()
 
63
.. i18n: --------
 
64
.. i18n: rarely necessary (unless you really have no idea what the type of the variable you're trying to clone is), never necessary for built-in collections: just call the constructor with your existing collection:
 
65
..
 
66
 
 
67
.clone()
 
68
--------
 
69
rarely necessary (unless you really have no idea what the type of the variable you're trying to clone is), never necessary for built-in collections: just call the constructor with your existing collection:
 
70
 
 
71
.. i18n: .. code-block:: python
 
72
.. i18n: 
 
73
.. i18n:         # bad
 
74
.. i18n:         new_dict = my_dict.clone()
 
75
.. i18n:         # good
 
76
.. i18n:         new_dict = dict(my_dict)
 
77
.. i18n:         # bad
 
78
.. i18n:         new_list = old_list.clone()
 
79
.. i18n:         # good
 
80
.. i18n:         new_list = list(old_list)
 
81
..
 
82
 
 
83
.. code-block:: python
 
84
 
 
85
        # bad
 
86
        new_dict = my_dict.clone()
 
87
        # good
 
88
        new_dict = dict(my_dict)
 
89
        # bad
 
90
        new_list = old_list.clone()
 
91
        # good
 
92
        new_list = list(old_list)
 
93
 
 
94
.. i18n: And don't clone manually, please:
 
95
..
 
96
 
 
97
And don't clone manually, please:
 
98
 
 
99
.. i18n: .. code-block:: python
 
100
.. i18n: 
 
101
.. i18n:         # surely you jest!
 
102
.. i18n:         values = []
 
103
.. i18n:         for val in view_items:
 
104
.. i18n:                 values += [val]
 
105
.. i18n:         # sane
 
106
.. i18n:         values = list(view_items)
 
107
..
 
108
 
 
109
.. code-block:: python
 
110
 
 
111
        # surely you jest!
 
112
        values = []
 
113
        for val in view_items:
 
114
                values += [val]
 
115
        # sane
 
116
        values = list(view_items)
 
117
 
 
118
.. i18n: the "clone and update"
 
119
.. i18n: ----------------------
 
120
.. i18n: the ``dict`` constructor takes both a single (optional) positional argument (either a dictionary-like object or an iterable of 2-tuples) and an unlimited number of keyword arguments. Thus, you can "merge" two different dictionaries into a third, new, dictionary:
 
121
..
 
122
 
 
123
the "clone and update"
 
124
----------------------
 
125
the ``dict`` constructor takes both a single (optional) positional argument (either a dictionary-like object or an iterable of 2-tuples) and an unlimited number of keyword arguments. Thus, you can "merge" two different dictionaries into a third, new, dictionary:
 
126
 
 
127
.. i18n: .. code-block:: python
 
128
.. i18n: 
 
129
.. i18n:         # bad
 
130
.. i18n:         dictionary3 = dictionary1.clone()
 
131
.. i18n:         dictionary3.update(dictionary2)
 
132
.. i18n:         # worse
 
133
.. i18n:         dictionary3 = {}
 
134
.. i18n:         dictionary3.update(d1)
 
135
.. i18n:         dictionary3.update(d2)
 
136
.. i18n:         # good
 
137
.. i18n:         dictionary3 = dict(dictionary1, **dictionary2)
 
138
..
 
139
 
 
140
.. code-block:: python
 
141
 
 
142
        # bad
 
143
        dictionary3 = dictionary1.clone()
 
144
        dictionary3.update(dictionary2)
 
145
        # worse
 
146
        dictionary3 = {}
 
147
        dictionary3.update(d1)
 
148
        dictionary3.update(d2)
 
149
        # good
 
150
        dictionary3 = dict(dictionary1, **dictionary2)
 
151
 
 
152
.. i18n: .. **
 
153
..
 
154
 
 
155
.. **
 
156
 
 
157
.. i18n: You can use those properties for simpler operations, such as cloning an existing dict and (re) setting a key:
 
158
..
 
159
 
 
160
You can use those properties for simpler operations, such as cloning an existing dict and (re) setting a key:
 
161
 
 
162
.. i18n: .. code-block:: python
 
163
.. i18n: 
 
164
.. i18n:         # no
 
165
.. i18n:         context = kw.clone()
 
166
.. i18n:         context['foo'] = 'bar'
 
167
.. i18n:         # yes
 
168
.. i18n:         context = dict(kw, foo='bar')
 
169
..
 
170
 
 
171
.. code-block:: python
 
172
 
 
173
        # no
 
174
        context = kw.clone()
 
175
        context['foo'] = 'bar'
 
176
        # yes
 
177
        context = dict(kw, foo='bar')
 
178
 
 
179
.. i18n: "manual update"
 
180
.. i18n: ---------------
 
181
.. i18n: the signature of dict.update is the same as ``dict()``: a single, optional, positional argument and an unlimited number of keyword arguments.
 
182
..
 
183
 
 
184
"manual update"
 
185
---------------
 
186
the signature of dict.update is the same as ``dict()``: a single, optional, positional argument and an unlimited number of keyword arguments.
 
187
 
 
188
.. i18n: Thus the following are possible:
 
189
..
 
190
 
 
191
Thus the following are possible:
 
192
 
 
193
.. i18n: merging a dict from another one:
 
194
..
 
195
 
 
196
merging a dict from another one:
 
197
 
 
198
.. i18n: .. code-block:: python
 
199
.. i18n: 
 
200
.. i18n:         # bad
 
201
.. i18n:         for key, value in other_dict.iteritems():
 
202
.. i18n:                 my_dict[key] = value
 
203
.. i18n:         # good
 
204
.. i18n:         my_dict.update(other_dict)
 
205
..
 
206
 
 
207
.. code-block:: python
 
208
 
 
209
        # bad
 
210
        for key, value in other_dict.iteritems():
 
211
                my_dict[key] = value
 
212
        # good
 
213
        my_dict.update(other_dict)
 
214
 
 
215
.. i18n: Setting a bunch of keys at the same time:
 
216
..
 
217
 
 
218
Setting a bunch of keys at the same time:
 
219
 
 
220
.. i18n: .. code-block:: python
 
221
.. i18n: 
 
222
.. i18n:         # bad
 
223
.. i18n:         my_dict['foo'] = 3
 
224
.. i18n:         my_dict['bar'] = 4
 
225
.. i18n:         my_dict['baz'] = 5
 
226
.. i18n:         # good
 
227
.. i18n:         my_dict.update(
 
228
.. i18n:                 foo=3,
 
229
.. i18n:                 bar=4,
 
230
.. i18n:                 baz=5)
 
231
..
 
232
 
 
233
.. code-block:: python
 
234
 
 
235
        # bad
 
236
        my_dict['foo'] = 3
 
237
        my_dict['bar'] = 4
 
238
        my_dict['baz'] = 5
 
239
        # good
 
240
        my_dict.update(
 
241
                foo=3,
 
242
                bar=4,
 
243
                baz=5)
 
244
 
 
245
.. i18n: Java dictionary creation
 
246
.. i18n: ------------------------
 
247
.. i18n: Python isn't java, it has literals:
 
248
..
 
249
 
 
250
Java dictionary creation
 
251
------------------------
 
252
Python isn't java, it has literals:
 
253
 
 
254
.. i18n: .. code-block:: python
 
255
.. i18n: 
 
256
.. i18n:         # very very very bad
 
257
.. i18n:         my_dict = {}
 
258
.. i18n:         my_dict['foo'] = 3
 
259
.. i18n:         my_dict['bar'] = 4
 
260
.. i18n:         my_dict['baz'] = 5
 
261
.. i18n:         # good
 
262
.. i18n:         my_dict = {
 
263
.. i18n:                 'foo': 3,
 
264
.. i18n:                 'bar': 4,
 
265
.. i18n:                 'baz': 5
 
266
.. i18n:         }
 
267
..
 
268
 
 
269
.. code-block:: python
 
270
 
 
271
        # very very very bad
 
272
        my_dict = {}
 
273
        my_dict['foo'] = 3
 
274
        my_dict['bar'] = 4
 
275
        my_dict['baz'] = 5
 
276
        # good
 
277
        my_dict = {
 
278
                'foo': 3,
 
279
                'bar': 4,
 
280
                'baz': 5
 
281
        }
 
282
 
 
283
.. i18n: "temporary kwargs"
 
284
.. i18n: ------------------
 
285
.. i18n: keyword arguments are a good way to get a bunch of unspecified supplementary arguments if e.g. you just want to forward them:
 
286
..
 
287
 
 
288
"temporary kwargs"
 
289
------------------
 
290
keyword arguments are a good way to get a bunch of unspecified supplementary arguments if e.g. you just want to forward them:
 
291
 
 
292
.. i18n: .. code-block:: python
 
293
.. i18n: 
 
294
.. i18n:         def foo(**kwargs):
 
295
.. i18n:                 logging.debug('Calling foo with arguments %s', kwargs)
 
296
.. i18n:                 return bar(**kwargs)
 
297
..
 
298
 
 
299
.. code-block:: python
 
300
 
 
301
        def foo(**kwargs):
 
302
                logging.debug('Calling foo with arguments %s', kwargs)
 
303
                return bar(**kwargs)
 
304
 
 
305
.. i18n: or if you retrieved a ready-made dict (from another function) and want to pass its content to another function or method:
 
306
..
 
307
 
 
308
or if you retrieved a ready-made dict (from another function) and want to pass its content to another function or method:
 
309
 
 
310
.. i18n: .. code-block:: python
 
311
.. i18n: 
 
312
.. i18n:         sessions = some_function_returning_a_dict_of_sessions()
 
313
.. i18n:         some_other_function(**sessions)
 
314
..
 
315
 
 
316
.. code-block:: python
 
317
 
 
318
        sessions = some_function_returning_a_dict_of_sessions()
 
319
        some_other_function(**sessions)
 
320
 
 
321
.. i18n: but there is no point whatsoever in creating a dict for the sake of passing it as ``**kwargs``, just provide the damn keyword arguments:
 
322
..
 
323
 
 
324
but there is no point whatsoever in creating a dict for the sake of passing it as ``**kwargs``, just provide the damn keyword arguments:
 
325
 
 
326
.. i18n: .. ``
 
327
..
 
328
 
 
329
.. ``
 
330
 
 
331
.. i18n: .. code-block:: python
 
332
.. i18n: 
 
333
.. i18n:         # waste of time and space
 
334
.. i18n:         my_dict = {
 
335
.. i18n:                 'foo': 3,
 
336
.. i18n:                 'bar': 4,
 
337
.. i18n:                 'baz': 5
 
338
.. i18n:         }
 
339
.. i18n:         some_func(**my_dict)
 
340
.. i18n:         # good
 
341
.. i18n:         some_func(foo=3, bar=4, baz=5)
 
342
..
 
343
 
 
344
.. code-block:: python
 
345
 
 
346
        # waste of time and space
 
347
        my_dict = {
 
348
                'foo': 3,
 
349
                'bar': 4,
 
350
                'baz': 5
 
351
        }
 
352
        some_func(**my_dict)
 
353
        # good
 
354
        some_func(foo=3, bar=4, baz=5)
 
355
 
 
356
.. i18n: .. **
 
357
..
 
358
 
 
359
.. **
 
360
 
 
361
.. i18n: deprecated methods (formally or informally)
 
362
.. i18n: -------------------------------------------
 
363
.. i18n: ``dict.has_key(key)`` is deprecated, please use the ``in`` operator:
 
364
..
 
365
 
 
366
deprecated methods (formally or informally)
 
367
-------------------------------------------
 
368
``dict.has_key(key)`` is deprecated, please use the ``in`` operator:
 
369
 
 
370
.. i18n: .. code-block:: python
 
371
.. i18n: 
 
372
.. i18n:         # bad
 
373
.. i18n:         kw.has_key('cross_on_pages')
 
374
.. i18n:         # good
 
375
.. i18n:         'cross_on_pages' in kw
 
376
..
 
377
 
 
378
.. code-block:: python
 
379
 
 
380
        # bad
 
381
        kw.has_key('cross_on_pages')
 
382
        # good
 
383
        'cross_on_pages' in kw
 
384
 
 
385
.. i18n: useless intermediate variables
 
386
.. i18n: ------------------------------
 
387
.. i18n: Temporary variables can make the code clearer by giving names to objects, but that doesn't mean you should create temporary variables all the time:
 
388
..
 
389
 
 
390
useless intermediate variables
 
391
------------------------------
 
392
Temporary variables can make the code clearer by giving names to objects, but that doesn't mean you should create temporary variables all the time:
 
393
 
 
394
.. i18n: .. code-block:: python
 
395
.. i18n: 
 
396
.. i18n:         # pointless
 
397
.. i18n:         schema = kw['schema']
 
398
.. i18n:         params = {'schema': schema}
 
399
.. i18n:         # simpler
 
400
.. i18n:         params = {'schema': kw['schema']}
 
401
..
 
402
 
 
403
.. code-block:: python
 
404
 
 
405
        # pointless
 
406
        schema = kw['schema']
 
407
        params = {'schema': schema}
 
408
        # simpler
 
409
        params = {'schema': kw['schema']}
 
410
 
 
411
.. i18n: 3 strikes, and the code's out
 
412
.. i18n: -----------------------------
 
413
.. i18n: A bit of redundancy can be accepted: maybe you have to get the content of an axis:
 
414
..
 
415
 
 
416
3 strikes, and the code's out
 
417
-----------------------------
 
418
A bit of redundancy can be accepted: maybe you have to get the content of an axis:
 
419
 
 
420
.. i18n: .. code-block:: python
 
421
.. i18n: 
 
422
.. i18n:         col_axes = []
 
423
.. i18n:         if kw.has_key('col_axis'):
 
424
.. i18n:             col_axes = self.axes(kw['col_axis'])
 
425
..
 
426
 
 
427
.. code-block:: python
 
428
 
 
429
        col_axes = []
 
430
        if kw.has_key('col_axis'):
 
431
            col_axes = self.axes(kw['col_axis'])
 
432
 
 
433
.. i18n: and a second one:
 
434
..
 
435
 
 
436
and a second one:
 
437
 
 
438
.. i18n: .. code-block:: python
 
439
.. i18n: 
 
440
.. i18n:         col_axes = []
 
441
.. i18n:         if kw.has_key('col_axis'):
 
442
.. i18n:             col_axes = self.axes(kw['col_axis'])
 
443
.. i18n:         page_axes= []
 
444
.. i18n:         if kw.has_key('page_axis'):
 
445
.. i18n:             page_axes = self.axes(kw['page_axis'])
 
446
..
 
447
 
 
448
.. code-block:: python
 
449
 
 
450
        col_axes = []
 
451
        if kw.has_key('col_axis'):
 
452
            col_axes = self.axes(kw['col_axis'])
 
453
        page_axes= []
 
454
        if kw.has_key('page_axis'):
 
455
            page_axes = self.axes(kw['page_axis'])
 
456
 
 
457
.. i18n: But at the third strike, chances are you're going to need the thing again and again. Time to refactor:
 
458
..
 
459
 
 
460
But at the third strike, chances are you're going to need the thing again and again. Time to refactor:
 
461
 
 
462
.. i18n: .. code-block:: python
 
463
.. i18n: 
 
464
.. i18n:         def get_axis(self, name, kw):
 
465
.. i18n:                 if name not in kw:
 
466
.. i18n:                     return []
 
467
.. i18n:                 return self.axes(kw[name])
 
468
.. i18n:         #[…]
 
469
.. i18n:         col_axes = self.get_axis('col_axis', kw)
 
470
.. i18n:         page_axes = self.get_axis('page_axis', kw)
 
471
..
 
472
 
 
473
.. code-block:: python
 
474
 
 
475
        def get_axis(self, name, kw):
 
476
                if name not in kw:
 
477
                    return []
 
478
                return self.axes(kw[name])
 
479
        #[…]
 
480
        col_axes = self.get_axis('col_axis', kw)
 
481
        page_axes = self.get_axis('page_axis', kw)
 
482
 
 
483
.. i18n: The refactoring could also be an improvement of a method already called (be sure to check where the method is called in order not to break other pieces of code. Or write tests):
 
484
..
 
485
 
 
486
The refactoring could also be an improvement of a method already called (be sure to check where the method is called in order not to break other pieces of code. Or write tests):
 
487
 
 
488
.. i18n: .. code-block:: python
 
489
.. i18n: 
 
490
.. i18n:         # from
 
491
.. i18n:         def axes(self, axis):
 
492
.. i18n:                 axes = []
 
493
.. i18n:                 if type(axis) == type([]):
 
494
.. i18n:                         axes.extend(axis)
 
495
.. i18n:                 else:
 
496
.. i18n:                         axes.append(axis)
 
497
.. i18n:                 return axes
 
498
.. i18n: 
 
499
.. i18n:         def output(self, **kw):
 
500
.. i18n:                 col_axes = []
 
501
.. i18n:                 if kw.has_key('col_axis'):
 
502
.. i18n:                         col_axes = self.axes(kw['col_axis'])
 
503
.. i18n:                 page_axes = []
 
504
.. i18n:                 if kw.has_key('page_axis'):
 
505
.. i18n:                         page_axes = self.axes(kw['page_axis'])
 
506
.. i18n:                 cross_on_rows = []
 
507
.. i18n:                 if kw.has_key('cross_on_rows'):
 
508
.. i18n:                          cross_on_rows = self.axes(kw['cross_on_rows'])
 
509
.. i18n: 
 
510
.. i18n:         # to:
 
511
.. i18n:         def axes(self, axis):
 
512
.. i18n:                 if axis is None: return []
 
513
.. i18n:                 axes = []
 
514
.. i18n:                 if type(axis) == type([]):
 
515
.. i18n:                         axes.extend(axis)
 
516
.. i18n:                 else:
 
517
.. i18n:                         axes.append(axis)
 
518
.. i18n:                 return axes
 
519
.. i18n: 
 
520
.. i18n:         def output(self, **kw):
 
521
.. i18n:                 col_axes = self.axes(kw.get('col_axis'))
 
522
.. i18n:                 page_axes = self.axes(kw.get('page_axis'))
 
523
.. i18n:                 cross_on_rows = self.axes(kw.get('cross_on_rows'))
 
524
..
 
525
 
 
526
.. code-block:: python
 
527
 
 
528
        # from
 
529
        def axes(self, axis):
 
530
                axes = []
 
531
                if type(axis) == type([]):
 
532
                        axes.extend(axis)
 
533
                else:
 
534
                        axes.append(axis)
 
535
                return axes
 
536
 
 
537
        def output(self, **kw):
 
538
                col_axes = []
 
539
                if kw.has_key('col_axis'):
 
540
                        col_axes = self.axes(kw['col_axis'])
 
541
                page_axes = []
 
542
                if kw.has_key('page_axis'):
 
543
                        page_axes = self.axes(kw['page_axis'])
 
544
                cross_on_rows = []
 
545
                if kw.has_key('cross_on_rows'):
 
546
                         cross_on_rows = self.axes(kw['cross_on_rows'])
 
547
 
 
548
        # to:
 
549
        def axes(self, axis):
 
550
                if axis is None: return []
 
551
                axes = []
 
552
                if type(axis) == type([]):
 
553
                        axes.extend(axis)
 
554
                else:
 
555
                        axes.append(axis)
 
556
                return axes
 
557
 
 
558
        def output(self, **kw):
 
559
                col_axes = self.axes(kw.get('col_axis'))
 
560
                page_axes = self.axes(kw.get('page_axis'))
 
561
                cross_on_rows = self.axes(kw.get('cross_on_rows'))
 
562
 
 
563
.. i18n: .. **
 
564
..
 
565
 
 
566
.. **
 
567
 
 
568
.. i18n: Multiple return points are OK, when they're simpler
 
569
.. i18n: ---------------------------------------------------
 
570
..
 
571
 
 
572
Multiple return points are OK, when they're simpler
 
573
---------------------------------------------------
 
574
 
 
575
.. i18n: .. code-block:: python
 
576
.. i18n: 
 
577
.. i18n:         # a bit complex and with a redundant temp variable
 
578
.. i18n:         def axes(self, axis):
 
579
.. i18n:                 axes = []
 
580
.. i18n:                 if type(axis) == type([]):
 
581
.. i18n:                         axes.extend(axis)
 
582
.. i18n:                 else:
 
583
.. i18n:                         axes.append(axis)
 
584
.. i18n:                 return axes
 
585
.. i18n: 
 
586
.. i18n:          # clearer
 
587
.. i18n:         def axes(self, axis):
 
588
.. i18n:                 if type(axis) == type([]):
 
589
.. i18n:                         return list(axis) # clone the axis
 
590
.. i18n:                 else:
 
591
.. i18n:                         return [axis] # single-element list
 
592
..
 
593
 
 
594
.. code-block:: python
 
595
 
 
596
        # a bit complex and with a redundant temp variable
 
597
        def axes(self, axis):
 
598
                axes = []
 
599
                if type(axis) == type([]):
 
600
                        axes.extend(axis)
 
601
                else:
 
602
                        axes.append(axis)
 
603
                return axes
 
604
 
 
605
         # clearer
 
606
        def axes(self, axis):
 
607
                if type(axis) == type([]):
 
608
                        return list(axis) # clone the axis
 
609
                else:
 
610
                        return [axis] # single-element list
 
611
 
 
612
.. i18n: Try to avoid type-testing
 
613
.. i18n: -------------------------
 
614
..
 
615
 
 
616
Try to avoid type-testing
 
617
-------------------------
 
618
 
 
619
.. i18n: Python is a dynamically typed languages, if you don't absolutely need to
 
620
.. i18n: receive a list, then don't test for a list, just do your stuff (e.g. iterating
 
621
.. i18n: on it, then caller can provide any kind of iterator or container)
 
622
..
 
623
 
 
624
Python is a dynamically typed languages, if you don't absolutely need to
 
625
receive a list, then don't test for a list, just do your stuff (e.g. iterating
 
626
on it, then caller can provide any kind of iterator or container)
 
627
 
 
628
.. i18n: Don't use ``type`` if you already know what the type you want is
 
629
.. i18n: ----------------------------------------------------------------
 
630
..
 
631
 
 
632
Don't use ``type`` if you already know what the type you want is
 
633
----------------------------------------------------------------
 
634
 
 
635
.. i18n: The type of a list is ``list``, the type of a dict is ``dict``:
 
636
..
 
637
 
 
638
The type of a list is ``list``, the type of a dict is ``dict``:
 
639
 
 
640
.. i18n: .. code-block:: python
 
641
.. i18n: 
 
642
.. i18n:         # bad
 
643
.. i18n:         def axes(self, axis):
 
644
.. i18n:                 if type(axis) == type([]): # we already know what the type of [] is
 
645
.. i18n:                         return list(axis)
 
646
.. i18n:                 else:
 
647
.. i18n:                         return [axis]
 
648
.. i18n:         # good
 
649
.. i18n:         def axes(self, axis):
 
650
.. i18n:                 if type(axis) == list:
 
651
.. i18n:                         return list(axis)
 
652
.. i18n:                 else:
 
653
.. i18n:                         return [axis]
 
654
..
 
655
 
 
656
.. code-block:: python
 
657
 
 
658
        # bad
 
659
        def axes(self, axis):
 
660
                if type(axis) == type([]): # we already know what the type of [] is
 
661
                        return list(axis)
 
662
                else:
 
663
                        return [axis]
 
664
        # good
 
665
        def axes(self, axis):
 
666
                if type(axis) == list:
 
667
                        return list(axis)
 
668
                else:
 
669
                        return [axis]
 
670
 
 
671
.. i18n: plus Python types are singletons, so you can just test for identity, it reads better:
 
672
..
 
673
 
 
674
plus Python types are singletons, so you can just test for identity, it reads better:
 
675
 
 
676
.. i18n: .. code-block:: python
 
677
.. i18n: 
 
678
.. i18n:         # better
 
679
.. i18n:         def axes(self, axis):
 
680
.. i18n:                 if type(axis) is list:
 
681
.. i18n:                         return list(axis)
 
682
.. i18n:                 else:
 
683
.. i18n:                         return [axis]
 
684
..
 
685
 
 
686
.. code-block:: python
 
687
 
 
688
        # better
 
689
        def axes(self, axis):
 
690
                if type(axis) is list:
 
691
                        return list(axis)
 
692
                else:
 
693
                        return [axis]
 
694
 
 
695
.. i18n: But really, if you need type testing just use the tools python provides
 
696
.. i18n: -----------------------------------------------------------------------
 
697
..
 
698
 
 
699
But really, if you need type testing just use the tools python provides
 
700
-----------------------------------------------------------------------
 
701
 
 
702
.. i18n: The previous piece of code will fail if the caller provided a *subclass* of ``list`` (which is possible and allowed), because ``==`` and ``is`` don't check for subtypes. ``isinstance`` does:
 
703
..
 
704
 
 
705
The previous piece of code will fail if the caller provided a *subclass* of ``list`` (which is possible and allowed), because ``==`` and ``is`` don't check for subtypes. ``isinstance`` does:
 
706
 
 
707
.. i18n: .. code-block:: python
 
708
.. i18n: 
 
709
.. i18n:         # shiny
 
710
.. i18n:         def axes(self, axis):
 
711
.. i18n:                 if isinstance(axis, list):
 
712
.. i18n:                         return list(axis) # clone the axis
 
713
.. i18n:                 else:
 
714
.. i18n:                         return [axis] # single-element list
 
715
..
 
716
 
 
717
.. code-block:: python
 
718
 
 
719
        # shiny
 
720
        def axes(self, axis):
 
721
                if isinstance(axis, list):
 
722
                        return list(axis) # clone the axis
 
723
                else:
 
724
                        return [axis] # single-element list
 
725
 
 
726
.. i18n: Don't create functions just to call callables
 
727
.. i18n: ---------------------------------------------
 
728
..
 
729
 
 
730
Don't create functions just to call callables
 
731
---------------------------------------------
 
732
 
 
733
.. i18n: .. code-block:: python
 
734
.. i18n: 
 
735
.. i18n:         # dumb, ``str`` is already callable
 
736
.. i18n:         parent_id = map(lambda x: str(x), parent_id.split(','))
 
737
.. i18n:         # better
 
738
.. i18n:         parent_id = map(str, parent_id.split(','))
 
739
..
 
740
 
 
741
.. code-block:: python
 
742
 
 
743
        # dumb, ``str`` is already callable
 
744
        parent_id = map(lambda x: str(x), parent_id.split(','))
 
745
        # better
 
746
        parent_id = map(str, parent_id.split(','))
 
747
 
 
748
.. i18n: Know your builtins
 
749
.. i18n: ------------------
 
750
..
 
751
 
 
752
Know your builtins
 
753
------------------
 
754
 
 
755
.. i18n: You should at least have a basic understanding of all the Python builtins (http://docs.python.org/library/functions.html)
 
756
..
 
757
 
 
758
You should at least have a basic understanding of all the Python builtins (http://docs.python.org/library/functions.html)
 
759
 
 
760
.. i18n: For example, ``isinstance`` can take more than one type as its second argument, so you could write:
 
761
..
 
762
 
 
763
For example, ``isinstance`` can take more than one type as its second argument, so you could write:
 
764
 
 
765
.. i18n: .. code-block:: python
 
766
.. i18n: 
 
767
.. i18n:         def axes(self, axis):
 
768
.. i18n:                 if isinstance(axis, (list, set, dict)):
 
769
.. i18n:                         return list(axis)
 
770
.. i18n:                 else:
 
771
.. i18n:                         return [axis]
 
772
..
 
773
 
 
774
.. code-block:: python
 
775
 
 
776
        def axes(self, axis):
 
777
                if isinstance(axis, (list, set, dict)):
 
778
                        return list(axis)
 
779
                else:
 
780
                        return [axis]
 
781
 
 
782
.. i18n: Another one is ``dict.get``, whose second argument defaults to ``None``:
 
783
..
 
784
 
 
785
Another one is ``dict.get``, whose second argument defaults to ``None``:
 
786
 
 
787
.. i18n: .. code-block:: python
 
788
.. i18n: 
 
789
.. i18n:         # very very redundant
 
790
.. i18n:         value = my_dict.get('key', None)
 
791
.. i18n:         # good
 
792
.. i18n:         value= my_dict.get('key')
 
793
..
 
794
 
 
795
.. code-block:: python
 
796
 
 
797
        # very very redundant
 
798
        value = my_dict.get('key', None)
 
799
        # good
 
800
        value= my_dict.get('key')
 
801
 
 
802
.. i18n: Also, ``if 'key' in my_dict`` and ``if my_dict.get('key')`` have very different meaning, be sure that you're using the right one.
 
803
..
 
804
 
 
805
Also, ``if 'key' in my_dict`` and ``if my_dict.get('key')`` have very different meaning, be sure that you're using the right one.
 
806
 
 
807
.. i18n: Learn list comprehensions
 
808
.. i18n: -------------------------
 
809
..
 
810
 
 
811
Learn list comprehensions
 
812
-------------------------
 
813
 
 
814
.. i18n: When used correctly, list comprehensions can greatly enhance the quality of a piece of code when mapping and/or filtering collections:
 
815
..
 
816
 
 
817
When used correctly, list comprehensions can greatly enhance the quality of a piece of code when mapping and/or filtering collections:
 
818
 
 
819
.. i18n: .. code-block:: python
 
820
.. i18n: 
 
821
.. i18n:         # not very good
 
822
.. i18n:         cube = []
 
823
.. i18n:         for i in res:
 
824
.. i18n:                 cube.append((i['id'],i['name']))
 
825
.. i18n:         # better
 
826
.. i18n:         cube = [(i['id'], i['name']) for i in res]
 
827
..
 
828
 
 
829
.. code-block:: python
 
830
 
 
831
        # not very good
 
832
        cube = []
 
833
        for i in res:
 
834
                cube.append((i['id'],i['name']))
 
835
        # better
 
836
        cube = [(i['id'], i['name']) for i in res]
 
837
 
 
838
.. i18n: But beware: with great power comes great responsibility, and list comprehensions can become overly complex. In that case, either revert back to "normal" ``for`` loops, extract functions or do your transformation in multiple steps
 
839
..
 
840
 
 
841
But beware: with great power comes great responsibility, and list comprehensions can become overly complex. In that case, either revert back to "normal" ``for`` loops, extract functions or do your transformation in multiple steps
 
842
 
 
843
.. i18n: Learn your standard library
 
844
.. i18n: ---------------------------
 
845
..
 
846
 
 
847
Learn your standard library
 
848
---------------------------
 
849
 
 
850
.. i18n: Python is provided "with batteries included", but these batteries are often
 
851
.. i18n: criminally underused. Some standard modules to know are ``itertools``,
 
852
.. i18n: ``operator`` and ``collections``, among others (though be careful to note the
 
853
.. i18n: python version at which functions and objects were introduced, don't break
 
854
.. i18n: compatibility with the officially supported versions for your tool):
 
855
..
 
856
 
 
857
Python is provided "with batteries included", but these batteries are often
 
858
criminally underused. Some standard modules to know are ``itertools``,
 
859
``operator`` and ``collections``, among others (though be careful to note the
 
860
python version at which functions and objects were introduced, don't break
 
861
compatibility with the officially supported versions for your tool):
 
862
 
 
863
.. i18n: .. code-block:: python
 
864
.. i18n: 
 
865
.. i18n:         # no
 
866
.. i18n:         cube = map(lambda i: (i['id'], i['name']), res)
 
867
.. i18n:         # still no
 
868
.. i18n:         cube = [(i['id'], i['name']) for i in res]
 
869
.. i18n:         # yes, with operator.itemgetter
 
870
.. i18n:         cube = map(itemgetter('id', 'name'), res)
 
871
..
 
872
 
 
873
.. code-block:: python
 
874
 
 
875
        # no
 
876
        cube = map(lambda i: (i['id'], i['name']), res)
 
877
        # still no
 
878
        cube = [(i['id'], i['name']) for i in res]
 
879
        # yes, with operator.itemgetter
 
880
        cube = map(itemgetter('id', 'name'), res)
 
881
 
 
882
.. i18n: Excellent resources for this are the official stdlib documentation (http://docs.python.org/library/index.html ) and Python Module of the Week (http://www.doughellmann.com/projects/PyMOTW/, do subscribe to its RSS feed)
 
883
..
 
884
 
 
885
Excellent resources for this are the official stdlib documentation (http://docs.python.org/library/index.html ) and Python Module of the Week (http://www.doughellmann.com/projects/PyMOTW/, do subscribe to its RSS feed)
 
886
 
 
887
.. i18n: Collections are booleans too
 
888
.. i18n: ----------------------------
 
889
..
 
890
 
 
891
Collections are booleans too
 
892
----------------------------
 
893
 
 
894
.. i18n: In python, many objects have "boolean-ish" value when evaluated in a boolean context (such as an ``if``). Among these are collections (lists, dicts, sets, …) which are "falsy" when empty and "truthy" when containing items:
 
895
..
 
896
 
 
897
In python, many objects have "boolean-ish" value when evaluated in a boolean context (such as an ``if``). Among these are collections (lists, dicts, sets, …) which are "falsy" when empty and "truthy" when containing items:
 
898
 
 
899
.. i18n: .. code-block:: python
 
900
.. i18n: 
 
901
.. i18n:         bool([]) is False
 
902
.. i18n:         bool([1]) is True
 
903
.. i18n:         bool([False]) is True
 
904
..
 
905
 
 
906
.. code-block:: python
 
907
 
 
908
        bool([]) is False
 
909
        bool([1]) is True
 
910
        bool([False]) is True
 
911
 
 
912
.. i18n: therefore, no need to call ``len``:
 
913
..
 
914
 
 
915
therefore, no need to call ``len``:
 
916
 
 
917
.. i18n: .. code-block:: python
 
918
.. i18n: 
 
919
.. i18n:         # redundant
 
920
.. i18n:         if len(some_collection):
 
921
.. i18n:                 "do something..."
 
922
.. i18n:         # simpler
 
923
.. i18n:         if some_collection:
 
924
.. i18n:                 "do something..."
 
925
..
 
926
 
 
927
.. code-block:: python
 
928
 
 
929
        # redundant
 
930
        if len(some_collection):
 
931
                "do something..."
 
932
        # simpler
 
933
        if some_collection:
 
934
                "do something..."
 
935
 
 
936
.. i18n: You can append a single object to a list, it's ok
 
937
.. i18n: -------------------------------------------------
 
938
..
 
939
 
 
940
You can append a single object to a list, it's ok
 
941
-------------------------------------------------
 
942
 
 
943
.. i18n: .. code-block:: python
 
944
.. i18n: 
 
945
.. i18n:         # no
 
946
.. i18n:         some_list += [some_item]
 
947
.. i18n:         # yes
 
948
.. i18n:         some_list.append(some_item)
 
949
.. i18n:         # very no
 
950
.. i18n:         view += [(code, values)]
 
951
.. i18n:         # yes
 
952
.. i18n:         view.append((code, values))
 
953
..
 
954
 
 
955
.. code-block:: python
 
956
 
 
957
        # no
 
958
        some_list += [some_item]
 
959
        # yes
 
960
        some_list.append(some_item)
 
961
        # very no
 
962
        view += [(code, values)]
 
963
        # yes
 
964
        view.append((code, values))
 
965
 
 
966
.. i18n: Add lists into bigger lists
 
967
.. i18n: ---------------------------
 
968
..
 
969
 
 
970
Add lists into bigger lists
 
971
---------------------------
 
972
 
 
973
.. i18n: .. code-block:: python
 
974
.. i18n: 
 
975
.. i18n:         # obscure
 
976
.. i18n:         my_list = []
 
977
.. i18n:         my_list += list1
 
978
.. i18n:         my_list += list2
 
979
.. i18n:         # simpler
 
980
.. i18n:         my_list = list1 + list2
 
981
..
 
982
 
 
983
.. code-block:: python
 
984
 
 
985
        # obscure
 
986
        my_list = []
 
987
        my_list += list1
 
988
        my_list += list2
 
989
        # simpler
 
990
        my_list = list1 + list2
 
991
 
 
992
.. i18n: Learn your standard library (2)
 
993
.. i18n: -------------------------------
 
994
..
 
995
 
 
996
Learn your standard library (2)
 
997
-------------------------------
 
998
 
 
999
.. i18n: Itertools is your friend for all things iterable:
 
1000
..
 
1001
 
 
1002
Itertools is your friend for all things iterable:
 
1003
 
 
1004
.. i18n: .. code-block:: python
 
1005
.. i18n: 
 
1006
.. i18n:         # ugly
 
1007
.. i18n:         my_list = []
 
1008
.. i18n:         my_list += list1
 
1009
.. i18n:         my_list += list2
 
1010
.. i18n:         for element in my_list:
 
1011
.. i18n:                 "do something..."
 
1012
.. i18n:         # unclear, creates a pointless temporary list
 
1013
.. i18n:         for element in list1 + list2:
 
1014
.. i18n:                 "do something..."
 
1015
.. i18n:         # says what I mean
 
1016
.. i18n:         for element in itertools.chain(list1, list2):
 
1017
.. i18n:                 "do something..."
 
1018
..
 
1019
 
 
1020
.. code-block:: python
 
1021
 
 
1022
        # ugly
 
1023
        my_list = []
 
1024
        my_list += list1
 
1025
        my_list += list2
 
1026
        for element in my_list:
 
1027
                "do something..."
 
1028
        # unclear, creates a pointless temporary list
 
1029
        for element in list1 + list2:
 
1030
                "do something..."
 
1031
        # says what I mean
 
1032
        for element in itertools.chain(list1, list2):
 
1033
                "do something..."
 
1034
 
 
1035
.. i18n: Iterate on iterables
 
1036
.. i18n: --------------------
 
1037
..
 
1038
 
 
1039
Iterate on iterables
 
1040
--------------------
 
1041
 
 
1042
.. i18n: .. code-block:: python
 
1043
.. i18n: 
 
1044
.. i18n:         # creates a temporary list and looks bar
 
1045
.. i18n:         for key in my_dict.keys():
 
1046
.. i18n:                 "do something..."
 
1047
.. i18n:         # better
 
1048
.. i18n:         for key in my_dict:
 
1049
.. i18n:                 "do something..."
 
1050
.. i18n:         # creates a temporary list
 
1051
.. i18n:         for key, value in my_dict.items():
 
1052
.. i18n:                 "do something..."
 
1053
.. i18n:         # only iterates
 
1054
.. i18n:         for key, value in my_dict.iteritems():
 
1055
.. i18n:                 "do something..."
 
1056
..
 
1057
 
 
1058
.. code-block:: python
 
1059
 
 
1060
        # creates a temporary list and looks bar
 
1061
        for key in my_dict.keys():
 
1062
                "do something..."
 
1063
        # better
 
1064
        for key in my_dict:
 
1065
                "do something..."
 
1066
        # creates a temporary list
 
1067
        for key, value in my_dict.items():
 
1068
                "do something..."
 
1069
        # only iterates
 
1070
        for key, value in my_dict.iteritems():
 
1071
                "do something..."
 
1072
 
 
1073
.. i18n: Chaining calls is ok, as long as you don't abuse it (too much)
 
1074
.. i18n: --------------------------------------------------------------
 
1075
..
 
1076
 
 
1077
Chaining calls is ok, as long as you don't abuse it (too much)
 
1078
--------------------------------------------------------------
 
1079
 
 
1080
.. i18n: .. code-block:: python
 
1081
.. i18n: 
 
1082
.. i18n:         # what's the point of the ``graph`` temporary variable?
 
1083
.. i18n:         # plus it shadows the ``graph`` module, bad move
 
1084
.. i18n:         graph = graph.Graph(kw)
 
1085
.. i18n:         mdx = ustr(graph.display())
 
1086
.. i18n:         # just as readable
 
1087
.. i18n:         mdx = ustr(grah.Graph(kw).display())
 
1088
..
 
1089
 
 
1090
.. code-block:: python
 
1091
 
 
1092
        # what's the point of the ``graph`` temporary variable?
 
1093
        # plus it shadows the ``graph`` module, bad move
 
1094
        graph = graph.Graph(kw)
 
1095
        mdx = ustr(graph.display())
 
1096
        # just as readable
 
1097
        mdx = ustr(grah.Graph(kw).display())
 
1098
 
 
1099
.. i18n: NOTE:
 
1100
..
 
1101
 
 
1102
NOTE:
 
1103
 
 
1104
.. i18n: yes, here the temporary variable `graph` is redundant but sometimes using such
 
1105
.. i18n: temporary variables simplify code debugging when you want to inspect the
 
1106
.. i18n: variable and you put breakpoint on the single line expression it's difficult to
 
1107
.. i18n: know when to do step-in and step-out.
 
1108
..
 
1109
 
 
1110
yes, here the temporary variable `graph` is redundant but sometimes using such
 
1111
temporary variables simplify code debugging when you want to inspect the
 
1112
variable and you put breakpoint on the single line expression it's difficult to
 
1113
know when to do step-in and step-out.
 
1114
 
 
1115
.. i18n: Use dict.setdefault
 
1116
.. i18n: -------------------
 
1117
..
 
1118
 
 
1119
Use dict.setdefault
 
1120
-------------------
 
1121
 
 
1122
.. i18n: If you need to modify a nested container for example:
 
1123
..
 
1124
 
 
1125
If you need to modify a nested container for example:
 
1126
 
 
1127
.. i18n: .. code-block:: python
 
1128
.. i18n: 
 
1129
.. i18n:         # longer.. harder to read
 
1130
.. i18n:         values = {}
 
1131
.. i18n:         for element in iterable:
 
1132
.. i18n:             if element not in values:
 
1133
.. i18n:                 values[element] = []
 
1134
.. i18n:             values[element].append(other_value)
 
1135
.. i18n: 
 
1136
.. i18n:         # better.. use dict.setdefault method
 
1137
.. i18n:         values = {}
 
1138
.. i18n:         for element in iterable:
 
1139
.. i18n:             values.setdefault(element, []).append(other_value)
 
1140
..
 
1141
 
 
1142
.. code-block:: python
 
1143
 
 
1144
        # longer.. harder to read
 
1145
        values = {}
 
1146
        for element in iterable:
 
1147
            if element not in values:
 
1148
                values[element] = []
 
1149
            values[element].append(other_value)
 
1150
 
 
1151
        # better.. use dict.setdefault method
 
1152
        values = {}
 
1153
        for element in iterable:
 
1154
            values.setdefault(element, []).append(other_value)
 
1155
 
 
1156
.. i18n: * `Documentation for dict.setdefault <http://docs.python.org/library/stdtypes.html?highlight=setdefault#dict.setdefault>`_
 
1157
..
 
1158
 
 
1159
* `Documentation for dict.setdefault <http://docs.python.org/library/stdtypes.html?highlight=setdefault#dict.setdefault>`_
 
1160
 
 
1161
.. i18n: Use constants and avoid magic numbers
 
1162
.. i18n: -------------------------------------
 
1163
..
 
1164
 
 
1165
Use constants and avoid magic numbers
 
1166
-------------------------------------
 
1167
 
 
1168
.. i18n: .. code-block:: python
 
1169
.. i18n: 
 
1170
.. i18n:         # bad
 
1171
.. i18n:         limit = 20
 
1172
.. i18n: 
 
1173
.. i18n:         # bad
 
1174
.. i18n:         search(cr, uid, ids, domain, limit=20, context=context)
 
1175
..
 
1176
 
 
1177
.. code-block:: python
 
1178
 
 
1179
        # bad
 
1180
        limit = 20
 
1181
 
 
1182
        # bad
 
1183
        search(cr, uid, ids, domain, limit=20, context=context)
 
1184
 
 
1185
.. i18n: You should use a constant, name it correctly, and perhaps add a comment on it
 
1186
.. i18n: explaining where the value comes from. And of course it's cleaner, easier
 
1187
.. i18n: to read and there's only one place to modify.
 
1188
.. i18n: Oh and that is true not just for numbers, but for any literal value that is
 
1189
.. i18n: semantically a constant!
 
1190
..
 
1191
 
 
1192
You should use a constant, name it correctly, and perhaps add a comment on it
 
1193
explaining where the value comes from. And of course it's cleaner, easier
 
1194
to read and there's only one place to modify.
 
1195
Oh and that is true not just for numbers, but for any literal value that is
 
1196
semantically a constant!
 
1197
 
 
1198
.. i18n: .. code-block:: python
 
1199
.. i18n: 
 
1200
.. i18n:         # better
 
1201
.. i18n:         DEFAULT_SEARCH_LIMIT = 20  # limit to 20 results due to screen size
 
1202
.. i18n: 
 
1203
.. i18n:         search(cr, uid, ids, domain, limit=DEFAULT_LIMIT, context=context)
 
1204
..
 
1205
 
 
1206
.. code-block:: python
 
1207
 
 
1208
        # better
 
1209
        DEFAULT_SEARCH_LIMIT = 20  # limit to 20 results due to screen size
 
1210
 
 
1211
        search(cr, uid, ids, domain, limit=DEFAULT_LIMIT, context=context)