SHOW:
|
|
- or go back to the newest paste.
| 1 | -----BEGIN PGP SIGNED MESSAGE----- | |
| 2 | Hash: SHA1 | |
| 3 | ||
| 4 | #!/usr/bin/env python3 | |
| 5 | # | |
| 6 | # So, I've got some free python training for you! This code works both | |
| 7 | # with python2 and python3. I love python3's print function, so let's add | |
| 8 | # it... | |
| 9 | ||
| 10 | from __future__ import print_function | |
| 11 | ||
| 12 | # Ok. First thing, did you know about namedtuple? It works as a regular | |
| 13 | # tuple, but you can access the fields by name (hence "named" tuple). | |
| 14 | # Besides, it is optimized for producing a large amount of instances. | |
| 15 | ||
| 16 | from collections import namedtuple | |
| 17 | ||
| 18 | # It works as a meta-type: you define types by calling a function. | |
| 19 | Row = namedtuple("Row", "name age city")
| |
| 20 | ||
| 21 | # ...but refer to the documentation (pydoc collections.namedtuple) for | |
| 22 | # that. Here a proof of how it works: | |
| 23 | ||
| 24 | r = Row("Luke", 40, "Amsterdam")
| |
| 25 | print("Showing Row:", r)
| |
| 26 | print("Showing fields of the row:", r.name, r.age, r.city)
| |
| 27 | ||
| 28 | # Let's take an hypotetical list of results. This Could be the output of | |
| 29 | # some stats file, or maybe from a database (even if the database will | |
| 30 | # probably have some inner type!). | |
| 31 | ||
| 32 | records = [ | |
| 33 | Row("Jane", 15, "London"),
| |
| 34 | Row("Georg", 15, "Berlin"),
| |
| 35 | Row("Urlika", 20, "Stockholm"),
| |
| 36 | Row("Johan", 17, "Stockholm"),
| |
| 37 | Row("Aldo", 9, "Rome"),
| |
| 38 | - | Row("Hans", 17, "Berlin")
|
| 38 | + | Row("Hans", 17, "Berlin"),
|
| 39 | Row("Petra", 21, "Berlin")
| |
| 40 | ] | |
| 41 | ||
| 42 | # About itertools? It's an extremely nice library of python, with useful | |
| 43 | # lazy-evaluated functions [ https://en.wikipedia.org/wiki/Lazy_evaluation ]. | |
| 44 | # Iterators in general allow to iterate (hence the word) on objects like | |
| 45 | # tuples or lists ("iterables").
| |
| 46 | ||
| 47 | import itertools as it | |
| 48 | ||
| 49 | # Iterators are very elegant, and if wisely used they can reduce the | |
| 50 | # memory footprint of a program, but on the minus side they get consumed: | |
| 51 | # you cannot use the same iterator twice. Example: | |
| 52 | ||
| 53 | # I can print lists twice: | |
| 54 | ||
| 55 | print('-' * 80)
| |
| 56 | print("Records")
| |
| 57 | for r in records: | |
| 58 | print("\t", r)
| |
| 59 | print("...second shot:")
| |
| 60 | for r in records: | |
| 61 | print("\t", r)
| |
| 62 | print("end.")
| |
| 63 | ||
| 64 | print('-' * 80)
| |
| 65 | ||
| 66 | # But iterators get consumed | |
| 67 | records_iterator = iter(records) | |
| 68 | print("Iterated records")
| |
| 69 | for r in records_iterator: | |
| 70 | print("\t", r)
| |
| 71 | print("...second shot (will be empty):")
| |
| 72 | for r in records_iterator: | |
| 73 | print("\t", r)
| |
| 74 | print("end.")
| |
| 75 | print('-' * 80)
| |
| 76 | ||
| 77 | # Also I'll include the "attrgetter" operator. Quoting the documentation: | |
| 78 | # | |
| 79 | # After, f=attrgetter('name'), the call f(r) returns r.name.
| |
| 80 | ||
| 81 | from operator import attrgetter as aget | |
| 82 | ||
| 83 | # For example, this is get_city: | |
| 84 | get_city = aget('city')
| |
| 85 | ||
| 86 | # Since the records are namedtuple with a field called `city`, | |
| 87 | # `aget('city')` will produce a function returning the `city` field:
| |
| 88 | r = Row("Luke", 40, "Amsterdam")
| |
| 89 | print("The city of Luke is:", get_city(r))
| |
| 90 | ||
| 91 | # Now some interesting use of the groupby function. | |
| 92 | # | |
| 93 | # It basically works as the "Group By" operator on SQL: group together | |
| 94 | # items of a table (in this case an iterator), and allow iteration over | |
| 95 | # single groups. The only caveat: items of the same group must be | |
| 96 | # contiguous, as for the "uniq" command of unix, which is often preceded | |
| 97 | # by the "sort" command, in pipe. | |
| 98 | # | |
| 99 | # For instance, let's take the `records` list, we defined previously, and | |
| 100 | # group the rows by the `city` attribute. The `get_city` we defined | |
| 101 | # before turns out to be a good grouping function So, those items have to | |
| 102 | # be sorted by the relevant field... | |
| 103 | records_sorted = sorted(records, key=get_city) | |
| 104 | ||
| 105 | # ...Then we can use the groupby: | |
| 106 | for city, rows in it.groupby(records_sorted, get_city): | |
| 107 | print("City:", city, end="\n\t")
| |
| 108 | print(*rows, sep="\n\t", end="\n\n") | |
| 109 | ||
| 110 | # Nice thing: this allows us to do some aggregation (e.g. sum, or average) | |
| 111 | # quite easily. Want to know the average age by city? | |
| 112 | for city, rows in it.groupby(records_sorted, get_city): | |
| 113 | print( | |
| 114 | "City:", city, | |
| 115 | "Avg.age:", sum(map(aget('age'), rows)),
| |
| 116 | end="\n\n" | |
| 117 | ) | |
| 118 | ||
| 119 | # Note: sorted() will return *a list*, not an iterator. That's why we can | |
| 120 | # use the it.groupby() function twice on it. If line 99 were | |
| 121 | # | |
| 122 | # records_sorted = iter(sorted(records, key=get_city)) | |
| 123 | # | |
| 124 | # Then the second cycle at line 108 would have not worked. Also you may | |
| 125 | # try to cycle with it.groupby on records instead of records_sorted: you | |
| 126 | # will see the results! :) | |
| 127 | ||
| 128 | # Happy hacking. | |
| 129 | ||
| 130 | -----BEGIN PGP SIGNATURE----- | |
| 131 | Version: GnuPG v1 | |
| 132 | ||
| 133 | - | iQIcBAEBAgAGBQJS3aTyAAoJEC+Zq7a6FN+eueYQALImcxP1jJaRrrxtaLqjAZqf |
| 133 | + | iQIcBAEBAgAGBQJS3achAAoJEC+Zq7a6FN+eurQP/j43RSd8kTDOsRGWIo2Xfm+U |
| 134 | - | weYjcOq634NuMg2LFdRUV7KFc0GuW6AClGokXSle9F7hGsJZaxaer1VknkN5vmnw |
| 134 | + | Ty1j4/GK8OugyWqNjUKoM+jhBMFuFGEINaRtK0PM4UOzlMyKvrFRSVezSvnj0yF6 |
| 135 | - | fzzs6soA+FN3fopjTmVxLXJvxrQNP+gSk4iuFF09KNU/E+UZfkKx5vpA+xg1wvr6 |
| 135 | + | zVPVTxVqRvONAi1+95k6j4CaaVWEZSvn3/jBjS7WfXk2bp1FsPcZfus3LkPQZmkn |
| 136 | - | R3Bi+jy8mDsz7zHkrv0sDdyIJF9ty6VZe3/afkM3TaaQN7/CRptpkGTJnA9bvkyJ |
| 136 | + | J6xZfDmhqlxT4AwTHU+lURaCm9U8qTa5oHImPt8HKr8toPuCT3pT8OPo2JLDb5i5 |
| 137 | - | eA9p5NyRD1eO9e6GScUeJ/Btn9sJ1kzfkYxdsLr4cRFSsRf3X0IymgUNOOpAAyH4 |
| 137 | + | +pUE+d3SsFFssRrRl0DgiQMJOFcqNRDv4QMrZHqJvz4MFq58gJxzwyBW0BNmgO/T |
| 138 | - | +gvyeUw8iowukJD3B0+YTQgfs7A9LUSZNHd+ElmC+eOJxAgmQwuhsxAvXN0I2seI |
| 138 | + | Pmn7VY37DfZLjMKDWB1Tfw7vVddRj1zezMQP3fwkeMM8koVCTfDsLvq5Mej8MHec |
| 139 | - | +CuNKmf+zF4TGY3bnQHjE2XBk4Q42m7N+gsN1wsd7t4gK/q5jK8Mf8eh8qvXE5tt |
| 139 | + | uouV2pht0aFem1OuspOXeaH/l1fUrCIKqG9deO9nZPyQQs9AF/c+/1Nvd126l3/6 |
| 140 | - | H8yLPc6y9kjglFLFzBtrskDKrKZ9M/rRPUJZJJOZ+rrkEAtOPq4JINKfhZL3tOsI |
| 140 | + | mZOdXQWoX8j3fNwBcZF4UhkQ6xksmvaJXaRGtEInLMjSa8HeCoxRQNqV+yCb8CHP |
| 141 | - | rYPKlBhbl8uEuy6w1lChsEn+yUE+Tct5XL/jF+hkLzlkWLCO9FB15Ta6XHxK+xff |
| 141 | + | ecTc5gxLA6N+sip+/5eLi1Joy3ViuCTZr1jZ5Mxis1kGVAFd904jS7HlOOeTPOyY |
| 142 | - | sssbeFOcarFra7ssKK1Hou8LPeoqkUy5AC2ywnp3oOMU3dk6+qXrG0+ROGp5IxXr |
| 142 | + | aXjEbhKWPE/HMmiaIMwflnvz4fQDy6K3fGXaHdbo6bUzPClF/jTt1O/We4/LOtcz |
| 143 | - | 0JiEGiykUmXqdqLAeSAwm8H2Ac7U9+8LT5g1FUTpMsvQezfburyUjmDVWLiaKcNE |
| 143 | + | pkij+MPtFI8eTiXUSuo9Quh/QVc095YMJiHAtS69m0EdyqgbWnRqQmM3lhRnGdcr |
| 144 | - | z6UCQu37bHVaRM0RmLqX |
| 144 | + | t2ymjyt6iUxFugniI0AN |
| 145 | - | =+CMW |
| 145 | + | =kl1F |
| 146 | -----END PGP SIGNATURE----- |