--- id: practical-python-2.24 source_exercise_id: "2.24" title: "First-class Data" section: "2.7 Objects" source_path: "02_Working_with_data/07_Objects.md" source_repo: "https://github.com/dabeaz-course/practical-python" source_commit: "93dca856b41c61a0a0f85ae334116e4c125629ea" student_visible_solution: false has_private_solution: false skip: false --- # Exercise 2.24: First-class Data > Source: Practical Python Programming, `02_Working_with_data/07_Objects.md`. ### Exercise 2.24: First-class Data In the file `Data/portfolio.csv`, we read data organized as columns that look like this: ```csv name,shares,price "AA",100,32.20 "IBM",50,91.10 ... ``` In previous code, we used the `csv` module to read the file, but still had to perform manual type conversions. For example: ```python for row in rows: name = row[0] shares = int(row[1]) price = float(row[2]) ``` This kind of conversion can also be performed in a more clever manner using some list basic operations. Make a Python list that contains the names of the conversion functions you would use to convert each column into the appropriate type: ```python >>> types = [str, int, float] >>> ``` The reason you can even create this list is that everything in Python is *first-class*. So, if you want to have a list of functions, that’s fine. The items in the list you created are functions for converting a value `x` into a given type (e.g., `str(x)`, `int(x)`, `float(x)`). Now, read a row of data from the above file: ```python >>> import csv >>> f = open('Data/portfolio.csv') >>> rows = csv.reader(f) >>> headers = next(rows) >>> row = next(rows) >>> row ['AA', '100', '32.20'] >>> ``` As noted, this row isn’t enough to do calculations because the types are wrong. For example: ```python >>> row[1] * row[2] Traceback (most recent call last): File "", line 1, in TypeError: can't multiply sequence by non-int of type 'str' >>> ``` However, maybe the data can be paired up with the types you specified in `types`. For example: ```python >>> types[1] >>> row[1] '100' >>> ``` Try converting one of the values: ```python >>> types[1](row[1]) # Same as int(row[1]) 100 >>> ``` Try converting a different value: ```python >>> types[2](row[2]) # Same as float(row[2]) 32.2 >>> ``` Try the calculation with converted values: ```python >>> types[1](row[1])*types[2](row[2]) 3220.0000000000005 >>> ``` Zip the column types with the fields and look at the result: ```python >>> r = list(zip(types, row)) >>> r [(, 'AA'), (, '100'), (,'32.20')] >>> ``` You will notice that this has paired a type conversion with a value. For example, `int` is paired with the value `'100'`. The zipped list is useful if you want to perform conversions on all of the values, one after the other. Try this: ```python >>> converted = [] >>> for func, val in zip(types, row): converted.append(func(val)) ... >>> converted ['AA', 100, 32.2] >>> converted[1] * converted[2] 3220.0000000000005 >>> ``` Make sure you understand what’s happening in the above code. In the loop, the `func` variable is one of the type conversion functions (e.g., `str`, `int`, etc.) and the `val` variable is one of the values like `'AA'`, `'100'`. The expression `func(val)` is converting a value (kind of like a type cast). The above code can be compressed into a single list comprehension. ```python >>> converted = [func(val) for func, val in zip(types, row)] >>> converted ['AA', 100, 32.2] >>> ``` ## 关联来源 - [[summaries/07_Objects]]