9
.. i18n: Python style guide
10
.. i18n: ++++++++++++++++++
11
.. i18n: In additions to these guidelines, you may also find the following link
17
In additions to these guidelines, you may also find the following link
20
.. i18n: * http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html (a little bit outdated, but quite relevant)
23
* http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html (a little bit outdated, but quite relevant)
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.
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.
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:
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:
39
.. i18n: .. code-block:: python
42
.. i18n: levels.__contains__(name)
44
.. i18n: name in levels
46
.. i18n: kw.__setitem__('nodes',nodes)
47
.. i18n: # much better
48
.. i18n: kw['nodes'] = nodes
51
.. code-block:: python
54
levels.__contains__(name)
58
kw.__setitem__('nodes',nodes)
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:
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:
71
.. i18n: .. code-block:: python
74
.. i18n: new_dict = my_dict.clone()
76
.. i18n: new_dict = dict(my_dict)
78
.. i18n: new_list = old_list.clone()
80
.. i18n: new_list = list(old_list)
83
.. code-block:: python
86
new_dict = my_dict.clone()
88
new_dict = dict(my_dict)
90
new_list = old_list.clone()
92
new_list = list(old_list)
94
.. i18n: And don't clone manually, please:
97
And don't clone manually, please:
99
.. i18n: .. code-block:: python
101
.. i18n: # surely you jest!
103
.. i18n: for val in view_items:
104
.. i18n: values += [val]
106
.. i18n: values = list(view_items)
109
.. code-block:: python
113
for val in view_items:
116
values = list(view_items)
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:
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:
127
.. i18n: .. code-block:: python
130
.. i18n: dictionary3 = dictionary1.clone()
131
.. i18n: dictionary3.update(dictionary2)
133
.. i18n: dictionary3 = {}
134
.. i18n: dictionary3.update(d1)
135
.. i18n: dictionary3.update(d2)
137
.. i18n: dictionary3 = dict(dictionary1, **dictionary2)
140
.. code-block:: python
143
dictionary3 = dictionary1.clone()
144
dictionary3.update(dictionary2)
147
dictionary3.update(d1)
148
dictionary3.update(d2)
150
dictionary3 = dict(dictionary1, **dictionary2)
157
.. i18n: You can use those properties for simpler operations, such as cloning an existing dict and (re) setting a key:
160
You can use those properties for simpler operations, such as cloning an existing dict and (re) setting a key:
162
.. i18n: .. code-block:: python
165
.. i18n: context = kw.clone()
166
.. i18n: context['foo'] = 'bar'
168
.. i18n: context = dict(kw, foo='bar')
171
.. code-block:: python
175
context['foo'] = 'bar'
177
context = dict(kw, foo='bar')
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.
186
the signature of dict.update is the same as ``dict()``: a single, optional, positional argument and an unlimited number of keyword arguments.
188
.. i18n: Thus the following are possible:
191
Thus the following are possible:
193
.. i18n: merging a dict from another one:
196
merging a dict from another one:
198
.. i18n: .. code-block:: python
201
.. i18n: for key, value in other_dict.iteritems():
202
.. i18n: my_dict[key] = value
204
.. i18n: my_dict.update(other_dict)
207
.. code-block:: python
210
for key, value in other_dict.iteritems():
213
my_dict.update(other_dict)
215
.. i18n: Setting a bunch of keys at the same time:
218
Setting a bunch of keys at the same time:
220
.. i18n: .. code-block:: python
223
.. i18n: my_dict['foo'] = 3
224
.. i18n: my_dict['bar'] = 4
225
.. i18n: my_dict['baz'] = 5
227
.. i18n: my_dict.update(
233
.. code-block:: python
245
.. i18n: Java dictionary creation
246
.. i18n: ------------------------
247
.. i18n: Python isn't java, it has literals:
250
Java dictionary creation
251
------------------------
252
Python isn't java, it has literals:
254
.. i18n: .. code-block:: python
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
269
.. code-block:: python
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:
290
keyword arguments are a good way to get a bunch of unspecified supplementary arguments if e.g. you just want to forward them:
292
.. i18n: .. code-block:: python
294
.. i18n: def foo(**kwargs):
295
.. i18n: logging.debug('Calling foo with arguments %s', kwargs)
296
.. i18n: return bar(**kwargs)
299
.. code-block:: python
302
logging.debug('Calling foo with arguments %s', kwargs)
305
.. i18n: or if you retrieved a ready-made dict (from another function) and want to pass its content to another function or method:
308
or if you retrieved a ready-made dict (from another function) and want to pass its content to another function or method:
310
.. i18n: .. code-block:: python
312
.. i18n: sessions = some_function_returning_a_dict_of_sessions()
313
.. i18n: some_other_function(**sessions)
316
.. code-block:: python
318
sessions = some_function_returning_a_dict_of_sessions()
319
some_other_function(**sessions)
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:
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:
331
.. i18n: .. code-block:: python
333
.. i18n: # waste of time and space
339
.. i18n: some_func(**my_dict)
341
.. i18n: some_func(foo=3, bar=4, baz=5)
344
.. code-block:: python
346
# waste of time and space
354
some_func(foo=3, bar=4, baz=5)
361
.. i18n: deprecated methods (formally or informally)
362
.. i18n: -------------------------------------------
363
.. i18n: ``dict.has_key(key)`` is deprecated, please use the ``in`` operator:
366
deprecated methods (formally or informally)
367
-------------------------------------------
368
``dict.has_key(key)`` is deprecated, please use the ``in`` operator:
370
.. i18n: .. code-block:: python
373
.. i18n: kw.has_key('cross_on_pages')
375
.. i18n: 'cross_on_pages' in kw
378
.. code-block:: python
381
kw.has_key('cross_on_pages')
383
'cross_on_pages' in kw
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:
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:
394
.. i18n: .. code-block:: python
397
.. i18n: schema = kw['schema']
398
.. i18n: params = {'schema': schema}
400
.. i18n: params = {'schema': kw['schema']}
403
.. code-block:: python
406
schema = kw['schema']
407
params = {'schema': schema}
409
params = {'schema': kw['schema']}
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:
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:
420
.. i18n: .. code-block:: python
422
.. i18n: col_axes = []
423
.. i18n: if kw.has_key('col_axis'):
424
.. i18n: col_axes = self.axes(kw['col_axis'])
427
.. code-block:: python
430
if kw.has_key('col_axis'):
431
col_axes = self.axes(kw['col_axis'])
433
.. i18n: and a second one:
438
.. i18n: .. code-block:: python
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'])
448
.. code-block:: python
451
if kw.has_key('col_axis'):
452
col_axes = self.axes(kw['col_axis'])
454
if kw.has_key('page_axis'):
455
page_axes = self.axes(kw['page_axis'])
457
.. i18n: But at the third strike, chances are you're going to need the thing again and again. Time to refactor:
460
But at the third strike, chances are you're going to need the thing again and again. Time to refactor:
462
.. i18n: .. code-block:: python
464
.. i18n: def get_axis(self, name, kw):
465
.. i18n: if name not in kw:
467
.. i18n: return self.axes(kw[name])
469
.. i18n: col_axes = self.get_axis('col_axis', kw)
470
.. i18n: page_axes = self.get_axis('page_axis', kw)
473
.. code-block:: python
475
def get_axis(self, name, kw):
478
return self.axes(kw[name])
480
col_axes = self.get_axis('col_axis', kw)
481
page_axes = self.get_axis('page_axis', kw)
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):
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):
488
.. i18n: .. code-block:: python
491
.. i18n: def axes(self, axis):
493
.. i18n: if type(axis) == type([]):
494
.. i18n: axes.extend(axis)
496
.. i18n: axes.append(axis)
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'])
511
.. i18n: def axes(self, axis):
512
.. i18n: if axis is None: return []
514
.. i18n: if type(axis) == type([]):
515
.. i18n: axes.extend(axis)
517
.. i18n: axes.append(axis)
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'))
526
.. code-block:: python
529
def axes(self, axis):
531
if type(axis) == type([]):
537
def output(self, **kw):
539
if kw.has_key('col_axis'):
540
col_axes = self.axes(kw['col_axis'])
542
if kw.has_key('page_axis'):
543
page_axes = self.axes(kw['page_axis'])
545
if kw.has_key('cross_on_rows'):
546
cross_on_rows = self.axes(kw['cross_on_rows'])
549
def axes(self, axis):
550
if axis is None: return []
552
if type(axis) == type([]):
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'))
568
.. i18n: Multiple return points are OK, when they're simpler
569
.. i18n: ---------------------------------------------------
572
Multiple return points are OK, when they're simpler
573
---------------------------------------------------
575
.. i18n: .. code-block:: python
577
.. i18n: # a bit complex and with a redundant temp variable
578
.. i18n: def axes(self, axis):
580
.. i18n: if type(axis) == type([]):
581
.. i18n: axes.extend(axis)
583
.. i18n: axes.append(axis)
587
.. i18n: def axes(self, axis):
588
.. i18n: if type(axis) == type([]):
589
.. i18n: return list(axis) # clone the axis
591
.. i18n: return [axis] # single-element list
594
.. code-block:: python
596
# a bit complex and with a redundant temp variable
597
def axes(self, axis):
599
if type(axis) == type([]):
606
def axes(self, axis):
607
if type(axis) == type([]):
608
return list(axis) # clone the axis
610
return [axis] # single-element list
612
.. i18n: Try to avoid type-testing
613
.. i18n: -------------------------
616
Try to avoid type-testing
617
-------------------------
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)
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)
628
.. i18n: Don't use ``type`` if you already know what the type you want is
629
.. i18n: ----------------------------------------------------------------
632
Don't use ``type`` if you already know what the type you want is
633
----------------------------------------------------------------
635
.. i18n: The type of a list is ``list``, the type of a dict is ``dict``:
638
The type of a list is ``list``, the type of a dict is ``dict``:
640
.. i18n: .. code-block:: python
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)
647
.. i18n: return [axis]
649
.. i18n: def axes(self, axis):
650
.. i18n: if type(axis) == list:
651
.. i18n: return list(axis)
653
.. i18n: return [axis]
656
.. code-block:: python
659
def axes(self, axis):
660
if type(axis) == type([]): # we already know what the type of [] is
665
def axes(self, axis):
666
if type(axis) == list:
671
.. i18n: plus Python types are singletons, so you can just test for identity, it reads better:
674
plus Python types are singletons, so you can just test for identity, it reads better:
676
.. i18n: .. code-block:: python
679
.. i18n: def axes(self, axis):
680
.. i18n: if type(axis) is list:
681
.. i18n: return list(axis)
683
.. i18n: return [axis]
686
.. code-block:: python
689
def axes(self, axis):
690
if type(axis) is list:
695
.. i18n: But really, if you need type testing just use the tools python provides
696
.. i18n: -----------------------------------------------------------------------
699
But really, if you need type testing just use the tools python provides
700
-----------------------------------------------------------------------
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:
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:
707
.. i18n: .. code-block:: python
710
.. i18n: def axes(self, axis):
711
.. i18n: if isinstance(axis, list):
712
.. i18n: return list(axis) # clone the axis
714
.. i18n: return [axis] # single-element list
717
.. code-block:: python
720
def axes(self, axis):
721
if isinstance(axis, list):
722
return list(axis) # clone the axis
724
return [axis] # single-element list
726
.. i18n: Don't create functions just to call callables
727
.. i18n: ---------------------------------------------
730
Don't create functions just to call callables
731
---------------------------------------------
733
.. i18n: .. code-block:: python
735
.. i18n: # dumb, ``str`` is already callable
736
.. i18n: parent_id = map(lambda x: str(x), parent_id.split(','))
738
.. i18n: parent_id = map(str, parent_id.split(','))
741
.. code-block:: python
743
# dumb, ``str`` is already callable
744
parent_id = map(lambda x: str(x), parent_id.split(','))
746
parent_id = map(str, parent_id.split(','))
748
.. i18n: Know your builtins
749
.. i18n: ------------------
755
.. i18n: You should at least have a basic understanding of all the Python builtins (http://docs.python.org/library/functions.html)
758
You should at least have a basic understanding of all the Python builtins (http://docs.python.org/library/functions.html)
760
.. i18n: For example, ``isinstance`` can take more than one type as its second argument, so you could write:
763
For example, ``isinstance`` can take more than one type as its second argument, so you could write:
765
.. i18n: .. code-block:: python
767
.. i18n: def axes(self, axis):
768
.. i18n: if isinstance(axis, (list, set, dict)):
769
.. i18n: return list(axis)
771
.. i18n: return [axis]
774
.. code-block:: python
776
def axes(self, axis):
777
if isinstance(axis, (list, set, dict)):
782
.. i18n: Another one is ``dict.get``, whose second argument defaults to ``None``:
785
Another one is ``dict.get``, whose second argument defaults to ``None``:
787
.. i18n: .. code-block:: python
789
.. i18n: # very very redundant
790
.. i18n: value = my_dict.get('key', None)
792
.. i18n: value= my_dict.get('key')
795
.. code-block:: python
797
# very very redundant
798
value = my_dict.get('key', None)
800
value= my_dict.get('key')
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.
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.
807
.. i18n: Learn list comprehensions
808
.. i18n: -------------------------
811
Learn list comprehensions
812
-------------------------
814
.. i18n: When used correctly, list comprehensions can greatly enhance the quality of a piece of code when mapping and/or filtering collections:
817
When used correctly, list comprehensions can greatly enhance the quality of a piece of code when mapping and/or filtering collections:
819
.. i18n: .. code-block:: python
821
.. i18n: # not very good
823
.. i18n: for i in res:
824
.. i18n: cube.append((i['id'],i['name']))
826
.. i18n: cube = [(i['id'], i['name']) for i in res]
829
.. code-block:: python
834
cube.append((i['id'],i['name']))
836
cube = [(i['id'], i['name']) for i in res]
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
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
843
.. i18n: Learn your standard library
844
.. i18n: ---------------------------
847
Learn your standard library
848
---------------------------
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):
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):
863
.. i18n: .. code-block:: python
866
.. i18n: cube = map(lambda i: (i['id'], i['name']), res)
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)
873
.. code-block:: python
876
cube = map(lambda i: (i['id'], i['name']), res)
878
cube = [(i['id'], i['name']) for i in res]
879
# yes, with operator.itemgetter
880
cube = map(itemgetter('id', 'name'), res)
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)
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)
887
.. i18n: Collections are booleans too
888
.. i18n: ----------------------------
891
Collections are booleans too
892
----------------------------
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:
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:
899
.. i18n: .. code-block:: python
901
.. i18n: bool([]) is False
902
.. i18n: bool([1]) is True
903
.. i18n: bool([False]) is True
906
.. code-block:: python
910
bool([False]) is True
912
.. i18n: therefore, no need to call ``len``:
915
therefore, no need to call ``len``:
917
.. i18n: .. code-block:: python
920
.. i18n: if len(some_collection):
921
.. i18n: "do something..."
923
.. i18n: if some_collection:
924
.. i18n: "do something..."
927
.. code-block:: python
930
if len(some_collection):
936
.. i18n: You can append a single object to a list, it's ok
937
.. i18n: -------------------------------------------------
940
You can append a single object to a list, it's ok
941
-------------------------------------------------
943
.. i18n: .. code-block:: python
946
.. i18n: some_list += [some_item]
948
.. i18n: some_list.append(some_item)
950
.. i18n: view += [(code, values)]
952
.. i18n: view.append((code, values))
955
.. code-block:: python
958
some_list += [some_item]
960
some_list.append(some_item)
962
view += [(code, values)]
964
view.append((code, values))
966
.. i18n: Add lists into bigger lists
967
.. i18n: ---------------------------
970
Add lists into bigger lists
971
---------------------------
973
.. i18n: .. code-block:: python
976
.. i18n: my_list = []
977
.. i18n: my_list += list1
978
.. i18n: my_list += list2
980
.. i18n: my_list = list1 + list2
983
.. code-block:: python
990
my_list = list1 + list2
992
.. i18n: Learn your standard library (2)
993
.. i18n: -------------------------------
996
Learn your standard library (2)
997
-------------------------------
999
.. i18n: Itertools is your friend for all things iterable:
1002
Itertools is your friend for all things iterable:
1004
.. i18n: .. code-block:: python
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..."
1020
.. code-block:: python
1026
for element in my_list:
1028
# unclear, creates a pointless temporary list
1029
for element in list1 + list2:
1032
for element in itertools.chain(list1, list2):
1035
.. i18n: Iterate on iterables
1036
.. i18n: --------------------
1039
Iterate on iterables
1040
--------------------
1042
.. i18n: .. code-block:: python
1044
.. i18n: # creates a temporary list and looks bar
1045
.. i18n: for key in my_dict.keys():
1046
.. i18n: "do something..."
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..."
1058
.. code-block:: python
1060
# creates a temporary list and looks bar
1061
for key in my_dict.keys():
1066
# creates a temporary list
1067
for key, value in my_dict.items():
1070
for key, value in my_dict.iteritems():
1073
.. i18n: Chaining calls is ok, as long as you don't abuse it (too much)
1074
.. i18n: --------------------------------------------------------------
1077
Chaining calls is ok, as long as you don't abuse it (too much)
1078
--------------------------------------------------------------
1080
.. i18n: .. code-block:: python
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())
1090
.. code-block:: python
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())
1097
mdx = ustr(grah.Graph(kw).display())
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.
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.
1115
.. i18n: Use dict.setdefault
1116
.. i18n: -------------------
1122
.. i18n: If you need to modify a nested container for example:
1125
If you need to modify a nested container for example:
1127
.. i18n: .. code-block:: python
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)
1136
.. i18n: # better.. use dict.setdefault method
1137
.. i18n: values = {}
1138
.. i18n: for element in iterable:
1139
.. i18n: values.setdefault(element, []).append(other_value)
1142
.. code-block:: python
1144
# longer.. harder to read
1146
for element in iterable:
1147
if element not in values:
1148
values[element] = []
1149
values[element].append(other_value)
1151
# better.. use dict.setdefault method
1153
for element in iterable:
1154
values.setdefault(element, []).append(other_value)
1156
.. i18n: * `Documentation for dict.setdefault <http://docs.python.org/library/stdtypes.html?highlight=setdefault#dict.setdefault>`_
1159
* `Documentation for dict.setdefault <http://docs.python.org/library/stdtypes.html?highlight=setdefault#dict.setdefault>`_
1161
.. i18n: Use constants and avoid magic numbers
1162
.. i18n: -------------------------------------
1165
Use constants and avoid magic numbers
1166
-------------------------------------
1168
.. i18n: .. code-block:: python
1174
.. i18n: search(cr, uid, ids, domain, limit=20, context=context)
1177
.. code-block:: python
1183
search(cr, uid, ids, domain, limit=20, context=context)
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!
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!
1198
.. i18n: .. code-block:: python
1201
.. i18n: DEFAULT_SEARCH_LIMIT = 20 # limit to 20 results due to screen size
1203
.. i18n: search(cr, uid, ids, domain, limit=DEFAULT_LIMIT, context=context)
1206
.. code-block:: python
1209
DEFAULT_SEARCH_LIMIT = 20 # limit to 20 results due to screen size
1211
search(cr, uid, ids, domain, limit=DEFAULT_LIMIT, context=context)