Initial commit
This commit is contained in:
parent
82e815fab2
commit
7d4b30154a
577564
Data/ctabus.csv
Normal file
577564
Data/ctabus.csv
Normal file
File diff suppressed because it is too large
Load Diff
2340
Data/dowstocks.csv
Executable file
2340
Data/dowstocks.csv
Executable file
@ -0,0 +1,2340 @@
|
||||
"AA",39.48,"6/11/2007","9:36am",-0.18,39.67,39.69,39.45,181800
|
||||
"AIG",71.38,"6/11/2007","9:36am",-0.15,71.29,71.60,71.15,195500
|
||||
"AXP",62.58,"6/11/2007","9:36am",-0.46,62.79,63.00,62.57,935000
|
||||
"BA",98.31,"6/11/2007","9:36am",+0.12,98.25,98.58,98.19,104800
|
||||
"C",53.08,"6/11/2007","9:36am",-0.25,53.20,53.25,53.03,360900
|
||||
"CAT",78.29,"6/11/2007","9:36am",-0.23,78.32,78.33,78.06,225400
|
||||
"DD",50.75,"6/11/2007","9:36am",-0.38,51.13,51.14,50.69,96900
|
||||
"DIS",34.20,"6/11/2007","9:35am",0.00,34.28,34.30,34.12,191000
|
||||
"GE",37.23,"6/11/2007","9:36am",-0.09,37.07,37.27,37.05,694300
|
||||
"GM",31.44,"6/11/2007","9:36am",+0.44,31.00,31.62,30.90,715429
|
||||
"HD",37.67,"6/11/2007","9:35am",-0.28,37.78,37.83,37.62,218369
|
||||
"HON",57.12,"6/11/2007","9:35am",-0.26,57.25,57.33,57.11,131100
|
||||
"HPQ",45.81,"6/11/2007","9:36am",+0.11,45.80,45.85,45.46,303200
|
||||
"IBM",102.86,"6/11/2007","9:35am",-0.21,102.87,102.99,102.50,151700
|
||||
"INTC",21.84,"6/11/2007","9:40am",+0.01,21.70,21.85,21.69,2927268
|
||||
"JNJ",62.25,"6/11/2007","9:35am",+0.12,62.89,62.89,62.15,343500
|
||||
"JPM",50.35,"6/11/2007","9:36am",-0.06,50.41,50.49,50.27,351100
|
||||
"KO",51.65,"6/11/2007","9:35am",-0.02,51.67,51.73,51.54,3981400
|
||||
"MCD",51.11,"6/11/2007","9:35am",-0.30,51.47,51.47,51.00,169100
|
||||
"MMM",85.60,"6/11/2007","9:35am",-0.34,85.94,85.98,85.50,190800
|
||||
"MO",70.09,"6/11/2007","9:36am",-0.21,70.25,70.30,70.04,471200
|
||||
"MRK",50.21,"6/11/2007","9:36am",+0.07,50.30,50.46,50.04,1453300
|
||||
"MSFT",30.08,"6/11/2007","9:41am",+0.03,30.05,30.09,29.93,6166010
|
||||
"PFE",26.40,"6/11/2007","9:36am",-0.12,26.50,26.50,26.34,835600
|
||||
"PG",62.79,"6/11/2007","9:35am",-0.28,62.80,62.87,62.75,256000
|
||||
"T",40.03,"6/11/2007","9:35am",-0.23,40.20,40.25,39.89,691400
|
||||
"UTX",69.81,"6/11/2007","9:36am",-0.42,69.85,70.20,69.51,153900
|
||||
"VZ",42.92,"6/11/2007","9:35am",-0.15,42.95,43.00,42.89,221000
|
||||
"WMT",49.78,"6/11/2007","9:36am",-0.30,49.90,50.00,49.76,676200
|
||||
"XOM",82.50,"6/11/2007","9:36am",-0.18,82.68,82.84,82.41,481200
|
||||
"AA",39.59,"6/11/2007","9:40am",-0.07,39.67,39.69,39.43,252600
|
||||
"AIG",71.46,"6/11/2007","9:40am",-0.07,71.29,71.60,71.15,276900
|
||||
"AXP",62.71,"6/11/2007","9:40am",-0.33,62.79,63.00,62.42,1002100
|
||||
"BA",98.31,"6/11/2007","9:40am",+0.12,98.25,98.58,98.17,149700
|
||||
"C",53.14,"6/11/2007","9:40am",-0.19,53.20,53.25,53.02,546500
|
||||
"CAT",78.49,"6/11/2007","9:40am",-0.03,78.32,78.49,78.06,262812
|
||||
"DD",50.85,"6/11/2007","9:40am",-0.28,51.13,51.14,50.69,155000
|
||||
"DIS",34.36,"6/11/2007","9:40am",+0.16,34.28,34.38,34.12,276900
|
||||
"GE",37.30,"6/11/2007","9:40am",-0.02,37.07,37.32,37.05,1039900
|
||||
"GM",31.40,"6/11/2007","9:40am",+0.40,31.00,31.62,30.90,1074079
|
||||
"HD",37.72,"6/11/2007","9:40am",-0.23,37.78,37.83,37.62,321769
|
||||
"HON",57.22,"6/11/2007","9:40am",-0.16,57.25,57.33,57.05,150400
|
||||
"HPQ",45.954,"6/11/2007","9:40am",+0.254,45.80,45.98,45.46,424600
|
||||
"IBM",102.95,"6/11/2007","9:40am",-0.12,102.87,102.99,102.50,229500
|
||||
"INTC",21.85,"6/11/2007","9:46am",+0.02,21.70,21.86,21.69,3605793
|
||||
"JNJ",62.42,"6/11/2007","9:40am",+0.29,62.89,62.89,62.15,433600
|
||||
"JPM",50.42,"6/11/2007","9:40am",+0.01,50.41,50.49,50.27,461400
|
||||
"KO",51.67,"6/11/2007","9:40am",0.00,51.67,51.73,51.54,4010650
|
||||
"MCD",51.42,"6/11/2007","9:40am",+0.01,51.47,51.47,50.98,245800
|
||||
"MMM",85.45,"6/11/2007","9:40am",-0.49,85.94,85.98,85.44,225500
|
||||
"MO",69.95,"6/11/2007","9:40am",-0.35,70.25,70.30,69.95,543600
|
||||
"MRK",50.58,"6/11/2007","9:40am",+0.44,50.30,50.63,50.04,1586100
|
||||
"MSFT",30.14,"6/11/2007","9:46am",+0.09,30.05,30.15,29.93,6758871
|
||||
"PFE",26.46,"6/11/2007","9:40am",-0.06,26.50,26.50,26.34,1101900
|
||||
"PG",62.97,"6/11/2007","9:40am",-0.10,62.80,63.00,62.75,431246
|
||||
"T",40.19,"6/11/2007","9:40am",-0.07,40.20,40.25,39.89,874100
|
||||
"UTX",69.88,"6/11/2007","9:40am",-0.35,69.85,70.20,69.51,191800
|
||||
"VZ",43.06,"6/11/2007","9:40am",-0.01,42.95,43.07,42.89,322700
|
||||
"WMT",49.72,"6/11/2007","9:40am",-0.36,49.90,50.00,49.70,822700
|
||||
"XOM",82.41,"6/11/2007","9:40am",-0.27,82.68,82.84,82.35,705500
|
||||
"AA",39.78,"6/11/2007","9:46am",+0.12,39.67,39.7946,39.43,347300
|
||||
"AIG",71.41,"6/11/2007","9:46am",-0.12,71.29,71.60,71.15,378000
|
||||
"AXP",62.87,"6/11/2007","9:46am",-0.17,62.79,63.00,62.42,1033500
|
||||
"BA",98.50,"6/11/2007","9:46am",+0.31,98.25,98.58,98.17,195100
|
||||
"C",53.13,"6/11/2007","9:46am",-0.20,53.20,53.25,53.02,715514
|
||||
"CAT",78.77,"6/11/2007","9:46am",+0.25,78.32,78.8128,78.06,329712
|
||||
"DD",50.81,"6/11/2007","9:46am",-0.32,51.13,51.14,50.69,198300
|
||||
"DIS",34.34,"6/11/2007","9:46am",+0.14,34.28,34.44,34.12,392500
|
||||
"GE",37.27,"6/11/2007","9:46am",-0.05,37.07,37.34,37.05,1311900
|
||||
"GM",31.42,"6/11/2007","9:46am",+0.42,31.00,31.62,30.90,1340279
|
||||
"HD",37.75,"6/11/2007","9:46am",-0.20,37.78,37.83,37.62,459769
|
||||
"HON",57.20,"6/11/2007","9:46am",-0.18,57.25,57.33,57.05,185700
|
||||
"HPQ",46.14,"6/11/2007","9:46am",+0.44,45.80,46.15,45.46,797800
|
||||
"IBM",103.39,"6/11/2007","9:46am",+0.32,102.87,103.47,102.50,413800
|
||||
"INTC",21.85,"6/11/2007","9:50am",+0.02,21.70,21.93,21.69,4380516
|
||||
"JNJ",62.52,"6/11/2007","9:46am",+0.39,62.89,62.89,62.15,548200
|
||||
"JPM",50.48,"6/11/2007","9:46am",+0.07,50.41,50.55,50.27,657600
|
||||
"KO",51.67,"6/11/2007","9:45am",0.00,51.67,51.77,51.54,4092550
|
||||
"MCD",51.36,"6/11/2007","9:46am",-0.05,51.47,51.47,50.98,343719
|
||||
"MMM",85.48,"6/11/2007","9:46am",-0.46,85.94,85.98,85.41,270900
|
||||
"MO",70.00,"6/11/2007","9:46am",-0.30,70.25,70.30,69.88,723100
|
||||
"MRK",50.50,"6/11/2007","9:46am",+0.36,50.30,50.63,50.04,1689100
|
||||
"MSFT",30.175,"6/11/2007","9:50am",+0.125,30.05,30.20,29.93,7234309
|
||||
"PFE",26.48,"6/11/2007","9:45am",-0.04,26.50,26.52,26.34,1363300
|
||||
"PG",62.89,"6/11/2007","9:46am",-0.18,62.80,63.00,62.75,553446
|
||||
"T",40.12,"6/11/2007","9:46am",-0.14,40.20,40.25,39.89,1073400
|
||||
"UTX",69.92,"6/11/2007","9:46am",-0.31,69.85,70.20,69.51,244600
|
||||
"VZ",43.09,"6/11/2007","9:46am",+0.02,42.95,43.17,42.89,515110
|
||||
"WMT",49.78,"6/11/2007","9:46am",-0.30,49.90,50.00,49.65,949000
|
||||
"XOM",82.60,"6/11/2007","9:46am",-0.08,82.68,82.84,82.35,934200
|
||||
"AA",40.15,"6/11/2007","9:51am",+0.49,39.67,40.15,39.43,510630
|
||||
"AIG",71.50,"6/11/2007","9:50am",-0.03,71.29,71.60,71.15,441900
|
||||
"AXP",62.99,"6/11/2007","9:50am",-0.05,62.79,63.07,62.42,1067820
|
||||
"BA",98.73,"6/11/2007","9:50am",+0.54,98.25,98.79,98.17,233900
|
||||
"C",53.15,"6/11/2007","9:51am",-0.18,53.20,53.25,53.02,885114
|
||||
"CAT",78.88,"6/11/2007","9:50am",+0.36,78.32,78.99,78.06,386652
|
||||
"DD",51.13,"6/11/2007","9:50am",0.00,51.13,51.14,50.69,274100
|
||||
"DIS",34.42,"6/11/2007","9:51am",+0.22,34.28,34.44,34.12,448200
|
||||
"GE",37.35,"6/11/2007","9:50am",+0.03,37.07,37.37,37.05,1541100
|
||||
"GM",31.28,"6/11/2007","9:50am",+0.28,31.00,31.62,30.90,1715679
|
||||
"HD",37.74,"6/11/2007","9:51am",-0.21,37.78,37.83,37.62,553869
|
||||
"HON",57.30,"6/11/2007","9:51am",-0.08,57.25,57.40,57.05,218000
|
||||
"HPQ",46.26,"6/11/2007","9:51am",+0.56,45.80,46.26,45.46,1497780
|
||||
"IBM",103.3781,"6/11/2007","9:50am",+0.3081,102.87,103.47,102.50,485800
|
||||
"INTC",21.83,"6/11/2007","9:55am",0.00,21.70,21.93,21.69,4802496
|
||||
"JNJ",62.68,"6/11/2007","9:51am",+0.55,62.89,62.89,62.15,756500
|
||||
"JPM",50.50,"6/11/2007","9:50am",+0.09,50.41,50.55,50.27,851700
|
||||
"KO",51.79,"6/11/2007","9:50am",+0.12,51.67,51.796,51.54,4175960
|
||||
"MCD",51.39,"6/11/2007","9:50am",-0.02,51.47,51.47,50.98,407819
|
||||
"MMM",85.74,"6/11/2007","9:50am",-0.20,85.94,85.98,85.41,311100
|
||||
"MO",70.08,"6/11/2007","9:51am",-0.22,70.25,70.30,69.88,840100
|
||||
"MRK",50.45,"6/11/2007","9:50am",+0.31,50.30,50.63,50.04,1830600
|
||||
"MSFT",30.22,"6/11/2007","9:56am",+0.17,30.05,30.24,29.93,7724578
|
||||
"PFE",26.45,"6/11/2007","9:51am",-0.07,26.50,26.52,26.34,1805950
|
||||
"PG",63.10,"6/11/2007","9:50am",+0.03,62.80,63.10,62.75,715146
|
||||
"T",40.16,"6/11/2007","9:50am",-0.10,40.20,40.25,39.89,1199500
|
||||
"UTX",70.06,"6/11/2007","9:50am",-0.17,69.85,70.20,69.51,270900
|
||||
"VZ",43.16,"6/11/2007","9:50am",+0.09,42.95,43.18,42.89,614810
|
||||
"WMT",49.87,"6/11/2007","9:51am",-0.21,49.90,50.00,49.65,1181700
|
||||
"XOM",82.77,"6/11/2007","9:50am",+0.09,82.68,82.86,82.35,1121100
|
||||
"AA",39.97,"6/11/2007","9:55am",+0.31,39.67,40.18,39.43,598130
|
||||
"AIG",71.43,"6/11/2007","9:55am",-0.10,71.29,71.60,71.15,487600
|
||||
"AXP",62.89,"6/11/2007","9:55am",-0.15,62.79,63.07,62.42,1084520
|
||||
"BA",98.66,"6/11/2007","9:56am",+0.47,98.25,98.79,98.17,265800
|
||||
"C",53.14,"6/11/2007","9:56am",-0.19,53.20,53.25,53.02,954314
|
||||
"CAT",78.87,"6/11/2007","9:55am",+0.35,78.32,78.99,78.06,425952
|
||||
"DD",51.18,"6/11/2007","9:55am",+0.05,51.13,51.21,50.69,341500
|
||||
"DIS",34.39,"6/11/2007","9:55am",+0.19,34.28,34.44,34.12,523200
|
||||
"GE",37.39,"6/11/2007","9:56am",+0.07,37.07,37.40,37.05,1786900
|
||||
"GM",31.31,"6/11/2007","9:55am",+0.31,31.00,31.62,30.90,2399379
|
||||
"HD",37.75,"6/11/2007","9:55am",-0.20,37.78,37.83,37.62,622969
|
||||
"HON",57.29,"6/11/2007","9:56am",-0.09,57.25,57.40,57.05,245000
|
||||
"HPQ",46.25,"6/11/2007","9:56am",+0.55,45.80,46.27,45.46,1615680
|
||||
"IBM",103.60,"6/11/2007","9:56am",+0.53,102.87,103.62,102.50,569900
|
||||
"INTC",21.85,"6/11/2007","10:00am",+0.02,21.70,21.95,21.69,5440945
|
||||
"JNJ",62.74,"6/11/2007","9:56am",+0.61,62.89,62.89,62.15,887700
|
||||
"JPM",50.48,"6/11/2007","9:56am",+0.07,50.41,50.55,50.27,924300
|
||||
"KO",51.63,"6/11/2007","9:56am",-0.04,51.67,51.82,51.54,4247360
|
||||
"MCD",51.40,"6/11/2007","9:56am",-0.01,51.47,51.47,50.98,448219
|
||||
"MMM",85.69,"6/11/2007","9:55am",-0.25,85.94,85.98,85.41,323200
|
||||
"MO",70.10,"6/11/2007","9:56am",-0.20,70.25,70.30,69.88,932900
|
||||
"MRK",50.38,"6/11/2007","9:55am",+0.24,50.30,50.63,50.04,1943600
|
||||
"MSFT",30.20,"6/11/2007","10:01am",+0.15,30.05,30.24,29.93,8053754
|
||||
"PFE",26.48,"6/11/2007","9:55am",-0.04,26.50,26.53,26.34,2029150
|
||||
"PG",63.07,"6/11/2007","9:56am",0.00,62.80,63.10,62.75,786646
|
||||
"T",40.08,"6/11/2007","9:56am",-0.18,40.20,40.25,39.89,1364200
|
||||
"UTX",70.06,"6/11/2007","9:56am",-0.17,69.85,70.20,69.51,289300
|
||||
"VZ",43.17,"6/11/2007","9:56am",+0.10,42.95,43.19,42.89,683810
|
||||
"WMT",49.88,"6/11/2007","9:56am",-0.20,49.90,50.00,49.65,1321100
|
||||
"XOM",82.68,"6/11/2007","9:56am",0.00,82.68,82.86,82.35,1235100
|
||||
"AA",39.94,"6/11/2007","10:00am",+0.28,39.67,40.18,39.43,660280
|
||||
"AIG",71.36,"6/11/2007","10:00am",-0.17,71.29,71.60,71.15,575042
|
||||
"AXP",62.83,"6/11/2007","10:00am",-0.21,62.79,63.07,62.42,1110520
|
||||
"BA",98.62,"6/11/2007","10:01am",+0.43,98.25,98.79,98.17,285700
|
||||
"C",53.06,"6/11/2007","10:01am",-0.27,53.20,53.25,53.02,1030199
|
||||
"CAT",78.66,"6/11/2007","10:00am",+0.14,78.32,78.99,78.06,452352
|
||||
"DD",51.09,"6/11/2007","10:01am",-0.04,51.13,51.21,50.69,402900
|
||||
"DIS",34.33,"6/11/2007","10:01am",+0.13,34.28,34.44,34.12,740300
|
||||
"GE",37.41,"6/11/2007","10:01am",+0.09,37.07,37.41,37.05,2106900
|
||||
"GM",31.29,"6/11/2007","10:01am",+0.29,31.00,31.62,30.90,2570679
|
||||
"HD",37.76,"6/11/2007","10:00am",-0.19,37.78,37.83,37.62,683769
|
||||
"HON",57.335,"6/11/2007","10:01am",-0.045,57.25,57.40,57.05,289100
|
||||
"HPQ",46.25,"6/11/2007","10:01am",+0.55,45.80,46.29,45.46,1752080
|
||||
"IBM",103.51,"6/11/2007","10:01am",+0.44,102.87,103.63,102.50,607300
|
||||
"INTC",21.85,"6/11/2007","10:05am",+0.02,21.70,21.95,21.69,5955030
|
||||
"JNJ",62.75,"6/11/2007","10:01am",+0.62,62.89,62.89,62.15,1031900
|
||||
"JPM",50.45,"6/11/2007","10:00am",+0.04,50.41,50.55,50.27,1010000
|
||||
"KO",51.55,"6/11/2007","10:00am",-0.12,51.67,51.82,51.54,4283460
|
||||
"MCD",51.34,"6/11/2007","10:01am",-0.07,51.47,51.47,50.98,481919
|
||||
"MMM",85.75,"6/11/2007","10:00am",-0.19,85.94,85.98,85.41,332600
|
||||
"MO",70.07,"6/11/2007","10:00am",-0.23,70.25,70.30,69.88,1007630
|
||||
"MRK",50.32,"6/11/2007","10:01am",+0.18,50.30,50.64,50.04,2165200
|
||||
"MSFT",30.21,"6/11/2007","10:05am",+0.16,30.05,30.25,29.93,8448925
|
||||
"PFE",26.45,"6/11/2007","10:01am",-0.07,26.50,26.53,26.34,2252150
|
||||
"PG",63.02,"6/11/2007","10:01am",-0.05,62.80,63.10,62.75,867746
|
||||
"T",40.05,"6/11/2007","10:01am",-0.21,40.20,40.25,39.89,1496200
|
||||
"UTX",70.00,"6/11/2007","10:00am",-0.23,69.85,70.20,69.51,308500
|
||||
"VZ",43.11,"6/11/2007","10:00am",+0.04,42.95,43.19,42.89,804110
|
||||
"WMT",49.78,"6/11/2007","10:01am",-0.30,49.90,50.00,49.65,1378500
|
||||
"XOM",82.71,"6/11/2007","10:00am",+0.03,82.68,82.86,82.35,1379100
|
||||
"AA",39.92,"6/11/2007","10:05am",+0.26,39.67,40.18,39.43,693080
|
||||
"AIG",71.29,"6/11/2007","10:05am",-0.24,71.29,71.60,71.15,630742
|
||||
"AXP",62.74,"6/11/2007","10:05am",-0.30,62.79,63.07,62.42,1149120
|
||||
"BA",98.47,"6/11/2007","10:06am",+0.28,98.25,98.79,98.17,306700
|
||||
"C",52.97,"6/11/2007","10:06am",-0.36,53.20,53.25,52.92,1191699
|
||||
"CAT",78.56,"6/11/2007","10:06am",+0.04,78.32,78.99,78.06,512752
|
||||
"DD",51.068,"6/11/2007","10:05am",-0.062,51.13,51.21,50.69,490000
|
||||
"DIS",34.29,"6/11/2007","10:05am",+0.09,34.28,34.44,34.12,790550
|
||||
"GE",37.36,"6/11/2007","10:06am",+0.04,37.07,37.41,37.05,2388519
|
||||
"GM",31.35,"6/11/2007","10:05am",+0.35,31.00,31.62,30.90,2770879
|
||||
"HD",37.75,"6/11/2007","10:06am",-0.20,37.78,37.83,37.62,909569
|
||||
"HON",57.22,"6/11/2007","10:06am",-0.16,57.25,57.40,57.05,307300
|
||||
"HPQ",46.22,"6/11/2007","10:05am",+0.52,45.80,46.29,45.46,1904480
|
||||
"IBM",103.57,"6/11/2007","10:05am",+0.50,102.87,103.63,102.50,666400
|
||||
"INTC",21.83,"6/11/2007","10:10am",0.00,21.70,21.95,21.69,6319637
|
||||
"JNJ",62.70,"6/11/2007","10:06am",+0.57,62.89,62.89,62.15,1138900
|
||||
"JPM",50.35,"6/11/2007","10:05am",-0.06,50.41,50.55,50.27,1112900
|
||||
"KO",51.57,"6/11/2007","10:06am",-0.10,51.67,51.82,51.53,4320060
|
||||
"MCD",51.31,"6/11/2007","10:06am",-0.10,51.47,51.47,50.98,533219
|
||||
"MMM",85.76,"6/11/2007","10:05am",-0.18,85.94,85.98,85.41,384000
|
||||
"MO",69.99,"6/11/2007","10:05am",-0.31,70.25,70.30,69.88,1191130
|
||||
"MRK",50.35,"6/11/2007","10:06am",+0.21,50.30,50.64,50.04,2250500
|
||||
"MSFT",30.18,"6/11/2007","10:11am",+0.13,30.05,30.25,29.93,9049059
|
||||
"PFE",26.41,"6/11/2007","10:05am",-0.11,26.50,26.53,26.34,2353550
|
||||
"PG",63.03,"6/11/2007","10:06am",-0.04,62.80,63.10,62.75,920246
|
||||
"T",40.04,"6/11/2007","10:06am",-0.22,40.20,40.25,39.89,1644100
|
||||
"UTX",69.95,"6/11/2007","10:05am",-0.28,69.85,70.20,69.51,332000
|
||||
"VZ",43.04,"6/11/2007","10:05am",-0.03,42.95,43.19,42.89,886910
|
||||
"WMT",49.70,"6/11/2007","10:05am",-0.38,49.90,50.00,49.65,1450500
|
||||
"XOM",82.8681,"6/11/2007","10:05am",+0.1881,82.68,82.89,82.35,1544600
|
||||
"AA",39.91,"6/11/2007","10:11am",+0.25,39.67,40.18,39.43,755880
|
||||
"AIG",71.29,"6/11/2007","10:11am",-0.24,71.29,71.60,71.15,706842
|
||||
"AXP",62.79,"6/11/2007","10:10am",-0.25,62.79,63.07,62.42,1183320
|
||||
"BA",98.45,"6/11/2007","10:11am",+0.26,98.25,98.79,98.17,344500
|
||||
"C",52.926,"6/11/2007","10:11am",-0.404,53.20,53.25,52.91,1347199
|
||||
"CAT",78.58,"6/11/2007","10:11am",+0.06,78.32,78.99,78.06,559652
|
||||
"DD",50.999,"6/11/2007","10:11am",-0.131,51.13,51.21,50.69,542900
|
||||
"DIS",34.29,"6/11/2007","10:11am",+0.09,34.28,34.44,34.12,836650
|
||||
"GE",37.38,"6/11/2007","10:11am",+0.06,37.07,37.41,37.05,2643919
|
||||
"GM",31.30,"6/11/2007","10:11am",+0.30,31.00,31.62,30.90,2943679
|
||||
"HD",37.75,"6/11/2007","10:11am",-0.20,37.78,37.83,37.62,1015369
|
||||
"HON",57.23,"6/11/2007","10:11am",-0.15,57.25,57.40,57.05,332200
|
||||
"HPQ",46.18,"6/11/2007","10:11am",+0.48,45.80,46.29,45.46,2006780
|
||||
"IBM",103.35,"6/11/2007","10:11am",+0.28,102.87,103.63,102.50,734000
|
||||
"INTC",21.83,"6/11/2007","10:16am",0.00,21.70,21.95,21.69,6790042
|
||||
"JNJ",62.71,"6/11/2007","10:11am",+0.58,62.89,62.89,62.15,1274000
|
||||
"JPM",50.28,"6/11/2007","10:11am",-0.13,50.41,50.55,50.26,1187600
|
||||
"KO",51.56,"6/11/2007","10:11am",-0.11,51.67,51.82,51.53,4370560
|
||||
"MCD",51.25,"6/11/2007","10:11am",-0.16,51.47,51.47,50.98,587519
|
||||
"MMM",85.78,"6/11/2007","10:10am",-0.16,85.94,85.98,85.41,414300
|
||||
"MO",69.95,"6/11/2007","10:11am",-0.35,70.25,70.30,69.88,1273530
|
||||
"MRK",50.29,"6/11/2007","10:11am",+0.15,50.30,50.64,50.04,2332500
|
||||
"MSFT",30.14,"6/11/2007","10:16am",+0.09,30.05,30.25,29.93,9998935
|
||||
"PFE",26.449,"6/11/2007","10:10am",-0.071,26.50,26.53,26.34,2551150
|
||||
"PG",63.07,"6/11/2007","10:10am",0.00,62.80,63.10,62.75,1010846
|
||||
"T",40.04,"6/11/2007","10:10am",-0.22,40.20,40.25,39.89,1922900
|
||||
"UTX",69.91,"6/11/2007","10:11am",-0.32,69.85,70.20,69.51,354300
|
||||
"VZ",43.10,"6/11/2007","10:10am",+0.03,42.95,43.19,42.89,998110
|
||||
"WMT",49.73,"6/11/2007","10:11am",-0.35,49.90,50.00,49.65,1577400
|
||||
"XOM",83.02,"6/11/2007","10:11am",+0.34,82.68,83.11,82.35,1789600
|
||||
"AA",39.76,"6/11/2007","10:15am",+0.10,39.67,40.18,39.43,804580
|
||||
"AIG",71.29,"6/11/2007","10:16am",-0.24,71.29,71.60,71.15,781442
|
||||
"AXP",62.69,"6/11/2007","10:16am",-0.35,62.79,63.07,62.42,1212120
|
||||
"BA",98.29,"6/11/2007","10:16am",+0.10,98.25,98.79,98.17,373200
|
||||
"C",52.91,"6/11/2007","10:16am",-0.42,53.20,53.25,52.84,1455899
|
||||
"CAT",78.51,"6/11/2007","10:16am",-0.01,78.32,78.99,78.06,607452
|
||||
"DD",51.02,"6/11/2007","10:16am",-0.11,51.13,51.21,50.69,576600
|
||||
"DIS",34.25,"6/11/2007","10:16am",+0.05,34.28,34.44,34.12,880150
|
||||
"GE",37.32,"6/11/2007","10:16am",0.00,37.07,37.41,37.05,2784119
|
||||
"GM",31.28,"6/11/2007","10:16am",+0.28,31.00,31.62,30.90,3270779
|
||||
"HD",37.76,"6/11/2007","10:16am",-0.19,37.78,37.83,37.62,1141469
|
||||
"HON",57.11,"6/11/2007","10:16am",-0.27,57.25,57.40,57.05,379600
|
||||
"HPQ",46.06,"6/11/2007","10:16am",+0.36,45.80,46.29,45.46,2073280
|
||||
"IBM",103.17,"6/11/2007","10:16am",+0.10,102.87,103.63,102.50,798600
|
||||
"INTC",21.84,"6/11/2007","10:20am",+0.01,21.70,21.95,21.69,7235829
|
||||
"JNJ",62.66,"6/11/2007","10:16am",+0.53,62.89,62.89,62.15,1412600
|
||||
"JPM",50.23,"6/11/2007","10:16am",-0.18,50.41,50.55,50.20,1304200
|
||||
"KO",51.52,"6/11/2007","10:16am",-0.15,51.67,51.82,51.50,4471160
|
||||
"MCD",51.17,"6/11/2007","10:15am",-0.24,51.47,51.47,50.98,640119
|
||||
"MMM",85.69,"6/11/2007","10:15am",-0.25,85.94,85.98,85.41,436200
|
||||
"MO",69.90,"6/11/2007","10:15am",-0.40,70.25,70.30,69.88,1350130
|
||||
"MRK",50.375,"6/11/2007","10:16am",+0.235,50.30,50.64,50.04,2501800
|
||||
"MSFT",30.105,"6/11/2007","10:21am",+0.055,30.05,30.25,29.93,11615862
|
||||
"PFE",26.43,"6/11/2007","10:16am",-0.09,26.50,26.53,26.34,2699150
|
||||
"PG",63.03,"6/11/2007","10:15am",-0.04,62.80,63.10,62.75,1084346
|
||||
"T",40.04,"6/11/2007","10:16am",-0.22,40.20,40.25,39.89,2049000
|
||||
"UTX",69.80,"6/11/2007","10:16am",-0.43,69.85,70.20,69.51,390200
|
||||
"VZ",43.12,"6/11/2007","10:16am",+0.05,42.95,43.19,42.89,1540810
|
||||
"WMT",49.71,"6/11/2007","10:16am",-0.37,49.90,50.00,49.65,1700900
|
||||
"XOM",82.79,"6/11/2007","10:16am",+0.11,82.68,83.11,82.35,1945300
|
||||
"AA",39.7264,"6/11/2007","10:21am",+0.0664,39.67,40.18,39.43,860780
|
||||
"AIG",71.28,"6/11/2007","10:21am",-0.25,71.29,71.60,71.15,826742
|
||||
"AXP",62.775,"6/11/2007","10:21am",-0.265,62.79,63.07,62.42,1262720
|
||||
"BA",98.17,"6/11/2007","10:21am",-0.02,98.25,98.79,98.15,420500
|
||||
"C",52.89,"6/11/2007","10:20am",-0.44,53.20,53.25,52.84,1554299
|
||||
"CAT",78.46,"6/11/2007","10:21am",-0.06,78.32,78.99,78.06,645152
|
||||
"DD",51.02,"6/11/2007","10:20am",-0.11,51.13,51.21,50.69,616000
|
||||
"DIS",34.29,"6/11/2007","10:21am",+0.09,34.28,34.44,34.12,961150
|
||||
"GE",37.31,"6/11/2007","10:21am",-0.01,37.07,37.41,37.05,2994719
|
||||
"GM",31.16,"6/11/2007","10:21am",+0.16,31.00,31.62,30.90,3450979
|
||||
"HD",37.75,"6/11/2007","10:20am",-0.20,37.78,37.83,37.62,1239969
|
||||
"HON",57.14,"6/11/2007","10:21am",-0.24,57.25,57.40,57.05,427800
|
||||
"HPQ",46.08,"6/11/2007","10:21am",+0.38,45.80,46.29,45.46,2169080
|
||||
"IBM",103.24,"6/11/2007","10:21am",+0.17,102.87,103.63,102.50,1110200
|
||||
"INTC",21.85,"6/11/2007","10:26am",+0.02,21.70,21.95,21.69,7668158
|
||||
"JNJ",62.65,"6/11/2007","10:21am",+0.52,62.89,62.89,62.15,1466978
|
||||
"JPM",50.14,"6/11/2007","10:21am",-0.27,50.41,50.55,50.13,1391100
|
||||
"KO",51.44,"6/11/2007","10:20am",-0.23,51.67,51.82,51.44,4528060
|
||||
"MCD",51.19,"6/11/2007","10:20am",-0.22,51.47,51.47,50.98,694019
|
||||
"MMM",85.58,"6/11/2007","10:20am",-0.36,85.94,85.98,85.41,455600
|
||||
"MO",69.86,"6/11/2007","10:21am",-0.44,70.25,70.30,69.76,1542230
|
||||
"MRK",50.34,"6/11/2007","10:21am",+0.20,50.30,50.64,50.04,2582400
|
||||
"MSFT",30.10,"6/11/2007","10:26am",+0.05,30.05,30.25,29.93,12004109
|
||||
"PFE",26.43,"6/11/2007","10:21am",-0.09,26.50,26.53,26.34,2986570
|
||||
"PG",63.06,"6/11/2007","10:21am",-0.01,62.80,63.10,62.75,1165646
|
||||
"T",40.04,"6/11/2007","10:20am",-0.22,40.20,40.25,39.89,2229600
|
||||
"UTX",69.66,"6/11/2007","10:21am",-0.57,69.85,70.20,69.51,420400
|
||||
"VZ",43.12,"6/11/2007","10:21am",+0.05,42.95,43.19,42.89,1615410
|
||||
"WMT",49.65,"6/11/2007","10:21am",-0.43,49.90,50.00,49.65,1851200
|
||||
"XOM",82.84,"6/11/2007","10:21am",+0.16,82.68,83.11,82.35,2132200
|
||||
"AA",39.63,"6/11/2007","10:26am",-0.03,39.67,40.18,39.43,899080
|
||||
"AIG",71.30,"6/11/2007","10:26am",-0.23,71.29,71.60,71.15,896542
|
||||
"AXP",62.80,"6/11/2007","10:26am",-0.24,62.79,63.07,62.42,1296620
|
||||
"BA",98.04,"6/11/2007","10:26am",-0.15,98.25,98.79,97.96,466900
|
||||
"C",52.91,"6/11/2007","10:26am",-0.42,53.20,53.25,52.81,1672699
|
||||
"CAT",78.37,"6/11/2007","10:26am",-0.15,78.32,78.99,78.06,704152
|
||||
"DD",50.94,"6/11/2007","10:25am",-0.19,51.13,51.21,50.69,643200
|
||||
"DIS",34.24,"6/11/2007","10:26am",+0.04,34.28,34.44,34.12,1025550
|
||||
"GE",37.26,"6/11/2007","10:26am",-0.06,37.07,37.41,37.05,3290619
|
||||
"GM",31.22,"6/11/2007","10:26am",+0.22,31.00,31.62,30.90,4096679
|
||||
"HD",37.75,"6/11/2007","10:26am",-0.20,37.78,37.83,37.62,1559369
|
||||
"HON",57.10,"6/11/2007","10:26am",-0.28,57.25,57.40,57.03,450900
|
||||
"HPQ",46.10,"6/11/2007","10:26am",+0.40,45.80,46.29,45.46,2260680
|
||||
"IBM",103.20,"6/11/2007","10:26am",+0.13,102.87,103.63,102.50,1147300
|
||||
"INTC",21.84,"6/11/2007","10:31am",+0.01,21.70,21.95,21.69,8125254
|
||||
"JNJ",62.55,"6/11/2007","10:26am",+0.42,62.89,62.89,62.15,1616778
|
||||
"JPM",50.14,"6/11/2007","10:26am",-0.27,50.41,50.55,50.05,1498700
|
||||
"KO",51.39,"6/11/2007","10:26am",-0.28,51.67,51.82,51.32,4607560
|
||||
"MCD",51.17,"6/11/2007","10:25am",-0.24,51.47,51.47,50.98,740919
|
||||
"MMM",85.51,"6/11/2007","10:26am",-0.43,85.94,85.98,85.41,478800
|
||||
"MO",69.85,"6/11/2007","10:26am",-0.45,70.25,70.30,69.76,1670405
|
||||
"MRK",50.32,"6/11/2007","10:25am",+0.18,50.30,50.64,50.04,2654700
|
||||
"MSFT",30.09,"6/11/2007","10:31am",+0.04,30.05,30.25,29.93,12221463
|
||||
"PFE",26.39,"6/11/2007","10:26am",-0.13,26.50,26.53,26.34,3224570
|
||||
"PG",63.04,"6/11/2007","10:25am",-0.03,62.80,63.10,62.75,1243746
|
||||
"T",40.001,"6/11/2007","10:26am",-0.259,40.20,40.25,39.89,2441800
|
||||
"UTX",69.63,"6/11/2007","10:26am",-0.60,69.85,70.20,69.51,446700
|
||||
"VZ",43.12,"6/11/2007","10:26am",+0.05,42.95,43.19,42.89,1681910
|
||||
"WMT",49.595,"6/11/2007","10:26am",-0.485,49.90,50.00,49.57,1951400
|
||||
"XOM",82.79,"6/11/2007","10:26am",+0.11,82.68,83.11,82.35,2330500
|
||||
"AA",39.555,"6/11/2007","10:31am",-0.105,39.67,40.18,39.43,944980
|
||||
"AIG",71.32,"6/11/2007","10:30am",-0.21,71.29,71.60,71.15,953042
|
||||
"AXP",62.79,"6/11/2007","10:31am",-0.25,62.79,63.07,62.42,1329020
|
||||
"BA",97.85,"6/11/2007","10:31am",-0.34,98.25,98.79,97.74,531200
|
||||
"C",52.92,"6/11/2007","10:31am",-0.41,53.20,53.25,52.81,1746999
|
||||
"CAT",78.43,"6/11/2007","10:30am",-0.09,78.32,78.99,78.06,751652
|
||||
"DD",50.88,"6/11/2007","10:30am",-0.25,51.13,51.21,50.69,698200
|
||||
"DIS",34.21,"6/11/2007","10:30am",+0.01,34.28,34.44,34.12,1097150
|
||||
"GE",37.28,"6/11/2007","10:31am",-0.04,37.07,37.41,37.05,3653019
|
||||
"GM",31.15,"6/11/2007","10:31am",+0.15,31.00,31.62,30.90,4256179
|
||||
"HD",37.75,"6/11/2007","10:31am",-0.20,37.78,37.83,37.62,1712669
|
||||
"HON",57.09,"6/11/2007","10:30am",-0.29,57.25,57.40,57.03,481100
|
||||
"HPQ",46.08,"6/11/2007","10:30am",+0.38,45.80,46.29,45.46,2341480
|
||||
"IBM",103.20,"6/11/2007","10:30am",+0.13,102.87,103.63,102.50,1182000
|
||||
"INTC",21.85,"6/11/2007","10:36am",+0.02,21.70,21.95,21.69,8728776
|
||||
"JNJ",62.59,"6/11/2007","10:31am",+0.46,62.89,62.89,62.15,1771778
|
||||
"JPM",50.13,"6/11/2007","10:30am",-0.28,50.41,50.55,50.05,1552000
|
||||
"KO",51.34,"6/11/2007","10:31am",-0.33,51.67,51.82,51.32,4669560
|
||||
"MCD",51.20,"6/11/2007","10:30am",-0.21,51.47,51.47,50.98,779219
|
||||
"MMM",85.54,"6/11/2007","10:30am",-0.40,85.94,85.98,85.41,502100
|
||||
"MO",69.89,"6/11/2007","10:31am",-0.41,70.25,70.30,69.76,1759605
|
||||
"MRK",50.35,"6/11/2007","10:30am",+0.21,50.30,50.64,50.04,2706300
|
||||
"MSFT",30.11,"6/11/2007","10:36am",+0.06,30.05,30.25,29.93,12723552
|
||||
"PFE",26.38,"6/11/2007","10:30am",-0.14,26.50,26.53,26.34,3420170
|
||||
"PG",63.07,"6/11/2007","10:31am",0.00,62.80,63.10,62.75,1285746
|
||||
"T",40.05,"6/11/2007","10:30am",-0.21,40.20,40.25,39.89,2609700
|
||||
"UTX",69.60,"6/11/2007","10:31am",-0.63,69.85,70.20,69.51,500000
|
||||
"VZ",43.15,"6/11/2007","10:30am",+0.08,42.95,43.19,42.89,1759910
|
||||
"WMT",49.61,"6/11/2007","10:31am",-0.47,49.90,50.00,49.57,2038300
|
||||
"XOM",82.69,"6/11/2007","10:31am",+0.01,82.68,83.11,82.35,2496800
|
||||
"AA",39.56,"6/11/2007","10:36am",-0.10,39.67,40.18,39.43,999880
|
||||
"AIG",71.37,"6/11/2007","10:36am",-0.16,71.29,71.60,71.15,991742
|
||||
"AXP",62.89,"6/11/2007","10:36am",-0.15,62.79,63.07,62.42,1356220
|
||||
"BA",98.00,"6/11/2007","10:36am",-0.19,98.25,98.79,97.74,561900
|
||||
"C",52.97,"6/11/2007","10:36am",-0.36,53.20,53.25,52.81,1864099
|
||||
"CAT",78.53,"6/11/2007","10:36am",+0.01,78.32,78.99,78.06,785752
|
||||
"DD",50.85,"6/11/2007","10:35am",-0.28,51.13,51.21,50.69,754500
|
||||
"DIS",34.22,"6/11/2007","10:36am",+0.02,34.28,34.44,34.12,1157550
|
||||
"GE",37.30,"6/11/2007","10:36am",-0.02,37.07,37.41,37.05,3926919
|
||||
"GM",31.15,"6/11/2007","10:36am",+0.15,31.00,31.62,30.90,4421279
|
||||
"HD",37.76,"6/11/2007","10:36am",-0.19,37.78,37.83,37.62,1911069
|
||||
"HON",57.08,"6/11/2007","10:36am",-0.30,57.25,57.40,57.00,530800
|
||||
"HPQ",46.01,"6/11/2007","10:35am",+0.31,45.80,46.29,45.46,2464880
|
||||
"IBM",103.19,"6/11/2007","10:36am",+0.12,102.87,103.63,102.50,1242400
|
||||
"INTC",21.88,"6/11/2007","10:41am",+0.05,21.70,21.95,21.69,9039718
|
||||
"JNJ",62.54,"6/11/2007","10:35am",+0.41,62.89,62.89,62.15,1848378
|
||||
"JPM",50.17,"6/11/2007","10:36am",-0.24,50.41,50.55,50.05,1615800
|
||||
"KO",51.37,"6/11/2007","10:35am",-0.30,51.67,51.82,51.32,4715160
|
||||
"MCD",51.19,"6/11/2007","10:36am",-0.22,51.47,51.47,50.98,862419
|
||||
"MMM",85.60,"6/11/2007","10:36am",-0.34,85.94,85.98,85.41,535100
|
||||
"MO",69.90,"6/11/2007","10:36am",-0.40,70.25,70.30,69.76,1818105
|
||||
"MRK",50.405,"6/11/2007","10:36am",+0.265,50.30,50.64,50.04,2773500
|
||||
"MSFT",30.08,"6/11/2007","10:41am",+0.03,30.05,30.25,29.93,13110210
|
||||
"PFE",26.37,"6/11/2007","10:36am",-0.15,26.50,26.53,26.34,3656096
|
||||
"PG",63.07,"6/11/2007","10:36am",0.00,62.80,63.10,62.75,1344746
|
||||
"T",40.03,"6/11/2007","10:36am",-0.23,40.20,40.25,39.89,2759900
|
||||
"UTX",69.6875,"6/11/2007","10:36am",-0.5425,69.85,70.20,69.51,573400
|
||||
"VZ",43.17,"6/11/2007","10:36am",+0.10,42.95,43.19,42.89,1809710
|
||||
"WMT",49.71,"6/11/2007","10:36am",-0.37,49.90,50.00,49.56,2171500
|
||||
"XOM",82.87,"6/11/2007","10:36am",+0.19,82.68,83.11,82.35,2657200
|
||||
"AA",39.59,"6/11/2007","10:41am",-0.07,39.67,40.18,39.43,1035980
|
||||
"AIG",71.38,"6/11/2007","10:41am",-0.15,71.29,71.60,71.15,1036342
|
||||
"AXP",62.99,"6/11/2007","10:40am",-0.05,62.79,63.07,62.42,1401320
|
||||
"BA",97.98,"6/11/2007","10:40am",-0.21,98.25,98.79,97.74,586200
|
||||
"C",53.04,"6/11/2007","10:41am",-0.29,53.20,53.25,52.81,1974394
|
||||
"CAT",78.47,"6/11/2007","10:40am",-0.05,78.32,78.99,78.06,811452
|
||||
"DD",50.75,"6/11/2007","10:41am",-0.38,51.13,51.21,50.69,816700
|
||||
"DIS",34.22,"6/11/2007","10:41am",+0.02,34.28,34.44,34.12,1264950
|
||||
"GE",37.32,"6/11/2007","10:41am",0.00,37.07,37.41,37.05,4056919
|
||||
"GM",31.15,"6/11/2007","10:41am",+0.15,31.00,31.62,30.90,4500679
|
||||
"HD",37.74,"6/11/2007","10:40am",-0.21,37.78,37.83,37.62,1949569
|
||||
"HON",57.05,"6/11/2007","10:40am",-0.33,57.25,57.40,57.00,566900
|
||||
"HPQ",45.95,"6/11/2007","10:41am",+0.25,45.80,46.29,45.46,2574530
|
||||
"IBM",103.23,"6/11/2007","10:40am",+0.16,102.87,103.63,102.50,1272000
|
||||
"INTC",21.88,"6/11/2007","10:46am",+0.05,21.70,21.95,21.69,9537433
|
||||
"JNJ",62.52,"6/11/2007","10:41am",+0.39,62.89,62.89,62.15,1896678
|
||||
"JPM",50.18,"6/11/2007","10:41am",-0.23,50.41,50.55,50.05,1682000
|
||||
"KO",51.38,"6/11/2007","10:40am",-0.29,51.67,51.82,51.32,4744560
|
||||
"MCD",51.20,"6/11/2007","10:40am",-0.21,51.47,51.47,50.98,903219
|
||||
"MMM",85.61,"6/11/2007","10:40am",-0.33,85.94,85.98,85.41,544900
|
||||
"MO",69.95,"6/11/2007","10:40am",-0.35,70.25,70.30,69.76,1858005
|
||||
"MRK",50.49,"6/11/2007","10:40am",+0.35,50.30,50.64,50.04,2889100
|
||||
"MSFT",30.022,"6/11/2007","10:45am",-0.028,30.05,30.25,29.93,13503536
|
||||
"PFE",26.365,"6/11/2007","10:40am",-0.155,26.50,26.53,26.34,3770896
|
||||
"PG",63.08,"6/11/2007","10:41am",+0.01,62.80,63.12,62.75,1435146
|
||||
"T",39.99,"6/11/2007","10:41am",-0.27,40.20,40.25,39.89,2921500
|
||||
"UTX",69.77,"6/11/2007","10:41am",-0.46,69.85,70.20,69.51,589900
|
||||
"VZ",43.19,"6/11/2007","10:40am",+0.12,42.95,43.19,42.89,1878010
|
||||
"WMT",49.625,"6/11/2007","10:41am",-0.455,49.90,50.00,49.56,2239000
|
||||
"XOM",82.89,"6/11/2007","10:40am",+0.21,82.68,83.11,82.35,2766800
|
||||
"AA",39.54,"6/11/2007","10:46am",-0.12,39.67,40.18,39.43,1080380
|
||||
"AIG",71.32,"6/11/2007","10:46am",-0.21,71.29,71.60,71.15,1071642
|
||||
"AXP",62.85,"6/11/2007","10:46am",-0.19,62.79,63.07,62.42,1441520
|
||||
"BA",98.00,"6/11/2007","10:45am",-0.19,98.25,98.79,97.74,632300
|
||||
"C",52.95,"6/11/2007","10:46am",-0.38,53.20,53.25,52.81,2045194
|
||||
"CAT",78.50,"6/11/2007","10:45am",-0.02,78.32,78.99,78.06,834252
|
||||
"DD",50.64,"6/11/2007","10:46am",-0.49,51.13,51.21,50.63,893400
|
||||
"DIS",34.21,"6/11/2007","10:45am",+0.01,34.28,34.44,34.12,1344850
|
||||
"GE",37.2627,"6/11/2007","10:46am",-0.0573,37.07,37.41,37.05,4537419
|
||||
"GM",31.13,"6/11/2007","10:45am",+0.13,31.00,31.62,30.90,4598979
|
||||
"HD",37.73,"6/11/2007","10:46am",-0.22,37.78,37.83,37.62,2039969
|
||||
"HON",57.05,"6/11/2007","10:45am",-0.33,57.25,57.40,57.00,611200
|
||||
"HPQ",45.89,"6/11/2007","10:46am",+0.19,45.80,46.29,45.46,2729030
|
||||
"IBM",103.19,"6/11/2007","10:46am",+0.12,102.87,103.63,102.50,1319400
|
||||
"INTC",21.90,"6/11/2007","10:51am",+0.07,21.70,21.95,21.69,9897575
|
||||
"JNJ",62.42,"6/11/2007","10:46am",+0.29,62.89,62.89,62.15,1948833
|
||||
"JPM",50.20,"6/11/2007","10:46am",-0.21,50.41,50.55,50.05,1760600
|
||||
"KO",51.38,"6/11/2007","10:46am",-0.29,51.67,51.82,51.32,4787060
|
||||
"MCD",51.21,"6/11/2007","10:45am",-0.20,51.47,51.47,50.98,957919
|
||||
"MMM",85.54,"6/11/2007","10:45am",-0.40,85.94,85.98,85.41,565800
|
||||
"MO",69.96,"6/11/2007","10:45am",-0.34,70.25,70.30,69.76,1992005
|
||||
"MRK",50.41,"6/11/2007","10:45am",+0.27,50.30,50.64,50.04,2985100
|
||||
"MSFT",30.04,"6/11/2007","10:50am",-0.01,30.05,30.25,29.93,13852152
|
||||
"PFE",26.37,"6/11/2007","10:46am",-0.15,26.50,26.53,26.34,4020496
|
||||
"PG",63.02,"6/11/2007","10:45am",-0.05,62.80,63.12,62.75,1542146
|
||||
"T",39.95,"6/11/2007","10:46am",-0.31,40.20,40.25,39.89,3119000
|
||||
"UTX",69.72,"6/11/2007","10:45am",-0.51,69.85,70.20,69.51,611500
|
||||
"VZ",43.18,"6/11/2007","10:45am",+0.11,42.95,43.20,42.89,1971710
|
||||
"WMT",49.60,"6/11/2007","10:46am",-0.48,49.90,50.00,49.56,2280600
|
||||
"XOM",82.72,"6/11/2007","10:46am",+0.04,82.68,83.11,82.35,2888400
|
||||
"AA",39.65,"6/11/2007","10:50am",-0.01,39.67,40.18,39.43,1109080
|
||||
"AIG",71.44,"6/11/2007","10:50am",-0.09,71.29,71.60,71.15,1102442
|
||||
"AXP",62.83,"6/11/2007","10:50am",-0.21,62.79,63.07,62.42,1484920
|
||||
"BA",98.11,"6/11/2007","10:51am",-0.08,98.25,98.79,97.74,653100
|
||||
"C",53.01,"6/11/2007","10:51am",-0.32,53.20,53.25,52.81,2147094
|
||||
"CAT",78.56,"6/11/2007","10:50am",+0.04,78.32,78.99,78.06,857252
|
||||
"DD",50.78,"6/11/2007","10:50am",-0.35,51.13,51.21,50.62,931500
|
||||
"DIS",34.24,"6/11/2007","10:51am",+0.04,34.28,34.44,34.12,1456950
|
||||
"GE",37.29,"6/11/2007","10:51am",-0.03,37.07,37.41,37.05,6374978
|
||||
"GM",31.17,"6/11/2007","10:50am",+0.17,31.00,31.62,30.90,4686579
|
||||
"HD",37.745,"6/11/2007","10:51am",-0.205,37.78,37.83,37.62,2111769
|
||||
"HON",57.14,"6/11/2007","10:51am",-0.24,57.25,57.40,57.00,672700
|
||||
"HPQ",46.03,"6/11/2007","10:51am",+0.33,45.80,46.29,45.46,2822530
|
||||
"IBM",103.39,"6/11/2007","10:51am",+0.32,102.87,103.63,102.50,1365400
|
||||
"INTC",21.89,"6/11/2007","10:56am",+0.06,21.70,21.95,21.69,10794661
|
||||
"JNJ",62.60,"6/11/2007","10:51am",+0.47,62.89,62.89,62.15,2051122
|
||||
"JPM",50.25,"6/11/2007","10:50am",-0.16,50.41,50.55,50.05,1853000
|
||||
"KO",51.46,"6/11/2007","10:51am",-0.21,51.67,51.82,51.32,4861660
|
||||
"MCD",51.34,"6/11/2007","10:50am",-0.07,51.47,51.47,50.98,999019
|
||||
"MMM",85.53,"6/11/2007","10:50am",-0.41,85.94,85.98,85.41,582200
|
||||
"MO",70.0411,"6/11/2007","10:51am",-0.2589,70.25,70.30,69.76,2064105
|
||||
"MRK",50.48,"6/11/2007","10:51am",+0.34,50.30,50.64,50.04,3053000
|
||||
"MSFT",30.01,"6/11/2007","10:56am",-0.04,30.05,30.25,29.93,14553915
|
||||
"PFE",26.385,"6/11/2007","10:51am",-0.135,26.50,26.53,26.34,4373096
|
||||
"PG",63.04,"6/11/2007","10:51am",-0.03,62.80,63.12,62.75,1613346
|
||||
"T",39.97,"6/11/2007","10:51am",-0.29,40.20,40.25,39.89,3263300
|
||||
"UTX",69.79,"6/11/2007","10:51am",-0.44,69.85,70.20,69.51,629700
|
||||
"VZ",43.19,"6/11/2007","10:50am",+0.12,42.95,43.20,42.89,2067610
|
||||
"WMT",49.70,"6/11/2007","10:51am",-0.38,49.90,50.00,49.56,2349800
|
||||
"XOM",82.73,"6/11/2007","10:51am",+0.05,82.68,83.11,82.35,2994800
|
||||
"AA",39.67,"6/11/2007","10:56am",+0.01,39.67,40.18,39.43,1139080
|
||||
"AIG",71.45,"6/11/2007","10:55am",-0.08,71.29,71.60,71.15,1141342
|
||||
"AXP",62.94,"6/11/2007","10:56am",-0.10,62.79,63.07,62.42,1517420
|
||||
"BA",98.18,"6/11/2007","10:56am",-0.01,98.25,98.79,97.74,695900
|
||||
"C",53.02,"6/11/2007","10:56am",-0.31,53.20,53.25,52.81,2245494
|
||||
"CAT",78.57,"6/11/2007","10:56am",+0.05,78.32,78.99,78.06,892252
|
||||
"DD",50.8276,"6/11/2007","10:55am",-0.3024,51.13,51.21,50.62,991900
|
||||
"DIS",34.23,"6/11/2007","10:56am",+0.03,34.28,34.44,34.12,1533350
|
||||
"GE",37.34,"6/11/2007","10:56am",+0.02,37.07,37.41,37.05,6871678
|
||||
"GM",31.20,"6/11/2007","10:56am",+0.20,31.00,31.62,30.90,4845779
|
||||
"HD",37.74,"6/11/2007","10:56am",-0.21,37.78,37.83,37.62,2282669
|
||||
"HON",57.17,"6/11/2007","10:56am",-0.21,57.25,57.40,57.00,700800
|
||||
"HPQ",46.07,"6/11/2007","10:56am",+0.37,45.80,46.29,45.46,2941930
|
||||
"IBM",103.48,"6/11/2007","10:56am",+0.41,102.87,103.63,102.50,1449900
|
||||
"INTC",21.93,"6/11/2007","11:01am",+0.10,21.70,21.95,21.69,11463765
|
||||
"JNJ",62.47,"6/11/2007","10:56am",+0.34,62.89,62.89,62.15,2184222
|
||||
"JPM",50.27,"6/11/2007","10:56am",-0.14,50.41,50.55,50.05,1992300
|
||||
"KO",51.45,"6/11/2007","10:56am",-0.22,51.67,51.82,51.32,4959242
|
||||
"MCD",51.39,"6/11/2007","10:56am",-0.02,51.47,51.47,50.98,1065114
|
||||
"MMM",85.58,"6/11/2007","10:56am",-0.36,85.94,85.98,85.41,603300
|
||||
"MO",70.09,"6/11/2007","10:56am",-0.21,70.25,70.30,69.76,2152505
|
||||
"MRK",50.45,"6/11/2007","10:56am",+0.31,50.30,50.64,50.04,3120700
|
||||
"MSFT",29.985,"6/11/2007","11:01am",-0.065,30.05,30.25,29.93,15025412
|
||||
"PFE",26.35,"6/11/2007","10:55am",-0.17,26.50,26.53,26.34,4707596
|
||||
"PG",63.01,"6/11/2007","10:56am",-0.06,62.80,63.12,62.75,1804846
|
||||
"T",40.01,"6/11/2007","10:56am",-0.25,40.20,40.25,39.89,3433900
|
||||
"UTX",69.90,"6/11/2007","10:56am",-0.33,69.85,70.20,69.51,647800
|
||||
"VZ",43.17,"6/11/2007","10:56am",+0.10,42.95,43.20,42.89,2246610
|
||||
"WMT",49.61,"6/11/2007","10:56am",-0.47,49.90,50.00,49.56,2553200
|
||||
"XOM",82.90,"6/11/2007","10:56am",+0.22,82.68,83.11,82.35,3157200
|
||||
"AA",39.65,"6/11/2007","11:01am",-0.01,39.67,40.18,39.43,1162580
|
||||
"AIG",71.46,"6/11/2007","11:01am",-0.07,71.29,71.60,71.15,1167442
|
||||
"AXP",62.99,"6/11/2007","11:00am",-0.05,62.79,63.07,62.42,1532720
|
||||
"BA",98.09,"6/11/2007","11:01am",-0.10,98.25,98.79,97.74,714300
|
||||
"C",53.11,"6/11/2007","11:01am",-0.22,53.20,53.25,52.81,2344994
|
||||
"CAT",78.52,"6/11/2007","11:00am",0.00,78.32,78.99,78.06,915752
|
||||
"DD",50.75,"6/11/2007","11:01am",-0.38,51.13,51.21,50.62,1025100
|
||||
"DIS",34.22,"6/11/2007","11:01am",+0.02,34.28,34.44,34.12,1567750
|
||||
"GE",37.32,"6/11/2007","11:01am",0.00,37.07,37.41,37.05,7016378
|
||||
"GM",31.22,"6/11/2007","11:01am",+0.22,31.00,31.62,30.90,4944879
|
||||
"HD",37.73,"6/11/2007","11:01am",-0.22,37.78,37.83,37.62,2556369
|
||||
"HON",57.10,"6/11/2007","11:01am",-0.28,57.25,57.40,57.00,794183
|
||||
"HPQ",46.06,"6/11/2007","11:01am",+0.36,45.80,46.29,45.46,3023656
|
||||
"IBM",103.37,"6/11/2007","11:01am",+0.30,102.87,103.63,102.50,1483400
|
||||
"INTC",21.97,"6/11/2007","11:06am",+0.14,21.70,21.97,21.69,12553376
|
||||
"JNJ",62.46,"6/11/2007","11:01am",+0.33,62.89,62.89,62.15,2309522
|
||||
"JPM",50.32,"6/11/2007","11:01am",-0.09,50.41,50.55,50.05,2110500
|
||||
"KO",51.42,"6/11/2007","11:01am",-0.25,51.67,51.82,51.32,5000442
|
||||
"MCD",51.50,"6/11/2007","11:01am",+0.09,51.47,51.50,50.98,1190914
|
||||
"MMM",85.62,"6/11/2007","11:00am",-0.32,85.94,85.98,85.41,620500
|
||||
"MO",70.12,"6/11/2007","11:01am",-0.18,70.25,70.30,69.76,2246305
|
||||
"MRK",50.505,"6/11/2007","11:01am",+0.365,50.30,50.64,50.04,3232600
|
||||
"MSFT",30.02,"6/11/2007","11:06am",-0.03,30.05,30.25,29.93,15371602
|
||||
"PFE",26.34,"6/11/2007","11:01am",-0.18,26.50,26.53,26.33,5134096
|
||||
"PG",62.98,"6/11/2007","11:01am",-0.09,62.80,63.12,62.75,1854046
|
||||
"T",40.02,"6/11/2007","11:01am",-0.24,40.20,40.25,39.89,3531300
|
||||
"UTX",69.91,"6/11/2007","11:00am",-0.32,69.85,70.20,69.51,658800
|
||||
"VZ",43.16,"6/11/2007","11:01am",+0.09,42.95,43.20,42.89,2288410
|
||||
"WMT",49.59,"6/11/2007","11:01am",-0.49,49.90,50.00,49.56,2659033
|
||||
"XOM",82.98,"6/11/2007","11:01am",+0.30,82.68,83.11,82.35,3296100
|
||||
"AA",39.60,"6/11/2007","11:06am",-0.06,39.67,40.18,39.43,1189680
|
||||
"AIG",71.49,"6/11/2007","11:06am",-0.04,71.29,71.60,71.15,1226342
|
||||
"AXP",63.11,"6/11/2007","11:06am",+0.07,62.79,63.11,62.42,1576120
|
||||
"BA",98.20,"6/11/2007","11:06am",+0.01,98.25,98.79,97.74,743800
|
||||
"C",53.17,"6/11/2007","11:06am",-0.16,53.20,53.25,52.81,2445294
|
||||
"CAT",78.70,"6/11/2007","11:06am",+0.18,78.32,78.99,78.06,950252
|
||||
"DD",50.73,"6/11/2007","11:06am",-0.40,51.13,51.21,50.62,1165000
|
||||
"DIS",34.24,"6/11/2007","11:06am",+0.04,34.28,34.44,34.12,1636250
|
||||
"GE",37.3314,"6/11/2007","11:06am",+0.0114,37.07,37.41,37.05,7140328
|
||||
"GM",31.19,"6/11/2007","11:06am",+0.19,31.00,31.62,30.90,5065479
|
||||
"HD",37.72,"6/11/2007","11:06am",-0.23,37.78,37.83,37.62,2746169
|
||||
"HON",57.04,"6/11/2007","11:06am",-0.34,57.25,57.40,57.00,851383
|
||||
"HPQ",46.09,"6/11/2007","11:06am",+0.39,45.80,46.29,45.46,3113456
|
||||
"IBM",103.35,"6/11/2007","11:05am",+0.28,102.87,103.63,102.50,1528300
|
||||
"INTC",21.95,"6/11/2007","11:11am",+0.12,21.70,21.98,21.69,13630344
|
||||
"JNJ",62.45,"6/11/2007","11:06am",+0.32,62.89,62.89,62.15,2409122
|
||||
"JPM",50.38,"6/11/2007","11:06am",-0.03,50.41,50.55,50.05,2209200
|
||||
"KO",51.44,"6/11/2007","11:05am",-0.23,51.67,51.82,51.32,5034642
|
||||
"MCD",51.61,"6/11/2007","11:06am",+0.20,51.47,51.61,50.98,1307114
|
||||
"MMM",85.66,"6/11/2007","11:06am",-0.28,85.94,85.98,85.41,642300
|
||||
"MO",70.15,"6/11/2007","11:06am",-0.15,70.25,70.30,69.76,2298805
|
||||
"MRK",50.52,"6/11/2007","11:06am",+0.38,50.30,50.64,50.04,3272300
|
||||
"MSFT",30.06,"6/11/2007","11:11am",+0.01,30.05,30.25,29.93,15638561
|
||||
"PFE",26.36,"6/11/2007","11:06am",-0.16,26.50,26.53,26.33,5407496
|
||||
"PG",62.99,"6/11/2007","11:06am",-0.08,62.80,63.12,62.75,1906246
|
||||
"T",40.01,"6/11/2007","11:06am",-0.25,40.20,40.25,39.89,3728000
|
||||
"UTX",69.92,"6/11/2007","11:06am",-0.31,69.85,70.20,69.51,669300
|
||||
"VZ",43.17,"6/11/2007","11:06am",+0.10,42.95,43.20,42.89,2322610
|
||||
"WMT",49.55,"6/11/2007","11:06am",-0.53,49.90,50.00,49.55,2733633
|
||||
"XOM",83.08,"6/11/2007","11:06am",+0.40,82.68,83.11,82.35,3448500
|
||||
"AA",39.52,"6/11/2007","11:11am",-0.14,39.67,40.18,39.43,1258280
|
||||
"AIG",71.50,"6/11/2007","11:11am",-0.03,71.29,71.60,71.15,1250442
|
||||
"AXP",63.05,"6/11/2007","11:10am",+0.01,62.79,63.12,62.42,1588220
|
||||
"BA",98.16,"6/11/2007","11:11am",-0.03,98.25,98.79,97.74,759800
|
||||
"C",53.18,"6/11/2007","11:11am",-0.15,53.20,53.25,52.81,2614994
|
||||
"CAT",78.82,"6/11/2007","11:11am",+0.30,78.32,78.99,78.06,992052
|
||||
"DD",50.72,"6/11/2007","11:11am",-0.41,51.13,51.21,50.59,1241700
|
||||
"DIS",34.22,"6/11/2007","11:11am",+0.02,34.28,34.44,34.12,1691750
|
||||
"GE",37.36,"6/11/2007","11:11am",+0.04,37.07,37.41,37.05,7342028
|
||||
"GM",31.14,"6/11/2007","11:10am",+0.14,31.00,31.62,30.90,5195679
|
||||
"HD",37.74,"6/11/2007","11:11am",-0.21,37.78,37.83,37.62,2859969
|
||||
"HON",57.08,"6/11/2007","11:10am",-0.30,57.25,57.40,57.00,888783
|
||||
"HPQ",46.08,"6/11/2007","11:11am",+0.38,45.80,46.29,45.46,3164856
|
||||
"IBM",103.34,"6/11/2007","11:11am",+0.27,102.87,103.63,102.50,1554100
|
||||
"INTC",21.92,"6/11/2007","11:15am",+0.09,21.70,21.98,21.69,14546699
|
||||
"JNJ",62.49,"6/11/2007","11:11am",+0.36,62.89,62.89,62.15,2467522
|
||||
"JPM",50.39,"6/11/2007","11:11am",-0.02,50.41,50.55,50.05,2298000
|
||||
"KO",51.46,"6/11/2007","11:11am",-0.21,51.67,51.82,51.32,5073542
|
||||
"MCD",51.60,"6/11/2007","11:11am",+0.19,51.47,51.62,50.98,1438514
|
||||
"MMM",85.70,"6/11/2007","11:11am",-0.24,85.94,85.98,85.41,673300
|
||||
"MO",70.2306,"6/11/2007","11:11am",-0.0694,70.25,70.30,69.76,2382105
|
||||
"MRK",50.46,"6/11/2007","11:11am",+0.32,50.30,50.64,50.04,3337000
|
||||
"MSFT",30.04,"6/11/2007","11:16am",-0.01,30.05,30.25,29.93,15908978
|
||||
"PFE",26.32,"6/11/2007","11:11am",-0.20,26.50,26.53,26.31,5832646
|
||||
"PG",62.99,"6/11/2007","11:11am",-0.08,62.80,63.12,62.75,1960846
|
||||
"T",40.001,"6/11/2007","11:11am",-0.259,40.20,40.25,39.89,3794300
|
||||
"UTX",69.85,"6/11/2007","11:10am",-0.38,69.85,70.20,69.51,678300
|
||||
"VZ",43.16,"6/11/2007","11:10am",+0.09,42.95,43.20,42.89,2358345
|
||||
"WMT",49.64,"6/11/2007","11:11am",-0.44,49.90,50.00,49.55,2915433
|
||||
"XOM",83.09,"6/11/2007","11:11am",+0.41,82.68,83.11,82.35,3609300
|
||||
"AA",39.48,"6/11/2007","11:16am",-0.18,39.67,40.18,39.43,1290380
|
||||
"AIG",71.42,"6/11/2007","11:16am",-0.11,71.29,71.60,71.15,1276442
|
||||
"AXP",63.01,"6/11/2007","11:16am",-0.03,62.79,63.12,62.42,1616220
|
||||
"BA",98.01,"6/11/2007","11:16am",-0.18,98.25,98.79,97.74,777300
|
||||
"C",53.23,"6/11/2007","11:16am",-0.10,53.20,53.25,52.81,2793394
|
||||
"CAT",78.65,"6/11/2007","11:16am",+0.13,78.32,78.99,78.06,1046952
|
||||
"DD",50.70,"6/11/2007","11:16am",-0.43,51.13,51.21,50.59,1337600
|
||||
"DIS",34.20,"6/11/2007","11:15am",0.00,34.28,34.44,34.12,1805250
|
||||
"GE",37.35,"6/11/2007","11:16am",+0.03,37.07,37.41,37.05,7601178
|
||||
"GM",31.10,"6/11/2007","11:15am",+0.10,31.00,31.62,30.90,5260279
|
||||
"HD",37.73,"6/11/2007","11:16am",-0.22,37.78,37.83,37.62,2927469
|
||||
"HON",56.965,"6/11/2007","11:16am",-0.415,57.25,57.40,56.93,940183
|
||||
"HPQ",46.04,"6/11/2007","11:16am",+0.34,45.80,46.29,45.46,3255956
|
||||
"IBM",103.24,"6/11/2007","11:16am",+0.17,102.87,103.63,102.50,1595900
|
||||
"INTC",21.92,"6/11/2007","11:21am",+0.09,21.70,21.98,21.69,14851182
|
||||
"JNJ",62.55,"6/11/2007","11:16am",+0.42,62.89,62.89,62.15,2628222
|
||||
"JPM",50.32,"6/11/2007","11:16am",-0.09,50.41,50.55,50.05,2372200
|
||||
"KO",51.46,"6/11/2007","11:16am",-0.21,51.67,51.82,51.32,5091242
|
||||
"MCD",51.49,"6/11/2007","11:15am",+0.08,51.47,51.62,50.98,1719114
|
||||
"MMM",85.57,"6/11/2007","11:15am",-0.37,85.94,85.98,85.41,691600
|
||||
"MO",70.17,"6/11/2007","11:16am",-0.13,70.25,70.30,69.76,2466057
|
||||
"MRK",50.52,"6/11/2007","11:16am",+0.38,50.30,50.64,50.04,3543600
|
||||
"MSFT",30.025,"6/11/2007","11:20am",-0.025,30.05,30.25,29.93,16110835
|
||||
"PFE",26.33,"6/11/2007","11:16am",-0.19,26.50,26.53,26.31,6034446
|
||||
"PG",62.96,"6/11/2007","11:16am",-0.11,62.80,63.12,62.75,2028646
|
||||
"T",39.97,"6/11/2007","11:16am",-0.29,40.20,40.25,39.89,3937400
|
||||
"UTX",69.81,"6/11/2007","11:15am",-0.42,69.85,70.20,69.51,697600
|
||||
"VZ",43.15,"6/11/2007","11:16am",+0.08,42.95,43.20,42.89,2414845
|
||||
"WMT",49.63,"6/11/2007","11:16am",-0.45,49.90,50.00,49.55,3029233
|
||||
"XOM",82.96,"6/11/2007","11:16am",+0.28,82.68,83.11,82.35,3750000
|
||||
"AA",39.46,"6/11/2007","11:20am",-0.20,39.67,40.18,39.43,1318080
|
||||
"AIG",71.37,"6/11/2007","11:20am",-0.16,71.29,71.60,71.15,1313142
|
||||
"AXP",63.09,"6/11/2007","11:21am",+0.05,62.79,63.12,62.42,1639320
|
||||
"BA",98.06,"6/11/2007","11:21am",-0.13,98.25,98.79,97.74,796300
|
||||
"C",53.24,"6/11/2007","11:21am",-0.09,53.20,53.26,52.81,2937494
|
||||
"CAT",78.72,"6/11/2007","11:21am",+0.20,78.32,78.99,78.06,1095252
|
||||
"DD",50.67,"6/11/2007","11:21am",-0.46,51.13,51.21,50.59,1421500
|
||||
"DIS",34.21,"6/11/2007","11:21am",+0.01,34.28,34.44,34.12,1882850
|
||||
"GE",37.41,"6/11/2007","11:21am",+0.09,37.07,37.43,37.05,8394078
|
||||
"GM",31.06,"6/11/2007","11:21am",+0.06,31.00,31.62,30.90,5321979
|
||||
"HD",37.72,"6/11/2007","11:21am",-0.23,37.78,37.83,37.62,2989469
|
||||
"HON",56.98,"6/11/2007","11:21am",-0.40,57.25,57.40,56.91,1018183
|
||||
"HPQ",46.01,"6/11/2007","11:21am",+0.31,45.80,46.29,45.46,3347656
|
||||
"IBM",103.27,"6/11/2007","11:21am",+0.20,102.87,103.63,102.50,1635600
|
||||
"INTC",21.93,"6/11/2007","11:26am",+0.10,21.70,21.98,21.69,15018868
|
||||
"JNJ",62.58,"6/11/2007","11:21am",+0.45,62.89,62.89,62.15,2706422
|
||||
"JPM",50.30,"6/11/2007","11:21am",-0.11,50.41,50.55,50.05,2419700
|
||||
"KO",51.45,"6/11/2007","11:20am",-0.22,51.67,51.82,51.32,5160942
|
||||
"MCD",51.46,"6/11/2007","11:20am",+0.05,51.47,51.62,50.98,1812914
|
||||
"MMM",85.47,"6/11/2007","11:21am",-0.47,85.94,85.98,85.41,710100
|
||||
"MO",70.20,"6/11/2007","11:21am",-0.10,70.25,70.30,69.76,2517457
|
||||
"MRK",50.48,"6/11/2007","11:21am",+0.34,50.30,50.64,50.04,3625200
|
||||
"MSFT",30.07,"6/11/2007","11:26am",+0.02,30.05,30.25,29.93,16343142
|
||||
"PFE",26.349,"6/11/2007","11:21am",-0.171,26.50,26.53,26.31,6275846
|
||||
"PG",62.98,"6/11/2007","11:21am",-0.09,62.80,63.12,62.75,2075846
|
||||
"T",39.99,"6/11/2007","11:21am",-0.27,40.20,40.25,39.89,4085900
|
||||
"UTX",69.76,"6/11/2007","11:21am",-0.47,69.85,70.20,69.51,714000
|
||||
"VZ",43.16,"6/11/2007","11:20am",+0.09,42.95,43.20,42.89,2522445
|
||||
"WMT",49.63,"6/11/2007","11:21am",-0.45,49.90,50.00,49.55,3096833
|
||||
"XOM",82.91,"6/11/2007","11:21am",+0.23,82.68,83.11,82.35,3880100
|
||||
"AA",39.50,"6/11/2007","11:26am",-0.16,39.67,40.18,39.43,1352980
|
||||
"AIG",71.37,"6/11/2007","11:26am",-0.16,71.29,71.60,71.15,1345042
|
||||
"AXP",63.03,"6/11/2007","11:26am",-0.01,62.79,63.12,62.42,1654030
|
||||
"BA",98.06,"6/11/2007","11:26am",-0.13,98.25,98.79,97.74,817500
|
||||
"C",53.24,"6/11/2007","11:26am",-0.09,53.20,53.26,52.81,3004394
|
||||
"CAT",78.75,"6/11/2007","11:26am",+0.23,78.32,78.99,78.06,1112652
|
||||
"DD",50.80,"6/11/2007","11:26am",-0.33,51.13,51.21,50.59,1443900
|
||||
"DIS",34.22,"6/11/2007","11:25am",+0.02,34.28,34.44,34.12,1977950
|
||||
"GE",37.401,"6/11/2007","11:26am",+0.081,37.07,37.43,37.05,8549378
|
||||
"GM",31.18,"6/11/2007","11:26am",+0.18,31.00,31.62,30.90,5486579
|
||||
"HD",37.74,"6/11/2007","11:26am",-0.21,37.78,37.83,37.62,3055769
|
||||
"HON",57.03,"6/11/2007","11:26am",-0.35,57.25,57.40,56.91,1098842
|
||||
"HPQ",46.00,"6/11/2007","11:26am",+0.30,45.80,46.29,45.46,3418156
|
||||
"IBM",103.33,"6/11/2007","11:26am",+0.26,102.87,103.63,102.50,1657200
|
||||
"INTC",21.94,"6/11/2007","11:31am",+0.11,21.70,21.98,21.69,15378550
|
||||
"JNJ",62.59,"6/11/2007","11:26am",+0.46,62.89,62.89,62.15,2772722
|
||||
"JPM",50.38,"6/11/2007","11:26am",-0.03,50.41,50.55,50.05,2481600
|
||||
"KO",51.45,"6/11/2007","11:26am",-0.22,51.67,51.82,51.32,5180942
|
||||
"MCD",51.49,"6/11/2007","11:26am",+0.08,51.47,51.62,50.98,1867514
|
||||
"MMM",85.45,"6/11/2007","11:25am",-0.49,85.94,85.98,85.39,738400
|
||||
"MO",70.21,"6/11/2007","11:26am",-0.09,70.25,70.30,69.76,2582357
|
||||
"MRK",50.55,"6/11/2007","11:26am",+0.41,50.30,50.64,50.04,3773900
|
||||
"MSFT",30.05,"6/11/2007","11:31am",0.00,30.05,30.25,29.93,16793888
|
||||
"PFE",26.34,"6/11/2007","11:26am",-0.18,26.50,26.53,26.31,6551746
|
||||
"PG",62.99,"6/11/2007","11:26am",-0.08,62.80,63.12,62.75,2114846
|
||||
"T",40.06,"6/11/2007","11:26am",-0.20,40.20,40.25,39.89,4212600
|
||||
"UTX",69.73,"6/11/2007","11:25am",-0.50,69.85,70.20,69.51,725600
|
||||
"VZ",43.16,"6/11/2007","11:26am",+0.09,42.95,43.20,42.89,2595445
|
||||
"WMT",49.64,"6/11/2007","11:25am",-0.44,49.90,50.00,49.55,3167033
|
||||
"XOM",82.89,"6/11/2007","11:26am",+0.21,82.68,83.11,82.35,4029000
|
||||
"AA",39.47,"6/11/2007","11:31am",-0.19,39.67,40.18,39.43,1409980
|
||||
"AIG",71.48,"6/11/2007","11:31am",-0.05,71.29,71.60,71.15,1424042
|
||||
"AXP",63.07,"6/11/2007","11:30am",+0.03,62.79,63.14,62.42,1695430
|
||||
"BA",98.00,"6/11/2007","11:31am",-0.19,98.25,98.79,97.74,865400
|
||||
"C",53.31,"6/11/2007","11:31am",-0.02,53.20,53.31,52.81,3119094
|
||||
"CAT",78.72,"6/11/2007","11:31am",+0.20,78.32,78.99,78.06,1140252
|
||||
"DD",50.75,"6/11/2007","11:31am",-0.38,51.13,51.21,50.59,1474900
|
||||
"DIS",34.20,"6/11/2007","11:30am",0.00,34.28,34.44,34.12,2013250
|
||||
"GE",37.41,"6/11/2007","11:31am",+0.09,37.07,37.44,37.05,8715001
|
||||
"GM",31.23,"6/11/2007","11:31am",+0.23,31.00,31.62,30.90,5548179
|
||||
"HD",37.72,"6/11/2007","11:30am",-0.23,37.78,37.83,37.62,3183869
|
||||
"HON",57.01,"6/11/2007","11:31am",-0.37,57.25,57.40,56.91,1290742
|
||||
"HPQ",45.99,"6/11/2007","11:31am",+0.29,45.80,46.29,45.46,3514456
|
||||
"IBM",103.31,"6/11/2007","11:31am",+0.24,102.87,103.63,102.50,1841300
|
||||
"INTC",21.94,"6/11/2007","11:36am",+0.11,21.70,21.98,21.69,15795668
|
||||
"JNJ",62.59,"6/11/2007","11:31am",+0.46,62.89,62.89,62.15,2811022
|
||||
"JPM",50.41,"6/11/2007","11:30am",0.00,50.41,50.55,50.05,2553800
|
||||
"KO",51.45,"6/11/2007","11:30am",-0.22,51.67,51.82,51.32,5207142
|
||||
"MCD",51.47,"6/11/2007","11:30am",+0.06,51.47,51.62,50.98,1913314
|
||||
"MMM",85.41,"6/11/2007","11:31am",-0.53,85.94,85.98,85.39,805100
|
||||
"MO",70.36,"6/11/2007","11:31am",+0.06,70.25,70.36,69.76,2672857
|
||||
"MRK",50.55,"6/11/2007","11:31am",+0.41,50.30,50.64,50.04,3841500
|
||||
"MSFT",30.09,"6/11/2007","11:36am",+0.04,30.05,30.25,29.93,17107810
|
||||
"PFE",26.35,"6/11/2007","11:31am",-0.17,26.50,26.53,26.31,6720346
|
||||
"PG",62.98,"6/11/2007","11:31am",-0.09,62.80,63.12,62.75,2155746
|
||||
"T",40.07,"6/11/2007","11:31am",-0.19,40.20,40.25,39.89,4436800
|
||||
"UTX",69.74,"6/11/2007","11:31am",-0.49,69.85,70.20,69.51,757000
|
||||
"VZ",43.18,"6/11/2007","11:31am",+0.11,42.95,43.22,42.89,2918545
|
||||
"WMT",49.66,"6/11/2007","11:31am",-0.42,49.90,50.00,49.55,3258333
|
||||
"XOM",82.92,"6/11/2007","11:31am",+0.24,82.68,83.11,82.35,4163200
|
||||
"AA",39.50,"6/11/2007","11:36am",-0.16,39.67,40.18,39.43,1444680
|
||||
"AIG",71.48,"6/11/2007","11:35am",-0.05,71.29,71.60,71.15,1468642
|
||||
"AXP",63.0703,"6/11/2007","11:35am",+0.0303,62.79,63.14,62.42,1717830
|
||||
"BA",98.0028,"6/11/2007","11:36am",-0.1872,98.25,98.79,97.74,951000
|
||||
"C",53.34,"6/11/2007","11:36am",+0.01,53.20,53.37,52.81,3207894
|
||||
"CAT",78.66,"6/11/2007","11:36am",+0.14,78.32,78.99,78.06,1150952
|
||||
"DD",50.73,"6/11/2007","11:36am",-0.40,51.13,51.21,50.59,1500000
|
||||
"DIS",34.19,"6/11/2007","11:36am",-0.01,34.28,34.44,34.12,2167150
|
||||
"GE",37.38,"6/11/2007","11:36am",+0.06,37.07,37.44,37.05,9041801
|
||||
"GM",31.25,"6/11/2007","11:35am",+0.25,31.00,31.62,30.90,5608979
|
||||
"HD",37.75,"6/11/2007","11:36am",-0.20,37.78,37.83,37.62,3269369
|
||||
"HON",56.98,"6/11/2007","11:36am",-0.40,57.25,57.40,56.91,1330742
|
||||
"HPQ",45.94,"6/11/2007","11:36am",+0.24,45.80,46.29,45.46,3621556
|
||||
"IBM",103.28,"6/11/2007","11:36am",+0.21,102.87,103.63,102.50,1859400
|
||||
"INTC",21.97,"6/11/2007","11:41am",+0.14,21.70,21.98,21.69,16127869
|
||||
"JNJ",62.56,"6/11/2007","11:35am",+0.43,62.89,62.89,62.15,2852322
|
||||
"JPM",50.43,"6/11/2007","11:36am",+0.02,50.41,50.55,50.05,2656300
|
||||
"KO",51.40,"6/11/2007","11:36am",-0.27,51.67,51.82,51.32,5232342
|
||||
"MCD",51.47,"6/11/2007","11:35am",+0.06,51.47,51.62,50.98,1953314
|
||||
"MMM",85.43,"6/11/2007","11:35am",-0.51,85.94,85.98,85.39,828900
|
||||
"MO",70.39,"6/11/2007","11:35am",+0.09,70.25,70.48,69.76,2823257
|
||||
"MRK",50.551,"6/11/2007","11:36am",+0.411,50.30,50.64,50.04,3911800
|
||||
"MSFT",30.11,"6/11/2007","11:41am",+0.06,30.05,30.25,29.93,17353272
|
||||
"PFE",26.38,"6/11/2007","11:36am",-0.14,26.50,26.53,26.31,6992796
|
||||
"PG",62.93,"6/11/2007","11:35am",-0.14,62.80,63.12,62.75,2196846
|
||||
"T",40.03,"6/11/2007","11:35am",-0.23,40.20,40.25,39.89,4610000
|
||||
"UTX",69.77,"6/11/2007","11:35am",-0.46,69.85,70.20,69.51,788900
|
||||
"VZ",43.18,"6/11/2007","11:35am",+0.11,42.95,43.22,42.89,2997845
|
||||
"WMT",49.74,"6/11/2007","11:36am",-0.34,49.90,50.00,49.55,3415433
|
||||
"XOM",83.04,"6/11/2007","11:36am",+0.36,82.68,83.11,82.35,4297200
|
||||
"AA",39.53,"6/11/2007","11:41am",-0.13,39.67,40.18,39.43,1481180
|
||||
"AIG",71.53,"6/11/2007","11:41am",0.00,71.29,71.60,71.15,1498042
|
||||
"AXP",63.07,"6/11/2007","11:41am",+0.03,62.79,63.14,62.42,1760630
|
||||
"BA",98.06,"6/11/2007","11:41am",-0.13,98.25,98.79,97.74,968400
|
||||
"C",53.41,"6/11/2007","11:41am",+0.08,53.20,53.41,52.81,3275394
|
||||
"CAT",78.72,"6/11/2007","11:41am",+0.20,78.32,78.99,78.06,1165852
|
||||
"DD",50.77,"6/11/2007","11:41am",-0.36,51.13,51.21,50.59,1523000
|
||||
"DIS",34.19,"6/11/2007","11:41am",-0.01,34.28,34.44,34.12,2223450
|
||||
"GE",37.42,"6/11/2007","11:41am",+0.10,37.07,37.44,37.05,9239801
|
||||
"GM",31.30,"6/11/2007","11:41am",+0.30,31.00,31.62,30.90,5699079
|
||||
"HD",37.75,"6/11/2007","11:41am",-0.20,37.78,37.83,37.62,3307869
|
||||
"HON",56.99,"6/11/2007","11:41am",-0.39,57.25,57.40,56.91,1376642
|
||||
"HPQ",45.96,"6/11/2007","11:41am",+0.26,45.80,46.29,45.46,3706756
|
||||
"IBM",103.44,"6/11/2007","11:41am",+0.37,102.87,103.63,102.50,1880700
|
||||
"INTC",21.97,"6/11/2007","11:45am",+0.14,21.70,21.98,21.69,16406027
|
||||
"JNJ",62.64,"6/11/2007","11:41am",+0.51,62.89,62.89,62.15,2899022
|
||||
"JPM",50.50,"6/11/2007","11:41am",+0.09,50.41,50.55,50.05,2715800
|
||||
"KO",51.45,"6/11/2007","11:41am",-0.22,51.67,51.82,51.32,5260042
|
||||
"MCD",51.49,"6/11/2007","11:40am",+0.08,51.47,51.62,50.98,2029314
|
||||
"MMM",85.52,"6/11/2007","11:41am",-0.42,85.94,85.98,85.39,843500
|
||||
"MO",70.47,"6/11/2007","11:41am",+0.17,70.25,70.50,69.76,3705557
|
||||
"MRK",50.65,"6/11/2007","11:41am",+0.51,50.30,50.65,50.04,4091400
|
||||
"MSFT",30.135,"6/11/2007","11:46am",+0.085,30.05,30.25,29.93,17741848
|
||||
"PFE",26.42,"6/11/2007","11:41am",-0.10,26.50,26.53,26.31,7347396
|
||||
"PG",63.00,"6/11/2007","11:41am",-0.07,62.80,63.12,62.75,2256146
|
||||
"T",40.09,"6/11/2007","11:41am",-0.17,40.20,40.25,39.89,4805400
|
||||
"UTX",69.82,"6/11/2007","11:40am",-0.41,69.85,70.20,69.51,796500
|
||||
"VZ",43.23,"6/11/2007","11:40am",+0.16,42.95,43.23,42.89,3077145
|
||||
"WMT",49.81,"6/11/2007","11:41am",-0.27,49.90,50.00,49.55,3532333
|
||||
"XOM",83.18,"6/11/2007","11:41am",+0.50,82.68,83.18,82.35,4455700
|
||||
"AA",39.54,"6/11/2007","11:46am",-0.12,39.67,40.18,39.43,1499880
|
||||
"AIG",71.59,"6/11/2007","11:45am",+0.06,71.29,71.61,71.15,1522942
|
||||
"AXP",63.09,"6/11/2007","11:46am",+0.05,62.79,63.14,62.42,1799530
|
||||
"BA",98.01,"6/11/2007","11:46am",-0.18,98.25,98.79,97.74,1016800
|
||||
"C",53.48,"6/11/2007","11:46am",+0.15,53.20,53.52,52.81,3465694
|
||||
"CAT",78.7346,"6/11/2007","11:45am",+0.2146,78.32,78.99,78.06,1189152
|
||||
"DD",50.72,"6/11/2007","11:46am",-0.41,51.13,51.21,50.59,1542800
|
||||
"DIS",34.202,"6/11/2007","11:46am",+0.002,34.28,34.44,34.12,2263250
|
||||
"GE",37.43,"6/11/2007","11:46am",+0.11,37.07,37.44,37.05,9402901
|
||||
"GM",31.30,"6/11/2007","11:46am",+0.30,31.00,31.62,30.90,5791779
|
||||
"HD",37.74,"6/11/2007","11:46am",-0.21,37.78,37.83,37.62,3438769
|
||||
"HON",57.01,"6/11/2007","11:45am",-0.37,57.25,57.40,56.91,1411942
|
||||
"HPQ",45.99,"6/11/2007","11:46am",+0.29,45.80,46.29,45.46,3780456
|
||||
"IBM",103.53,"6/11/2007","11:46am",+0.46,102.87,103.63,102.50,1931500
|
||||
"INTC",21.98,"6/11/2007","11:51am",+0.15,21.70,21.98,21.69,16615722
|
||||
"JNJ",62.64,"6/11/2007","11:45am",+0.51,62.89,62.89,62.15,2931522
|
||||
"JPM",50.52,"6/11/2007","11:46am",+0.11,50.41,50.55,50.05,2820200
|
||||
"KO",51.48,"6/11/2007","11:45am",-0.19,51.67,51.82,51.32,5289842
|
||||
"MCD",51.54,"6/11/2007","11:45am",+0.13,51.47,51.62,50.98,2063014
|
||||
"MMM",85.53,"6/11/2007","11:46am",-0.41,85.94,85.98,85.39,860900
|
||||
"MO",70.30,"6/11/2007","11:46am",0.00,70.25,70.50,69.76,3796557
|
||||
"MRK",50.73,"6/11/2007","11:46am",+0.59,50.30,50.74,50.04,4201600
|
||||
"MSFT",30.1603,"6/11/2007","11:50am",+0.1103,30.05,30.25,29.93,18232994
|
||||
"PFE",26.43,"6/11/2007","11:46am",-0.09,26.50,26.53,26.31,7612046
|
||||
"PG",62.95,"6/11/2007","11:45am",-0.12,62.80,63.12,62.75,2315446
|
||||
"T",40.14,"6/11/2007","11:46am",-0.12,40.20,40.25,39.89,4928500
|
||||
"UTX",69.83,"6/11/2007","11:46am",-0.40,69.85,70.20,69.51,808300
|
||||
"VZ",43.25,"6/11/2007","11:45am",+0.18,42.95,43.26,42.89,3194645
|
||||
"WMT",49.81,"6/11/2007","11:46am",-0.27,49.90,50.00,49.55,3691733
|
||||
"XOM",83.20,"6/11/2007","11:46am",+0.52,82.68,83.25,82.35,4596200
|
||||
"AA",39.59,"6/11/2007","11:51am",-0.07,39.67,40.18,39.43,1549580
|
||||
"AIG",71.62,"6/11/2007","11:51am",+0.09,71.29,71.68,71.15,1587442
|
||||
"AXP",63.09,"6/11/2007","11:51am",+0.05,62.79,63.19,62.42,1825430
|
||||
"BA",97.98,"6/11/2007","11:50am",-0.21,98.25,98.79,97.74,1036600
|
||||
"C",53.52,"6/11/2007","11:50am",+0.19,53.20,53.57,52.81,3665594
|
||||
"CAT",78.78,"6/11/2007","11:51am",+0.26,78.32,78.99,78.06,1234052
|
||||
"DD",50.74,"6/11/2007","11:51am",-0.39,51.13,51.21,50.59,1579200
|
||||
"DIS",34.23,"6/11/2007","11:51am",+0.03,34.28,34.44,34.12,2335050
|
||||
"GE",37.469,"6/11/2007","11:51am",+0.149,37.07,37.48,37.05,9637101
|
||||
"GM",31.31,"6/11/2007","11:50am",+0.31,31.00,31.62,30.90,5900779
|
||||
"HD",37.77,"6/11/2007","11:51am",-0.18,37.78,37.83,37.62,3574469
|
||||
"HON",56.99,"6/11/2007","11:50am",-0.39,57.25,57.40,56.91,1469142
|
||||
"HPQ",46.0616,"6/11/2007","11:50am",+0.3616,45.80,46.29,45.46,3886556
|
||||
"IBM",103.70,"6/11/2007","11:51am",+0.63,102.87,103.71,102.50,1993700
|
||||
"INTC",22.01,"6/11/2007","11:56am",+0.18,21.70,22.01,21.69,17848980
|
||||
"JNJ",62.719,"6/11/2007","11:51am",+0.589,62.89,62.89,62.15,3194722
|
||||
"JPM",50.53,"6/11/2007","11:51am",+0.12,50.41,50.55,50.05,2963700
|
||||
"KO",51.50,"6/11/2007","11:50am",-0.17,51.67,51.82,51.32,5313142
|
||||
"MCD",51.52,"6/11/2007","11:51am",+0.11,51.47,51.62,50.98,2105814
|
||||
"MMM",85.53,"6/11/2007","11:50am",-0.41,85.94,85.98,85.39,879300
|
||||
"MO",70.43,"6/11/2007","11:50am",+0.13,70.25,70.50,69.76,3873057
|
||||
"MRK",50.78,"6/11/2007","11:51am",+0.64,50.30,50.87,50.04,4316300
|
||||
"MSFT",30.18,"6/11/2007","11:56am",+0.13,30.05,30.25,29.93,18458900
|
||||
"PFE",26.50,"6/11/2007","11:51am",-0.02,26.50,26.53,26.31,7882446
|
||||
"PG",63.04,"6/11/2007","11:50am",-0.03,62.80,63.12,62.75,2384246
|
||||
"T",40.15,"6/11/2007","11:51am",-0.11,40.20,40.25,39.89,5143600
|
||||
"UTX",69.85,"6/11/2007","11:50am",-0.38,69.85,70.20,69.51,824600
|
||||
"VZ",43.29,"6/11/2007","11:51am",+0.22,42.95,43.30,42.89,3253445
|
||||
"WMT",49.84,"6/11/2007","11:51am",-0.24,49.90,50.00,49.55,3812233
|
||||
"XOM",83.26,"6/11/2007","11:51am",+0.58,82.68,83.35,82.35,4722700
|
||||
"AA",39.62,"6/11/2007","11:56am",-0.04,39.67,40.18,39.43,1573080
|
||||
"AIG",71.6254,"6/11/2007","11:56am",+0.0954,71.29,71.68,71.15,1630242
|
||||
"AXP",63.14,"6/11/2007","11:56am",+0.10,62.79,63.19,62.42,1839830
|
||||
"BA",97.97,"6/11/2007","11:55am",-0.22,98.25,98.79,97.74,1056000
|
||||
"C",53.58,"6/11/2007","11:56am",+0.25,53.20,53.58,52.81,3892094
|
||||
"CAT",78.76,"6/11/2007","11:55am",+0.24,78.32,78.99,78.06,1252852
|
||||
"DD",50.73,"6/11/2007","11:56am",-0.40,51.13,51.21,50.59,1632600
|
||||
"DIS",34.24,"6/11/2007","11:56am",+0.04,34.28,34.44,34.12,2414850
|
||||
"GE",37.44,"6/11/2007","11:56am",+0.12,37.07,37.49,37.05,9766201
|
||||
"GM",31.32,"6/11/2007","11:56am",+0.32,31.00,31.62,30.90,5949301
|
||||
"HD",37.74,"6/11/2007","11:56am",-0.21,37.78,37.83,37.62,3692669
|
||||
"HON",57.00,"6/11/2007","11:56am",-0.38,57.25,57.40,56.91,1521042
|
||||
"HPQ",46.10,"6/11/2007","11:56am",+0.40,45.80,46.29,45.46,4001456
|
||||
"IBM",103.62,"6/11/2007","11:56am",+0.55,102.87,103.71,102.50,2026100
|
||||
"INTC",22.00,"6/11/2007","12:01pm",+0.17,21.70,22.01,21.69,18388938
|
||||
"JNJ",62.66,"6/11/2007","11:55am",+0.53,62.89,62.89,62.15,3236322
|
||||
"JPM",50.54,"6/11/2007","11:56am",+0.13,50.41,50.56,50.05,3019600
|
||||
"KO",51.52,"6/11/2007","11:55am",-0.15,51.67,51.82,51.32,5345042
|
||||
"MCD",51.53,"6/11/2007","11:56am",+0.12,51.47,51.62,50.98,2134514
|
||||
"MMM",85.44,"6/11/2007","11:56am",-0.50,85.94,85.98,85.39,905400
|
||||
"MO",70.42,"6/11/2007","11:56am",+0.12,70.25,70.50,69.76,3926257
|
||||
"MRK",50.79,"6/11/2007","11:56am",+0.65,50.30,50.87,50.04,4401700
|
||||
"MSFT",30.18,"6/11/2007","12:01pm",+0.13,30.05,30.25,29.93,18749244
|
||||
"PFE",26.46,"6/11/2007","11:56am",-0.06,26.50,26.53,26.31,8528842
|
||||
"PG",63.04,"6/11/2007","11:56am",-0.03,62.80,63.12,62.75,2418246
|
||||
"T",40.16,"6/11/2007","11:56am",-0.10,40.20,40.25,39.89,5294500
|
||||
"UTX",69.8227,"6/11/2007","11:56am",-0.4073,69.85,70.20,69.51,840000
|
||||
"VZ",43.28,"6/11/2007","11:56am",+0.21,42.95,43.31,42.89,3317345
|
||||
"WMT",49.87,"6/11/2007","11:56am",-0.21,49.90,50.00,49.55,3928833
|
||||
"XOM",83.30,"6/11/2007","11:56am",+0.62,82.68,83.39,82.35,4932400
|
||||
"AA",39.615,"6/11/2007","12:00pm",-0.045,39.67,40.18,39.43,1587980
|
||||
"AIG",71.63,"6/11/2007","12:01pm",+0.10,71.29,71.68,71.15,1669942
|
||||
"AXP",63.142,"6/11/2007","12:01pm",+0.102,62.79,63.19,62.42,1852230
|
||||
"BA",98.00,"6/11/2007","12:01pm",-0.19,98.25,98.79,97.74,1079000
|
||||
"C",53.53,"6/11/2007","12:01pm",+0.20,53.20,53.58,52.81,3992194
|
||||
"CAT",78.76,"6/11/2007","12:01pm",+0.24,78.32,78.99,78.06,1270152
|
||||
"DD",50.69,"6/11/2007","12:01pm",-0.44,51.13,51.21,50.59,1671400
|
||||
"DIS",34.21,"6/11/2007","12:00pm",+0.01,34.28,34.44,34.12,2450550
|
||||
"GE",37.425,"6/11/2007","12:01pm",+0.105,37.07,37.49,37.05,9895501
|
||||
"GM",31.32,"6/11/2007","12:00pm",+0.32,31.00,31.62,30.90,5989901
|
||||
"HD",37.75,"6/11/2007","12:01pm",-0.20,37.78,37.83,37.62,3816469
|
||||
"HON",57.00,"6/11/2007","12:01pm",-0.38,57.25,57.40,56.91,1543942
|
||||
"HPQ",46.12,"6/11/2007","12:01pm",+0.42,45.80,46.29,45.46,4076156
|
||||
"IBM",103.63,"6/11/2007","12:00pm",+0.56,102.87,103.71,102.50,2060500
|
||||
"INTC",21.99,"6/11/2007","12:06pm",+0.16,21.70,22.02,21.69,18564224
|
||||
"JNJ",62.631,"6/11/2007","12:01pm",+0.501,62.89,62.89,62.15,3282922
|
||||
"JPM",50.60,"6/11/2007","12:01pm",+0.19,50.41,50.60,50.05,3118700
|
||||
"KO",51.53,"6/11/2007","12:00pm",-0.14,51.67,51.82,51.32,5393742
|
||||
"MCD",51.50,"6/11/2007","12:01pm",+0.09,51.47,51.62,50.98,2171014
|
||||
"MMM",85.43,"6/11/2007","12:00pm",-0.51,85.94,85.98,85.39,922700
|
||||
"MO",70.40,"6/11/2007","12:01pm",+0.10,70.25,70.50,69.76,3957957
|
||||
"MRK",50.7475,"6/11/2007","12:01pm",+0.6075,50.30,50.87,50.04,4475600
|
||||
"MSFT",30.1597,"6/11/2007","12:06pm",+0.1097,30.05,30.25,29.93,18960388
|
||||
"PFE",26.46,"6/11/2007","12:01pm",-0.06,26.50,26.53,26.31,8624142
|
||||
"PG",63.03,"6/11/2007","12:01pm",-0.04,62.80,63.12,62.75,2475246
|
||||
"T",40.20,"6/11/2007","12:01pm",-0.06,40.20,40.25,39.89,5445500
|
||||
"UTX",69.83,"6/11/2007","12:00pm",-0.40,69.85,70.20,69.51,860200
|
||||
"VZ",43.28,"6/11/2007","12:00pm",+0.21,42.95,43.31,42.89,3361245
|
||||
"WMT",49.87,"6/11/2007","12:01pm",-0.21,49.90,50.00,49.55,4143733
|
||||
"XOM",83.40,"6/11/2007","12:01pm",+0.72,82.68,83.46,82.35,5119800
|
||||
"AA",39.61,"6/11/2007","12:06pm",-0.05,39.67,40.18,39.43,1627080
|
||||
"AIG",71.68,"6/11/2007","12:06pm",+0.15,71.29,71.68,71.15,1706542
|
||||
"AXP",63.0546,"6/11/2007","12:05pm",+0.0146,62.79,63.19,62.42,1880930
|
||||
"BA",97.92,"6/11/2007","12:06pm",-0.27,98.25,98.79,97.74,1090100
|
||||
"C",53.55,"6/11/2007","12:06pm",+0.22,53.20,53.65,52.81,4319594
|
||||
"CAT",78.74,"6/11/2007","12:06pm",+0.22,78.32,78.99,78.06,1288152
|
||||
"DD",50.70,"6/11/2007","12:06pm",-0.43,51.13,51.21,50.59,1732500
|
||||
"DIS",34.22,"6/11/2007","12:06pm",+0.02,34.28,34.44,34.12,2493950
|
||||
"GE",37.4227,"6/11/2007","12:06pm",+0.1027,37.07,37.49,37.05,10023701
|
||||
"GM",31.32,"6/11/2007","12:06pm",+0.32,31.00,31.62,30.90,6029201
|
||||
"HD",37.7401,"6/11/2007","12:06pm",-0.2099,37.78,37.83,37.62,3862469
|
||||
"HON",57.00,"6/11/2007","12:06pm",-0.38,57.25,57.40,56.91,1627642
|
||||
"HPQ",46.10,"6/11/2007","12:06pm",+0.40,45.80,46.29,45.46,4113656
|
||||
"IBM",103.67,"6/11/2007","12:06pm",+0.60,102.87,103.71,102.50,2085800
|
||||
"INTC",21.99,"6/11/2007","12:11pm",+0.16,21.70,22.02,21.69,18898568
|
||||
"JNJ",62.62,"6/11/2007","12:06pm",+0.49,62.89,62.89,62.15,3318522
|
||||
"JPM",50.60,"6/11/2007","12:06pm",+0.19,50.41,50.62,50.05,3165700
|
||||
"KO",51.52,"6/11/2007","12:05pm",-0.15,51.67,51.82,51.32,5416242
|
||||
"MCD",51.47,"6/11/2007","12:06pm",+0.06,51.47,51.62,50.98,2192514
|
||||
"MMM",85.41,"6/11/2007","12:05pm",-0.53,85.94,85.98,85.39,941800
|
||||
"MO",70.41,"6/11/2007","12:06pm",+0.11,70.25,70.50,69.76,3992557
|
||||
"MRK",50.75,"6/11/2007","12:06pm",+0.61,50.30,50.87,50.04,4518500
|
||||
"MSFT",30.13,"6/11/2007","12:11pm",+0.08,30.05,30.25,29.93,19296222
|
||||
"PFE",26.43,"6/11/2007","12:06pm",-0.09,26.50,26.53,26.31,8765142
|
||||
"PG",63.02,"6/11/2007","12:06pm",-0.05,62.80,63.12,62.75,2498846
|
||||
"T",40.18,"6/11/2007","12:06pm",-0.08,40.20,40.25,39.89,5544800
|
||||
"UTX",69.89,"6/11/2007","12:05pm",-0.34,69.85,70.20,69.51,873700
|
||||
"VZ",43.32,"6/11/2007","12:06pm",+0.25,42.95,43.34,42.89,3454720
|
||||
"WMT",49.84,"6/11/2007","12:06pm",-0.24,49.90,50.00,49.55,4217433
|
||||
"XOM",83.40,"6/11/2007","12:06pm",+0.72,82.68,83.48,82.35,5317100
|
||||
"AA",39.57,"6/11/2007","12:11pm",-0.09,39.67,40.18,39.43,1657480
|
||||
"AIG",71.68,"6/11/2007","12:11pm",+0.15,71.29,71.70,71.15,1754142
|
||||
"AXP",63.01,"6/11/2007","12:11pm",-0.03,62.79,63.19,62.42,1905230
|
||||
"BA",97.856,"6/11/2007","12:11pm",-0.334,98.25,98.79,97.74,1115200
|
||||
"C",53.57,"6/11/2007","12:11pm",+0.24,53.20,53.65,52.81,4395794
|
||||
"CAT",78.82,"6/11/2007","12:10pm",+0.30,78.32,78.99,78.06,1308952
|
||||
"DD",50.71,"6/11/2007","12:10pm",-0.42,51.13,51.21,50.59,1760700
|
||||
"DIS",34.23,"6/11/2007","12:11pm",+0.03,34.28,34.44,34.12,2537850
|
||||
"GE",37.4025,"6/11/2007","12:11pm",+0.0825,37.07,37.49,37.05,10173701
|
||||
"GM",31.27,"6/11/2007","12:11pm",+0.27,31.00,31.62,30.90,6100601
|
||||
"HD",37.75,"6/11/2007","12:11pm",-0.20,37.78,37.83,37.62,4021569
|
||||
"HON",57.00,"6/11/2007","12:11pm",-0.38,57.25,57.40,56.91,1666742
|
||||
"HPQ",46.08,"6/11/2007","12:10pm",+0.38,45.80,46.29,45.46,4173956
|
||||
"IBM",103.56,"6/11/2007","12:11pm",+0.49,102.87,103.71,102.50,2111700
|
||||
"INTC",21.98,"6/11/2007","12:16pm",+0.15,21.70,22.02,21.69,18999740
|
||||
"JNJ",62.56,"6/11/2007","12:11pm",+0.43,62.89,62.89,62.15,3392622
|
||||
"JPM",50.60,"6/11/2007","12:11pm",+0.19,50.41,50.62,50.05,3283000
|
||||
"KO",51.53,"6/11/2007","12:11pm",-0.14,51.67,51.82,51.32,5438042
|
||||
"MCD",51.455,"6/11/2007","12:11pm",+0.045,51.47,51.62,50.98,2250714
|
||||
"MMM",85.32,"6/11/2007","12:11pm",-0.62,85.94,85.98,85.32,975500
|
||||
"MO",70.38,"6/11/2007","12:11pm",+0.08,70.25,70.50,69.76,4010457
|
||||
"MRK",50.75,"6/11/2007","12:10pm",+0.61,50.30,50.87,50.04,4599200
|
||||
"MSFT",30.13,"6/11/2007","12:16pm",+0.08,30.05,30.25,29.93,19453430
|
||||
"PFE",26.38,"6/11/2007","12:11pm",-0.14,26.50,26.53,26.31,9435387
|
||||
"PG",63.01,"6/11/2007","12:11pm",-0.06,62.80,63.12,62.75,2550946
|
||||
"T",40.13,"6/11/2007","12:11pm",-0.13,40.20,40.25,39.89,5619200
|
||||
"UTX",69.89,"6/11/2007","12:11pm",-0.34,69.85,70.20,69.51,893500
|
||||
"VZ",43.31,"6/11/2007","12:11pm",+0.24,42.95,43.34,42.89,3525820
|
||||
"WMT",49.86,"6/11/2007","12:11pm",-0.22,49.90,50.00,49.55,4320433
|
||||
"XOM",83.38,"6/11/2007","12:11pm",+0.70,82.68,83.48,82.35,5384300
|
||||
"AA",39.51,"6/11/2007","12:15pm",-0.15,39.67,40.18,39.43,1707780
|
||||
"AIG",71.68,"6/11/2007","12:16pm",+0.15,71.29,71.70,71.15,1797642
|
||||
"AXP",62.98,"6/11/2007","12:15pm",-0.06,62.79,63.19,62.42,1919830
|
||||
"BA",97.81,"6/11/2007","12:16pm",-0.38,98.25,98.79,97.74,1189800
|
||||
"C",53.51,"6/11/2007","12:15pm",+0.18,53.20,53.65,52.81,4582294
|
||||
"CAT",78.87,"6/11/2007","12:16pm",+0.35,78.32,78.99,78.06,1321852
|
||||
"DD",50.69,"6/11/2007","12:16pm",-0.44,51.13,51.21,50.59,1790800
|
||||
"DIS",34.21,"6/11/2007","12:16pm",+0.01,34.28,34.44,34.12,2586450
|
||||
"GE",37.40,"6/11/2007","12:16pm",+0.08,37.07,37.49,37.05,10295201
|
||||
"GM",31.27,"6/11/2007","12:16pm",+0.27,31.00,31.62,30.90,6373201
|
||||
"HD",37.752,"6/11/2007","12:16pm",-0.198,37.78,37.83,37.62,4072169
|
||||
"HON",57.00,"6/11/2007","12:16pm",-0.38,57.25,57.40,56.91,1683042
|
||||
"HPQ",46.06,"6/11/2007","12:15pm",+0.36,45.80,46.29,45.46,4225556
|
||||
"IBM",103.54,"6/11/2007","12:16pm",+0.47,102.87,103.71,102.50,2128600
|
||||
"INTC",21.98,"6/11/2007","12:21pm",+0.15,21.70,22.02,21.69,19143976
|
||||
"JNJ",62.57,"6/11/2007","12:16pm",+0.44,62.89,62.89,62.15,3439222
|
||||
"JPM",50.59,"6/11/2007","12:16pm",+0.18,50.41,50.62,50.05,3372000
|
||||
"KO",51.5418,"6/11/2007","12:16pm",-0.1282,51.67,51.82,51.32,5475442
|
||||
"MCD",51.44,"6/11/2007","12:16pm",+0.03,51.47,51.62,50.98,2321914
|
||||
"MMM",85.34,"6/11/2007","12:16pm",-0.60,85.94,85.98,85.32,985300
|
||||
"MO",70.31,"6/11/2007","12:15pm",+0.01,70.25,70.50,69.76,4067257
|
||||
"MRK",50.74,"6/11/2007","12:16pm",+0.60,50.30,50.87,50.04,4655400
|
||||
"MSFT",30.10,"6/11/2007","12:21pm",+0.05,30.05,30.25,29.93,19617530
|
||||
"PFE",26.38,"6/11/2007","12:16pm",-0.14,26.50,26.53,26.31,9916032
|
||||
"PG",62.99,"6/11/2007","12:16pm",-0.08,62.80,63.12,62.75,2626746
|
||||
"T",40.10,"6/11/2007","12:16pm",-0.16,40.20,40.25,39.89,5730400
|
||||
"UTX",69.89,"6/11/2007","12:15pm",-0.34,69.85,70.20,69.51,919000
|
||||
"VZ",43.252,"6/11/2007","12:16pm",+0.182,42.95,43.34,42.89,3563720
|
||||
"WMT",49.882,"6/11/2007","12:16pm",-0.198,49.90,50.00,49.55,4432033
|
||||
"XOM",83.37,"6/11/2007","12:16pm",+0.69,82.68,83.48,82.35,5492200
|
||||
"AA",39.48,"6/11/2007","12:21pm",-0.18,39.67,40.18,39.43,1815880
|
||||
"AIG",71.71,"6/11/2007","12:21pm",+0.18,71.29,71.71,71.15,1835142
|
||||
"AXP",62.92,"6/11/2007","12:20pm",-0.12,62.79,63.19,62.42,1934230
|
||||
"BA",97.79,"6/11/2007","12:21pm",-0.40,98.25,98.79,97.74,1228800
|
||||
"C",53.53,"6/11/2007","12:21pm",+0.20,53.20,53.65,52.81,4665694
|
||||
"CAT",78.84,"6/11/2007","12:20pm",+0.32,78.32,78.99,78.06,1332852
|
||||
"DD",50.69,"6/11/2007","12:21pm",-0.44,51.13,51.21,50.59,1803100
|
||||
"DIS",34.21,"6/11/2007","12:21pm",+0.01,34.28,34.44,34.12,2632250
|
||||
"GE",37.41,"6/11/2007","12:21pm",+0.09,37.07,37.49,37.05,10406001
|
||||
"GM",31.29,"6/11/2007","12:21pm",+0.29,31.00,31.62,30.90,6432201
|
||||
"HD",37.75,"6/11/2007","12:20pm",-0.20,37.78,37.83,37.62,5032769
|
||||
"HON",56.98,"6/11/2007","12:20pm",-0.40,57.25,57.40,56.91,1693442
|
||||
"HPQ",46.03,"6/11/2007","12:21pm",+0.33,45.80,46.29,45.46,4304056
|
||||
"IBM",103.56,"6/11/2007","12:20pm",+0.49,102.87,103.71,102.50,2151600
|
||||
"INTC",21.963,"6/11/2007","12:26pm",+0.133,21.70,22.02,21.69,19623448
|
||||
"JNJ",62.56,"6/11/2007","12:21pm",+0.43,62.89,62.89,62.15,3517522
|
||||
"JPM",50.58,"6/11/2007","12:21pm",+0.17,50.41,50.62,50.05,3435300
|
||||
"KO",51.52,"6/11/2007","12:21pm",-0.15,51.67,51.82,51.32,5546242
|
||||
"MCD",51.40,"6/11/2007","12:20pm",-0.01,51.47,51.62,50.98,2357014
|
||||
"MMM",85.33,"6/11/2007","12:20pm",-0.61,85.94,85.98,85.32,996200
|
||||
"MO",70.27,"6/11/2007","12:21pm",-0.03,70.25,70.50,69.76,4106257
|
||||
"MRK",50.72,"6/11/2007","12:21pm",+0.58,50.30,50.87,50.04,4713700
|
||||
"MSFT",30.12,"6/11/2007","12:26pm",+0.07,30.05,30.25,29.93,19901670
|
||||
"PFE",26.37,"6/11/2007","12:21pm",-0.15,26.50,26.53,26.31,10036232
|
||||
"PG",63.01,"6/11/2007","12:20pm",-0.06,62.80,63.12,62.75,2656246
|
||||
"T",40.09,"6/11/2007","12:21pm",-0.17,40.20,40.25,39.89,5849000
|
||||
"UTX",69.80,"6/11/2007","12:20pm",-0.43,69.85,70.20,69.51,935600
|
||||
"VZ",43.24,"6/11/2007","12:20pm",+0.17,42.95,43.34,42.89,3608620
|
||||
"WMT",49.87,"6/11/2007","12:21pm",-0.21,49.90,50.00,49.55,4485833
|
||||
"XOM",83.39,"6/11/2007","12:21pm",+0.71,82.68,83.48,82.35,5559000
|
||||
"AA",39.49,"6/11/2007","12:26pm",-0.17,39.67,40.18,39.43,1856980
|
||||
"AIG",71.73,"6/11/2007","12:26pm",+0.20,71.29,71.74,71.15,1875942
|
||||
"AXP",62.90,"6/11/2007","12:26pm",-0.14,62.79,63.19,62.42,1944730
|
||||
"BA",97.75,"6/11/2007","12:26pm",-0.44,98.25,98.79,97.74,1241700
|
||||
"C",53.55,"6/11/2007","12:26pm",+0.22,53.20,53.65,52.81,4736994
|
||||
"CAT",78.87,"6/11/2007","12:25pm",+0.35,78.32,78.99,78.06,1347152
|
||||
"DD",50.70,"6/11/2007","12:26pm",-0.43,51.13,51.21,50.59,1812800
|
||||
"DIS",34.20,"6/11/2007","12:26pm",0.00,34.28,34.44,34.12,2667150
|
||||
"GE",37.41,"6/11/2007","12:26pm",+0.09,37.07,37.49,37.05,10594701
|
||||
"GM",31.27,"6/11/2007","12:26pm",+0.27,31.00,31.62,30.90,6554401
|
||||
"HD",37.73,"6/11/2007","12:26pm",-0.22,37.78,37.83,37.62,5061669
|
||||
"HON",56.96,"6/11/2007","12:26pm",-0.42,57.25,57.40,56.91,1723242
|
||||
"HPQ",46.02,"6/11/2007","12:26pm",+0.32,45.80,46.29,45.46,4379056
|
||||
"IBM",103.64,"6/11/2007","12:26pm",+0.57,102.87,103.71,102.50,2198300
|
||||
"INTC",21.97,"6/11/2007","12:31pm",+0.14,21.70,22.02,21.69,19931936
|
||||
"JNJ",62.60,"6/11/2007","12:26pm",+0.47,62.89,62.89,62.15,3566938
|
||||
"JPM",50.61,"6/11/2007","12:26pm",+0.20,50.41,50.62,50.05,3481000
|
||||
"KO",51.55,"6/11/2007","12:26pm",-0.12,51.67,51.82,51.32,5589842
|
||||
"MCD",51.37,"6/11/2007","12:26pm",-0.04,51.47,51.62,50.98,2377914
|
||||
"MMM",85.33,"6/11/2007","12:25pm",-0.61,85.94,85.98,85.32,1000600
|
||||
"MO",70.23,"6/11/2007","12:26pm",-0.07,70.25,70.50,69.76,4146657
|
||||
"MRK",50.79,"6/11/2007","12:26pm",+0.65,50.30,50.87,50.04,4803500
|
||||
"MSFT",30.145,"6/11/2007","12:31pm",+0.095,30.05,30.25,29.93,20108456
|
||||
"PFE",26.38,"6/11/2007","12:26pm",-0.14,26.50,26.53,26.31,10177632
|
||||
"PG",62.98,"6/11/2007","12:26pm",-0.09,62.80,63.12,62.75,2697146
|
||||
"T",40.12,"6/11/2007","12:26pm",-0.14,40.20,40.25,39.89,5959900
|
||||
"UTX",69.80,"6/11/2007","12:26pm",-0.43,69.85,70.20,69.51,946900
|
||||
"VZ",43.23,"6/11/2007","12:26pm",+0.16,42.95,43.34,42.89,3648320
|
||||
"WMT",49.90,"6/11/2007","12:26pm",-0.18,49.90,50.00,49.55,4562633
|
||||
"XOM",83.43,"6/11/2007","12:26pm",+0.75,82.68,83.48,82.35,5652500
|
||||
"AA",39.50,"6/11/2007","12:30pm",-0.16,39.67,40.18,39.43,1898080
|
||||
"AIG",71.74,"6/11/2007","12:31pm",+0.21,71.29,71.74,71.15,1905942
|
||||
"AXP",62.9008,"6/11/2007","12:31pm",-0.1392,62.79,63.19,62.42,1968730
|
||||
"BA",97.67,"6/11/2007","12:30pm",-0.52,98.25,98.79,97.63,1294900
|
||||
"C",53.57,"6/11/2007","12:31pm",+0.24,53.20,53.65,52.81,4767094
|
||||
"CAT",78.89,"6/11/2007","12:31pm",+0.37,78.32,78.99,78.06,1360152
|
||||
"DD",50.73,"6/11/2007","12:31pm",-0.40,51.13,51.21,50.59,1833000
|
||||
"DIS",34.195,"6/11/2007","12:31pm",-0.005,34.28,34.44,34.12,2687550
|
||||
"GE",37.41,"6/11/2007","12:31pm",+0.09,37.07,37.49,37.05,10633601
|
||||
"GM",31.25,"6/11/2007","12:31pm",+0.25,31.00,31.62,30.90,6609301
|
||||
"HD",37.74,"6/11/2007","12:30pm",-0.21,37.78,37.83,37.62,5089969
|
||||
"HON",56.95,"6/11/2007","12:31pm",-0.43,57.25,57.40,56.91,1741042
|
||||
"HPQ",46.02,"6/11/2007","12:30pm",+0.32,45.80,46.29,45.46,4453056
|
||||
"IBM",103.67,"6/11/2007","12:31pm",+0.60,102.87,103.71,102.50,2226700
|
||||
"INTC",21.96,"6/11/2007","12:36pm",+0.13,21.70,22.02,21.69,20005174
|
||||
"JNJ",62.60,"6/11/2007","12:31pm",+0.47,62.89,62.89,62.15,3584438
|
||||
"JPM",50.60,"6/11/2007","12:31pm",+0.19,50.41,50.62,50.05,3513000
|
||||
"KO",51.56,"6/11/2007","12:30pm",-0.11,51.67,51.82,51.32,5620842
|
||||
"MCD",51.34,"6/11/2007","12:31pm",-0.07,51.47,51.62,50.98,2442514
|
||||
"MMM",85.28,"6/11/2007","12:30pm",-0.66,85.94,85.98,85.28,1017900
|
||||
"MO",70.24,"6/11/2007","12:30pm",-0.06,70.25,70.50,69.76,4185257
|
||||
"MRK",50.84,"6/11/2007","12:31pm",+0.70,50.30,50.87,50.04,4848100
|
||||
"MSFT",30.14,"6/11/2007","12:36pm",+0.09,30.05,30.25,29.93,20198736
|
||||
"PFE",26.39,"6/11/2007","12:31pm",-0.13,26.50,26.53,26.31,10293432
|
||||
"PG",63.00,"6/11/2007","12:31pm",-0.07,62.80,63.12,62.75,2745846
|
||||
"T",40.07,"6/11/2007","12:31pm",-0.19,40.20,40.25,39.89,6112900
|
||||
"UTX",69.81,"6/11/2007","12:31pm",-0.42,69.85,70.20,69.51,958400
|
||||
"VZ",43.27,"6/11/2007","12:31pm",+0.20,42.95,43.34,42.89,3693720
|
||||
"WMT",49.92,"6/11/2007","12:31pm",-0.16,49.90,50.00,49.55,4676833
|
||||
"XOM",83.46,"6/11/2007","12:31pm",+0.78,82.68,83.48,82.35,5711300
|
||||
"AA",39.47,"6/11/2007","12:35pm",-0.19,39.67,40.18,39.43,1938380
|
||||
"AIG",71.705,"6/11/2007","12:35pm",+0.175,71.29,71.76,71.15,1958142
|
||||
"AXP",62.91,"6/11/2007","12:36pm",-0.13,62.79,63.19,62.42,1982630
|
||||
"BA",97.65,"6/11/2007","12:35pm",-0.54,98.25,98.79,97.61,1324100
|
||||
"C",53.56,"6/11/2007","12:36pm",+0.23,53.20,53.65,52.81,4843994
|
||||
"CAT",78.82,"6/11/2007","12:36pm",+0.30,78.32,78.99,78.06,1381852
|
||||
"DD",50.70,"6/11/2007","12:36pm",-0.43,51.13,51.21,50.59,1854300
|
||||
"DIS",34.19,"6/11/2007","12:36pm",-0.01,34.28,34.44,34.12,2871150
|
||||
"GE",37.41,"6/11/2007","12:36pm",+0.09,37.07,37.49,37.05,10703601
|
||||
"GM",31.28,"6/11/2007","12:36pm",+0.28,31.00,31.62,30.90,6667301
|
||||
"HD",37.74,"6/11/2007","12:35pm",-0.21,37.78,37.83,37.62,5127869
|
||||
"HON",56.93,"6/11/2007","12:36pm",-0.45,57.25,57.40,56.91,1772242
|
||||
"HPQ",46.01,"6/11/2007","12:35pm",+0.31,45.80,46.29,45.46,4478756
|
||||
"IBM",103.70,"6/11/2007","12:36pm",+0.63,102.87,103.73,102.50,2253600
|
||||
"INTC",21.96,"6/11/2007","12:41pm",+0.13,21.70,22.02,21.69,20062818
|
||||
"JNJ",62.59,"6/11/2007","12:36pm",+0.46,62.89,62.89,62.15,3609538
|
||||
"JPM",50.60,"6/11/2007","12:36pm",+0.19,50.41,50.62,50.05,3631000
|
||||
"KO",51.58,"6/11/2007","12:36pm",-0.09,51.67,51.82,51.32,5643442
|
||||
"MCD",51.37,"6/11/2007","12:36pm",-0.04,51.47,51.62,50.98,2479114
|
||||
"MMM",85.34,"6/11/2007","12:36pm",-0.60,85.94,85.98,85.28,1032100
|
||||
"MO",70.32,"6/11/2007","12:36pm",+0.02,70.25,70.50,69.76,4255057
|
||||
"MRK",50.90,"6/11/2007","12:36pm",+0.76,50.30,50.91,50.04,4953900
|
||||
"MSFT",30.13,"6/11/2007","12:41pm",+0.08,30.05,30.25,29.93,20258588
|
||||
"PFE",26.41,"6/11/2007","12:36pm",-0.11,26.50,26.53,26.31,10510182
|
||||
"PG",63.03,"6/11/2007","12:36pm",-0.04,62.80,63.12,62.75,2776446
|
||||
"T",40.08,"6/11/2007","12:36pm",-0.18,40.20,40.25,39.89,6259175
|
||||
"UTX",69.81,"6/11/2007","12:35pm",-0.42,69.85,70.20,69.51,964300
|
||||
"VZ",43.28,"6/11/2007","12:36pm",+0.21,42.95,43.34,42.89,3720320
|
||||
"WMT",49.94,"6/11/2007","12:36pm",-0.14,49.90,50.00,49.55,4837533
|
||||
"XOM",83.45,"6/11/2007","12:36pm",+0.77,82.68,83.50,82.35,5799000
|
||||
"AA",39.48,"6/11/2007","12:41pm",-0.18,39.67,40.18,39.43,1988580
|
||||
"AIG",71.70,"6/11/2007","12:40pm",+0.17,71.29,71.76,71.15,2020642
|
||||
"AXP",62.91,"6/11/2007","12:41pm",-0.13,62.79,63.19,62.42,1993830
|
||||
"BA",97.73,"6/11/2007","12:40pm",-0.46,98.25,98.79,97.59,1344400
|
||||
"C",53.52,"6/11/2007","12:41pm",+0.19,53.20,53.65,52.81,5118094
|
||||
"CAT",78.88,"6/11/2007","12:41pm",+0.36,78.32,78.99,78.06,1400252
|
||||
"DD",50.73,"6/11/2007","12:41pm",-0.40,51.13,51.21,50.59,1866700
|
||||
"DIS",34.181,"6/11/2007","12:41pm",-0.019,34.28,34.44,34.12,2913850
|
||||
"GE",37.42,"6/11/2007","12:41pm",+0.10,37.07,37.49,37.05,10856501
|
||||
"GM",31.32,"6/11/2007","12:41pm",+0.32,31.00,31.62,30.90,6784901
|
||||
"HD",37.75,"6/11/2007","12:41pm",-0.20,37.78,37.83,37.62,5186969
|
||||
"HON",56.96,"6/11/2007","12:41pm",-0.42,57.25,57.40,56.91,1802642
|
||||
"HPQ",46.01,"6/11/2007","12:41pm",+0.31,45.80,46.29,45.46,4507656
|
||||
"IBM",103.74,"6/11/2007","12:40pm",+0.67,102.87,103.76,102.50,2288300
|
||||
"INTC",21.96,"6/11/2007","12:46pm",+0.13,21.70,22.02,21.69,20169300
|
||||
"JNJ",62.62,"6/11/2007","12:41pm",+0.49,62.89,62.89,62.15,3651538
|
||||
"JPM",50.55,"6/11/2007","12:41pm",+0.14,50.41,50.62,50.05,3814700
|
||||
"KO",51.56,"6/11/2007","12:40pm",-0.11,51.67,51.82,51.32,5678742
|
||||
"MCD",51.40,"6/11/2007","12:41pm",-0.01,51.47,51.62,50.98,2510514
|
||||
"MMM",85.33,"6/11/2007","12:40pm",-0.61,85.94,85.98,85.28,1043700
|
||||
"MO",70.35,"6/11/2007","12:41pm",+0.05,70.25,70.50,69.76,4300857
|
||||
"MRK",51.06,"6/11/2007","12:41pm",+0.92,50.30,51.07,50.04,5195700
|
||||
"MSFT",30.13,"6/11/2007","12:46pm",+0.08,30.05,30.25,29.93,20422172
|
||||
"PFE",26.41,"6/11/2007","12:41pm",-0.11,26.50,26.53,26.31,10595782
|
||||
"PG",63.05,"6/11/2007","12:41pm",-0.02,62.80,63.12,62.75,2806346
|
||||
"T",40.06,"6/11/2007","12:41pm",-0.20,40.20,40.25,39.89,6338875
|
||||
"UTX",69.81,"6/11/2007","12:41pm",-0.42,69.85,70.20,69.51,968100
|
||||
"VZ",43.28,"6/11/2007","12:41pm",+0.21,42.95,43.34,42.89,3802620
|
||||
"WMT",49.92,"6/11/2007","12:41pm",-0.16,49.90,50.00,49.55,4964533
|
||||
"XOM",83.55,"6/11/2007","12:41pm",+0.87,82.68,83.57,82.35,5902800
|
||||
"AA",39.48,"6/11/2007","12:46pm",-0.18,39.67,40.18,39.43,2011580
|
||||
"AIG",71.68,"6/11/2007","12:45pm",+0.15,71.29,71.76,71.15,2066242
|
||||
"AXP",62.93,"6/11/2007","12:46pm",-0.11,62.79,63.19,62.42,2001530
|
||||
"BA",97.68,"6/11/2007","12:46pm",-0.51,98.25,98.79,97.59,1387800
|
||||
"C",53.54,"6/11/2007","12:46pm",+0.21,53.20,53.65,52.81,5168294
|
||||
"CAT",78.87,"6/11/2007","12:46pm",+0.35,78.32,78.99,78.06,1416552
|
||||
"DD",50.70,"6/11/2007","12:46pm",-0.43,51.13,51.21,50.59,1884900
|
||||
"DIS",34.20,"6/11/2007","12:46pm",0.00,34.28,34.44,34.12,2958150
|
||||
"GE",37.40,"6/11/2007","12:46pm",+0.08,37.07,37.49,37.05,10931901
|
||||
"GM",31.29,"6/11/2007","12:46pm",+0.29,31.00,31.62,30.90,6930401
|
||||
"HD",37.75,"6/11/2007","12:46pm",-0.20,37.78,37.83,37.62,5233269
|
||||
"HON",56.99,"6/11/2007","12:46pm",-0.39,57.25,57.40,56.91,1827642
|
||||
"HPQ",46.01,"6/11/2007","12:46pm",+0.31,45.80,46.29,45.46,4557656
|
||||
"IBM",103.75,"6/11/2007","12:46pm",+0.68,102.87,103.78,102.50,2310700
|
||||
"INTC",22.00,"6/11/2007","12:51pm",+0.17,21.70,22.02,21.69,20568860
|
||||
"JNJ",62.59,"6/11/2007","12:46pm",+0.46,62.89,62.89,62.15,3702738
|
||||
"JPM",50.57,"6/11/2007","12:46pm",+0.16,50.41,50.62,50.05,3902300
|
||||
"KO",51.58,"6/11/2007","12:46pm",-0.09,51.67,51.82,51.32,5707242
|
||||
"MCD",51.44,"6/11/2007","12:46pm",+0.03,51.47,51.62,50.98,2534014
|
||||
"MMM",85.33,"6/11/2007","12:45pm",-0.61,85.94,85.98,85.28,1051300
|
||||
"MO",70.32,"6/11/2007","12:46pm",+0.02,70.25,70.50,69.76,4330057
|
||||
"MRK",50.97,"6/11/2007","12:46pm",+0.83,50.30,51.07,50.04,5430200
|
||||
"MSFT",30.13,"6/11/2007","12:51pm",+0.08,30.05,30.25,29.93,20730214
|
||||
"PFE",26.43,"6/11/2007","12:46pm",-0.09,26.50,26.53,26.31,10698282
|
||||
"PG",63.05,"6/11/2007","12:46pm",-0.02,62.80,63.12,62.75,2909046
|
||||
"T",40.06,"6/11/2007","12:46pm",-0.20,40.20,40.25,39.89,6387575
|
||||
"UTX",69.81,"6/11/2007","12:46pm",-0.42,69.85,70.20,69.51,976200
|
||||
"VZ",43.28,"6/11/2007","12:46pm",+0.21,42.95,43.34,42.89,3826420
|
||||
"WMT",49.95,"6/11/2007","12:46pm",-0.13,49.90,50.00,49.55,5329433
|
||||
"XOM",83.58,"6/11/2007","12:46pm",+0.90,82.68,83.59,82.35,5965700
|
||||
"AA",39.48,"6/11/2007","12:51pm",-0.18,39.67,40.18,39.43,2052980
|
||||
"AIG",71.67,"6/11/2007","12:51pm",+0.14,71.29,71.76,71.15,2109942
|
||||
"AXP",62.90,"6/11/2007","12:50pm",-0.14,62.79,63.19,62.42,2024830
|
||||
"BA",97.78,"6/11/2007","12:51pm",-0.41,98.25,98.79,97.59,1430600
|
||||
"C",53.56,"6/11/2007","12:51pm",+0.23,53.20,53.65,52.81,5509594
|
||||
"CAT",79.00,"6/11/2007","12:50pm",+0.48,78.32,79.00,78.06,1448852
|
||||
"DD",50.71,"6/11/2007","12:51pm",-0.42,51.13,51.21,50.59,1916300
|
||||
"DIS",34.192,"6/11/2007","12:50pm",-0.008,34.28,34.44,34.12,3022750
|
||||
"GE",37.41,"6/11/2007","12:51pm",+0.09,37.07,37.49,37.05,11159401
|
||||
"GM",31.38,"6/11/2007","12:51pm",+0.38,31.00,31.62,30.90,7135301
|
||||
"HD",37.76,"6/11/2007","12:51pm",-0.19,37.78,37.83,37.62,5362369
|
||||
"HON",57.05,"6/11/2007","12:51pm",-0.33,57.25,57.40,56.91,1871542
|
||||
"HPQ",46.03,"6/11/2007","12:51pm",+0.33,45.80,46.29,45.46,4684356
|
||||
"IBM",103.87,"6/11/2007","12:51pm",+0.80,102.87,103.89,102.50,2362500
|
||||
"INTC",22.00,"6/11/2007","12:56pm",+0.17,21.70,22.02,21.69,20977118
|
||||
"JNJ",62.58,"6/11/2007","12:51pm",+0.45,62.89,62.89,62.15,3790288
|
||||
"JPM",50.60,"6/11/2007","12:51pm",+0.19,50.41,50.63,50.05,3966000
|
||||
"KO",51.59,"6/11/2007","12:51pm",-0.08,51.67,51.82,51.32,5755942
|
||||
"MCD",51.42,"6/11/2007","12:50pm",+0.01,51.47,51.62,50.98,2580014
|
||||
"MMM",85.36,"6/11/2007","12:50pm",-0.58,85.94,85.98,85.28,1063300
|
||||
"MO",70.37,"6/11/2007","12:51pm",+0.07,70.25,70.50,69.76,4395657
|
||||
"MRK",50.97,"6/11/2007","12:51pm",+0.83,50.30,51.07,50.04,5527700
|
||||
"MSFT",30.17,"6/11/2007","12:56pm",+0.12,30.05,30.25,29.93,21243952
|
||||
"PFE",26.43,"6/11/2007","12:51pm",-0.09,26.50,26.53,26.31,10870382
|
||||
"PG",63.04,"6/11/2007","12:51pm",-0.03,62.80,63.12,62.75,3002346
|
||||
"T",40.01,"6/11/2007","12:51pm",-0.25,40.20,40.25,39.89,6578675
|
||||
"UTX",69.88,"6/11/2007","12:50pm",-0.35,69.85,70.20,69.51,986100
|
||||
"VZ",43.30,"6/11/2007","12:51pm",+0.23,42.95,43.34,42.89,3880620
|
||||
"WMT",49.9786,"6/11/2007","12:51pm",-0.1014,49.90,50.00,49.55,5399333
|
||||
"XOM",83.67,"6/11/2007","12:51pm",+0.99,82.68,83.67,82.35,6065100
|
||||
"AA",39.50,"6/11/2007","12:56pm",-0.16,39.67,40.18,39.43,2111680
|
||||
"AIG",71.68,"6/11/2007","12:56pm",+0.15,71.29,71.76,71.15,2166942
|
||||
"AXP",63.00,"6/11/2007","12:56pm",-0.04,62.79,63.19,62.42,2038530
|
||||
"BA",97.71,"6/11/2007","12:56pm",-0.48,98.25,98.79,97.59,1475700
|
||||
"C",53.5614,"6/11/2007","12:56pm",+0.2314,53.20,53.65,52.81,5665694
|
||||
"CAT",79.14,"6/11/2007","12:56pm",+0.62,78.32,79.14,78.06,1510252
|
||||
"DD",50.79,"6/11/2007","12:56pm",-0.34,51.13,51.21,50.59,1934600
|
||||
"DIS",34.20,"6/11/2007","12:56pm",0.00,34.28,34.44,34.12,3112050
|
||||
"GE",37.47,"6/11/2007","12:56pm",+0.15,37.07,37.49,37.05,11425501
|
||||
"GM",31.37,"6/11/2007","12:56pm",+0.37,31.00,31.62,30.90,7225026
|
||||
"HD",37.77,"6/11/2007","12:56pm",-0.18,37.78,37.83,37.62,5433169
|
||||
"HON",57.11,"6/11/2007","12:56pm",-0.27,57.25,57.40,56.91,1911042
|
||||
"HPQ",46.07,"6/11/2007","12:55pm",+0.37,45.80,46.29,45.46,4729356
|
||||
"IBM",103.97,"6/11/2007","12:56pm",+0.90,102.87,104.00,102.50,2444600
|
||||
"INTC",22.00,"6/11/2007","1:01pm",+0.17,21.70,22.02,21.69,21559788
|
||||
"JNJ",62.59,"6/11/2007","12:56pm",+0.46,62.89,62.89,62.15,3837538
|
||||
"JPM",50.65,"6/11/2007","12:56pm",+0.24,50.41,50.65,50.05,4042900
|
||||
"KO",51.67,"6/11/2007","12:56pm",0.00,51.67,51.82,51.32,5793842
|
||||
"MCD",51.44,"6/11/2007","12:56pm",+0.03,51.47,51.62,50.98,2635114
|
||||
"MMM",85.46,"6/11/2007","12:55pm",-0.48,85.94,85.98,85.28,1082400
|
||||
"MO",70.35,"6/11/2007","12:56pm",+0.05,70.25,70.50,69.76,4427157
|
||||
"MRK",50.96,"6/11/2007","12:56pm",+0.82,50.30,51.07,50.04,5634400
|
||||
"MSFT",30.14,"6/11/2007","1:01pm",+0.09,30.05,30.25,29.93,21696948
|
||||
"PFE",26.45,"6/11/2007","12:56pm",-0.07,26.50,26.53,26.31,11036032
|
||||
"PG",63.08,"6/11/2007","12:56pm",+0.01,62.80,63.12,62.75,3066446
|
||||
"T",40.04,"6/11/2007","12:56pm",-0.22,40.20,40.25,39.89,6709275
|
||||
"UTX",69.92,"6/11/2007","12:56pm",-0.31,69.85,70.20,69.51,1008800
|
||||
"VZ",43.32,"6/11/2007","12:56pm",+0.25,42.95,43.34,42.89,3928820
|
||||
"WMT",50.01,"6/11/2007","12:56pm",-0.07,49.90,50.04,49.55,5568433
|
||||
"XOM",83.66,"6/11/2007","12:56pm",+0.98,82.68,83.72,82.35,6158700
|
||||
"AA",39.51,"6/11/2007","1:01pm",-0.15,39.67,40.18,39.43,2207380
|
||||
"AIG",71.64,"6/11/2007","1:01pm",+0.11,71.29,71.76,71.15,2223342
|
||||
"AXP",62.96,"6/11/2007","1:01pm",-0.08,62.79,63.19,62.42,2056230
|
||||
"BA",97.65,"6/11/2007","1:01pm",-0.54,98.25,98.79,97.59,1502900
|
||||
"C",53.58,"6/11/2007","1:01pm",+0.25,53.20,53.65,52.81,5805794
|
||||
"CAT",79.07,"6/11/2007","1:00pm",+0.55,78.32,79.14,78.06,1532652
|
||||
"DD",50.7784,"6/11/2007","1:01pm",-0.3516,51.13,51.21,50.59,1957300
|
||||
"DIS",34.20,"6/11/2007","1:01pm",0.00,34.28,34.44,34.12,3159250
|
||||
"GE",37.48,"6/11/2007","1:01pm",+0.16,37.07,37.49,37.05,11769301
|
||||
"GM",31.34,"6/11/2007","1:01pm",+0.34,31.00,31.62,30.90,7327251
|
||||
"HD",37.75,"6/11/2007","1:01pm",-0.20,37.78,37.83,37.62,5482469
|
||||
"HON",56.98,"6/11/2007","1:01pm",-0.40,57.25,57.40,56.91,1964142
|
||||
"HPQ",46.05,"6/11/2007","1:01pm",+0.35,45.80,46.29,45.46,4778356
|
||||
"IBM",103.79,"6/11/2007","1:01pm",+0.72,102.87,104.00,102.50,2477300
|
||||
"INTC",22.01,"6/11/2007","1:06pm",+0.18,21.70,22.02,21.69,21974040
|
||||
"JNJ",62.55,"6/11/2007","1:01pm",+0.42,62.89,62.89,62.15,4653538
|
||||
"JPM",50.64,"6/11/2007","1:01pm",+0.23,50.41,50.66,50.05,4113700
|
||||
"KO",51.61,"6/11/2007","1:01pm",-0.06,51.67,51.82,51.32,5829142
|
||||
"MCD",51.40,"6/11/2007","1:01pm",-0.01,51.47,51.62,50.98,2668014
|
||||
"MMM",85.38,"6/11/2007","1:00pm",-0.56,85.94,85.98,85.28,1091500
|
||||
"MO",70.29,"6/11/2007","1:01pm",-0.01,70.25,70.50,69.76,4483057
|
||||
"MRK",50.95,"6/11/2007","1:01pm",+0.81,50.30,51.07,50.04,5728600
|
||||
"MSFT",30.14,"6/11/2007","1:06pm",+0.09,30.05,30.25,29.93,21878754
|
||||
"PFE",26.44,"6/11/2007","1:01pm",-0.08,26.50,26.53,26.31,11232332
|
||||
"PG",63.01,"6/11/2007","1:01pm",-0.06,62.80,63.12,62.75,3117846
|
||||
"T",40.03,"6/11/2007","1:01pm",-0.23,40.20,40.25,39.89,6799375
|
||||
"UTX",69.90,"6/11/2007","1:00pm",-0.33,69.85,70.20,69.51,1060200
|
||||
"VZ",43.25,"6/11/2007","1:01pm",+0.18,42.95,43.34,42.89,4008920
|
||||
"WMT",50.01,"6/11/2007","1:01pm",-0.07,49.90,50.04,49.55,5777993
|
||||
"XOM",83.57,"6/11/2007","1:01pm",+0.89,82.68,83.72,82.35,6284800
|
||||
"AA",39.52,"6/11/2007","1:06pm",-0.14,39.67,40.18,39.43,2284880
|
||||
"AIG",71.69,"6/11/2007","1:06pm",+0.16,71.29,71.76,71.15,2274642
|
||||
"AXP",62.95,"6/11/2007","1:06pm",-0.09,62.79,63.19,62.42,2065330
|
||||
"BA",97.65,"6/11/2007","1:06pm",-0.54,98.25,98.79,97.59,1513500
|
||||
"C",53.57,"6/11/2007","1:06pm",+0.24,53.20,53.65,52.81,5889294
|
||||
"CAT",79.07,"6/11/2007","1:05pm",+0.55,78.32,79.14,78.06,1549052
|
||||
"DD",50.79,"6/11/2007","1:06pm",-0.34,51.13,51.21,50.59,1978300
|
||||
"DIS",34.195,"6/11/2007","1:06pm",-0.005,34.28,34.44,34.12,3179550
|
||||
"GE",37.48,"6/11/2007","1:06pm",+0.16,37.07,37.49,37.05,11921001
|
||||
"GM",31.34,"6/11/2007","1:06pm",+0.34,31.00,31.62,30.90,7418551
|
||||
"HD",37.75,"6/11/2007","1:06pm",-0.20,37.78,37.83,37.62,5559869
|
||||
"HON",56.99,"6/11/2007","1:06pm",-0.39,57.25,57.40,56.91,2007542
|
||||
"HPQ",46.0627,"6/11/2007","1:05pm",+0.3627,45.80,46.29,45.46,4829456
|
||||
"IBM",103.76,"6/11/2007","1:05pm",+0.69,102.87,104.00,102.50,2503900
|
||||
"INTC",22.04,"6/11/2007","1:11pm",+0.21,21.70,22.05,21.69,22780160
|
||||
"JNJ",62.58,"6/11/2007","1:06pm",+0.45,62.89,62.89,62.15,4682578
|
||||
"JPM",50.649,"6/11/2007","1:06pm",+0.239,50.41,50.66,50.05,4156000
|
||||
"KO",51.68,"6/11/2007","1:06pm",+0.01,51.67,51.82,51.32,5958742
|
||||
"MCD",51.38,"6/11/2007","1:06pm",-0.03,51.47,51.62,50.98,2710414
|
||||
"MMM",85.40,"6/11/2007","1:05pm",-0.54,85.94,85.98,85.28,1115800
|
||||
"MO",70.29,"6/11/2007","1:06pm",-0.01,70.25,70.50,69.76,4510357
|
||||
"MRK",50.95,"6/11/2007","1:06pm",+0.81,50.30,51.07,50.04,5784900
|
||||
"MSFT",30.16,"6/11/2007","1:11pm",+0.11,30.05,30.25,29.93,22141974
|
||||
"PFE",26.50,"6/11/2007","1:06pm",-0.02,26.50,26.53,26.31,11587539
|
||||
"PG",63.07,"6/11/2007","1:06pm",0.00,62.80,63.12,62.75,3192946
|
||||
"T",40.13,"6/11/2007","1:06pm",-0.13,40.20,40.25,39.89,6957775
|
||||
"UTX",69.91,"6/11/2007","1:06pm",-0.32,69.85,70.20,69.51,1075500
|
||||
"VZ",43.2716,"6/11/2007","1:05pm",+0.2016,42.95,43.34,42.89,4057020
|
||||
"WMT",50.07,"6/11/2007","1:06pm",-0.01,49.90,50.08,49.55,5985207
|
||||
"XOM",83.53,"6/11/2007","1:06pm",+0.85,82.68,83.72,82.35,6388600
|
||||
"AA",39.53,"6/11/2007","1:11pm",-0.13,39.67,40.18,39.43,2319180
|
||||
"AIG",71.74,"6/11/2007","1:11pm",+0.21,71.29,71.76,71.15,2359542
|
||||
"AXP",63.12,"6/11/2007","1:11pm",+0.08,62.79,63.19,62.42,2086730
|
||||
"BA",97.75,"6/11/2007","1:11pm",-0.44,98.25,98.79,97.59,1556900
|
||||
"C",53.67,"6/11/2007","1:11pm",+0.34,53.20,53.69,52.81,6010594
|
||||
"CAT",79.17,"6/11/2007","1:11pm",+0.65,78.32,79.19,78.06,1577652
|
||||
"DD",50.79,"6/11/2007","1:11pm",-0.34,51.13,51.21,50.59,2054300
|
||||
"DIS",34.22,"6/11/2007","1:11pm",+0.02,34.28,34.44,34.12,3245950
|
||||
"GE",37.53,"6/11/2007","1:11pm",+0.21,37.07,37.53,37.05,12200301
|
||||
"GM",31.42,"6/11/2007","1:11pm",+0.42,31.00,31.62,30.90,7518551
|
||||
"HD",37.78,"6/11/2007","1:11pm",-0.17,37.78,37.83,37.62,5639569
|
||||
"HON",57.13,"6/11/2007","1:11pm",-0.25,57.25,57.40,56.91,2042642
|
||||
"HPQ",46.12,"6/11/2007","1:11pm",+0.42,45.80,46.29,45.46,5833149
|
||||
"IBM",103.86,"6/11/2007","1:11pm",+0.79,102.87,104.00,102.50,2536300
|
||||
"INTC",22.05,"6/11/2007","1:16pm",+0.22,21.70,22.07,21.69,23218578
|
||||
"JNJ",62.65,"6/11/2007","1:11pm",+0.52,62.89,62.89,62.15,4727878
|
||||
"JPM",50.69,"6/11/2007","1:11pm",+0.28,50.41,50.695,50.05,4239700
|
||||
"KO",51.74,"6/11/2007","1:11pm",+0.07,51.67,51.82,51.32,6054742
|
||||
"MCD",51.44,"6/11/2007","1:11pm",+0.03,51.47,51.62,50.98,2762114
|
||||
"MMM",85.53,"6/11/2007","1:11pm",-0.41,85.94,85.98,85.28,1145200
|
||||
"MO",70.41,"6/11/2007","1:11pm",+0.11,70.25,70.50,69.76,4563257
|
||||
"MRK",51.12,"6/11/2007","1:11pm",+0.98,50.30,51.13,50.04,5899900
|
||||
"MSFT",30.17,"6/11/2007","1:16pm",+0.12,30.05,30.25,29.93,22399100
|
||||
"PFE",26.5275,"6/11/2007","1:11pm",+0.0075,26.50,26.53,26.31,12044278
|
||||
"PG",63.0995,"6/11/2007","1:11pm",+0.0295,62.80,63.12,62.75,3229446
|
||||
"T",40.19,"6/11/2007","1:11pm",-0.07,40.20,40.25,39.89,7073875
|
||||
"UTX",69.97,"6/11/2007","1:11pm",-0.26,69.85,70.20,69.51,1092400
|
||||
"VZ",43.34,"6/11/2007","1:10pm",+0.27,42.95,43.34,42.88,4847820
|
||||
"WMT",50.08,"6/11/2007","1:11pm",0.00,49.90,50.12,49.55,6200407
|
||||
"XOM",83.66,"6/11/2007","1:11pm",+0.98,82.68,83.72,82.35,6490400
|
||||
"AA",39.56,"6/11/2007","1:16pm",-0.10,39.67,40.18,39.43,2433580
|
||||
"AIG",71.80,"6/11/2007","1:16pm",+0.27,71.29,71.83,71.15,2440996
|
||||
"AXP",63.20,"6/11/2007","1:15pm",+0.16,62.79,63.21,62.42,2110030
|
||||
"BA",97.85,"6/11/2007","1:16pm",-0.34,98.25,98.79,97.59,1590400
|
||||
"C",53.679,"6/11/2007","1:16pm",+0.349,53.20,53.71,52.81,6103294
|
||||
"CAT",79.35,"6/11/2007","1:16pm",+0.83,78.32,79.39,78.06,1651052
|
||||
"DD",50.85,"6/11/2007","1:16pm",-0.28,51.13,51.21,50.59,2111497
|
||||
"DIS",34.21,"6/11/2007","1:15pm",+0.01,34.28,34.44,34.12,3343750
|
||||
"GE",37.535,"6/11/2007","1:16pm",+0.215,37.07,37.54,37.05,12385801
|
||||
"GM",31.45,"6/11/2007","1:16pm",+0.45,31.00,31.62,30.90,7588451
|
||||
"HD",37.77,"6/11/2007","1:16pm",-0.18,37.78,37.83,37.62,5820967
|
||||
"HON",57.11,"6/11/2007","1:16pm",-0.27,57.25,57.40,56.91,2057642
|
||||
"HPQ",46.12,"6/11/2007","1:16pm",+0.42,45.80,46.29,45.46,5922449
|
||||
"IBM",103.83,"6/11/2007","1:16pm",+0.76,102.87,104.00,102.50,2579600
|
||||
"INTC",22.06,"6/11/2007","1:21pm",+0.23,21.70,22.08,21.69,23707494
|
||||
"JNJ",62.64,"6/11/2007","1:16pm",+0.51,62.89,62.89,62.15,4771978
|
||||
"JPM",50.75,"6/11/2007","1:16pm",+0.34,50.41,50.78,50.05,4349600
|
||||
"KO",51.73,"6/11/2007","1:16pm",+0.06,51.67,51.82,51.32,6085542
|
||||
"MCD",51.47,"6/11/2007","1:16pm",+0.06,51.47,51.62,50.98,2786414
|
||||
"MMM",85.56,"6/11/2007","1:16pm",-0.38,85.94,85.98,85.28,1180100
|
||||
"MO",70.37,"6/11/2007","1:16pm",+0.07,70.25,70.50,69.76,4636857
|
||||
"MRK",51.15,"6/11/2007","1:16pm",+1.01,50.30,51.16,50.04,6009400
|
||||
"MSFT",30.14,"6/11/2007","1:21pm",+0.09,30.05,30.25,29.93,23011740
|
||||
"PFE",26.51,"6/11/2007","1:16pm",-0.01,26.50,26.54,26.31,12225128
|
||||
"PG",63.10,"6/11/2007","1:16pm",+0.03,62.80,63.12,62.75,3329546
|
||||
"T",40.22,"6/11/2007","1:16pm",-0.04,40.20,40.26,39.89,7235875
|
||||
"UTX",69.95,"6/11/2007","1:16pm",-0.28,69.85,70.20,69.51,1122100
|
||||
"VZ",43.37,"6/11/2007","1:16pm",+0.30,42.95,43.38,42.88,4953820
|
||||
"WMT",50.04,"6/11/2007","1:16pm",-0.04,49.90,50.12,49.55,6345707
|
||||
"XOM",83.58,"6/11/2007","1:16pm",+0.90,82.68,83.72,82.35,6598900
|
||||
"AA",39.58,"6/11/2007","1:21pm",-0.08,39.67,40.18,39.43,2500980
|
||||
"AIG",71.90,"6/11/2007","1:21pm",+0.37,71.29,71.89,71.15,2494796
|
||||
"AXP",63.22,"6/11/2007","1:21pm",+0.18,62.79,63.25,62.42,2135530
|
||||
"BA",97.81,"6/11/2007","1:20pm",-0.38,98.25,98.79,97.59,1608700
|
||||
"C",53.76,"6/11/2007","1:21pm",+0.43,53.20,53.77,52.81,6249294
|
||||
"CAT",79.45,"6/11/2007","1:21pm",+0.93,78.32,79.45,78.06,1689152
|
||||
"DD",50.89,"6/11/2007","1:21pm",-0.24,51.13,51.21,50.59,2132297
|
||||
"DIS",34.23,"6/11/2007","1:20pm",+0.03,34.28,34.44,34.12,3370250
|
||||
"GE",37.53,"6/11/2007","1:21pm",+0.21,37.07,37.56,37.05,12700001
|
||||
"GM",31.43,"6/11/2007","1:21pm",+0.43,31.00,31.62,30.90,7705751
|
||||
"HD",37.76,"6/11/2007","1:21pm",-0.19,37.78,37.83,37.62,5879467
|
||||
"HON",57.17,"6/11/2007","1:21pm",-0.21,57.25,57.40,56.91,2107242
|
||||
"HPQ",46.17,"6/11/2007","1:21pm",+0.47,45.80,46.29,45.46,5961749
|
||||
"IBM",103.81,"6/11/2007","1:21pm",+0.74,102.87,104.00,102.50,2608100
|
||||
"INTC",22.04,"6/11/2007","1:26pm",+0.21,21.70,22.08,21.69,24314782
|
||||
"JNJ",62.66,"6/11/2007","1:21pm",+0.53,62.89,62.89,62.15,4802578
|
||||
"JPM",50.83,"6/11/2007","1:21pm",+0.42,50.41,50.84,50.05,4419800
|
||||
"KO",51.79,"6/11/2007","1:21pm",+0.12,51.67,51.82,51.32,6135742
|
||||
"MCD",51.48,"6/11/2007","1:21pm",+0.07,51.47,51.62,50.98,2853314
|
||||
"MMM",85.64,"6/11/2007","1:21pm",-0.30,85.94,85.98,85.28,1258800
|
||||
"MO",70.41,"6/11/2007","1:21pm",+0.11,70.25,70.50,69.76,4723085
|
||||
"MRK",51.22,"6/11/2007","1:21pm",+1.08,50.30,51.23,50.04,6148300
|
||||
"MSFT",30.1301,"6/11/2007","1:25pm",+0.0801,30.05,30.25,29.93,23292696
|
||||
"PFE",26.50,"6/11/2007","1:21pm",-0.02,26.50,26.54,26.31,12653153
|
||||
"PG",63.10,"6/11/2007","1:21pm",+0.03,62.80,63.12,62.75,3736946
|
||||
"T",40.28,"6/11/2007","1:21pm",+0.02,40.20,40.29,39.89,7365200
|
||||
"UTX",70.02,"6/11/2007","1:21pm",-0.21,69.85,70.20,69.51,1158800
|
||||
"VZ",43.43,"6/11/2007","1:21pm",+0.36,42.95,43.44,42.88,5053620
|
||||
"WMT",50.02,"6/11/2007","1:21pm",-0.06,49.90,50.12,49.55,6585307
|
||||
"XOM",83.66,"6/11/2007","1:21pm",+0.98,82.68,83.72,82.35,6705800
|
||||
"AA",39.52,"6/11/2007","1:26pm",-0.14,39.67,40.18,39.43,2540480
|
||||
"AIG",71.85,"6/11/2007","1:26pm",+0.32,71.29,71.90,71.15,2549996
|
||||
"AXP",63.18,"6/11/2007","1:26pm",+0.14,62.79,63.26,62.42,2157330
|
||||
"BA",97.71,"6/11/2007","1:26pm",-0.48,98.25,98.79,97.59,1636600
|
||||
"C",53.72,"6/11/2007","1:26pm",+0.39,53.20,53.77,52.81,6427294
|
||||
"CAT",79.20,"6/11/2007","1:26pm",+0.68,78.32,79.46,78.06,1768752
|
||||
"DD",50.86,"6/11/2007","1:26pm",-0.27,51.13,51.21,50.59,2150397
|
||||
"DIS",34.22,"6/11/2007","1:26pm",+0.02,34.28,34.44,34.12,3496150
|
||||
"GE",37.50,"6/11/2007","1:26pm",+0.18,37.07,37.56,37.05,12934301
|
||||
"GM",31.40,"6/11/2007","1:26pm",+0.40,31.00,31.62,30.90,7805351
|
||||
"HD",37.75,"6/11/2007","1:26pm",-0.20,37.78,37.83,37.62,5995967
|
||||
"HON",57.20,"6/11/2007","1:26pm",-0.18,57.25,57.40,56.91,2149442
|
||||
"HPQ",46.14,"6/11/2007","1:26pm",+0.44,45.80,46.29,45.46,6019149
|
||||
"IBM",103.70,"6/11/2007","1:26pm",+0.63,102.87,104.00,102.50,2638000
|
||||
"INTC",22.04,"6/11/2007","1:31pm",+0.21,21.70,22.08,21.69,24406300
|
||||
"JNJ",62.63,"6/11/2007","1:26pm",+0.50,62.89,62.89,62.15,4835178
|
||||
"JPM",50.81,"6/11/2007","1:26pm",+0.40,50.41,50.84,50.05,4480900
|
||||
"KO",51.78,"6/11/2007","1:26pm",+0.11,51.67,51.85,51.32,6233852
|
||||
"MCD",51.44,"6/11/2007","1:26pm",+0.03,51.47,51.62,50.98,2881414
|
||||
"MMM",85.62,"6/11/2007","1:26pm",-0.32,85.94,85.98,85.28,1282500
|
||||
"MO",70.36,"6/11/2007","1:26pm",+0.06,70.25,70.50,69.76,4767885
|
||||
"MRK",51.23,"6/11/2007","1:26pm",+1.09,50.30,51.27,50.04,6334000
|
||||
"MSFT",30.12,"6/11/2007","1:31pm",+0.07,30.05,30.25,29.93,23578844
|
||||
"PFE",26.50,"6/11/2007","1:26pm",-0.02,26.50,26.54,26.31,14778927
|
||||
"PG",63.09,"6/11/2007","1:26pm",+0.02,62.80,63.12,62.75,3928946
|
||||
"T",40.31,"6/11/2007","1:26pm",+0.05,40.20,40.33,39.89,7505100
|
||||
"UTX",69.98,"6/11/2007","1:26pm",-0.25,69.85,70.20,69.51,1210900
|
||||
"VZ",43.40,"6/11/2007","1:26pm",+0.33,42.95,43.45,42.88,5123120
|
||||
"WMT",49.96,"6/11/2007","1:26pm",-0.12,49.90,50.12,49.55,6687607
|
||||
"XOM",83.57,"6/11/2007","1:26pm",+0.89,82.68,83.72,82.35,6797200
|
||||
"AA",39.53,"6/11/2007","1:31pm",-0.13,39.67,40.18,39.43,2572580
|
||||
"AIG",71.85,"6/11/2007","1:31pm",+0.32,71.29,71.90,71.15,2602596
|
||||
"AXP",63.26,"6/11/2007","1:31pm",+0.22,62.79,63.26,62.42,2172130
|
||||
"BA",97.82,"6/11/2007","1:31pm",-0.37,98.25,98.79,97.59,1659000
|
||||
"C",53.74,"6/11/2007","1:31pm",+0.41,53.20,53.77,52.81,6462694
|
||||
"CAT",79.20,"6/11/2007","1:31pm",+0.68,78.32,79.46,78.06,1829052
|
||||
"DD",50.85,"6/11/2007","1:31pm",-0.28,51.13,51.21,50.59,2171097
|
||||
"DIS",34.24,"6/11/2007","1:31pm",+0.04,34.28,34.44,34.12,3579950
|
||||
"GE",37.52,"6/11/2007","1:31pm",+0.20,37.07,37.56,37.05,13012501
|
||||
"GM",31.42,"6/11/2007","1:31pm",+0.42,31.00,31.62,30.90,7927151
|
||||
"HD",37.76,"6/11/2007","1:31pm",-0.19,37.78,37.83,37.62,6189667
|
||||
"HON",57.21,"6/11/2007","1:31pm",-0.17,57.25,57.40,56.91,2172142
|
||||
"HPQ",46.14,"6/11/2007","1:31pm",+0.44,45.80,46.29,45.46,6057949
|
||||
"IBM",103.63,"6/11/2007","1:31pm",+0.56,102.87,104.00,102.50,2660504
|
||||
"INTC",22.04,"6/11/2007","1:36pm",+0.21,21.70,22.08,21.69,24689632
|
||||
"JNJ",62.61,"6/11/2007","1:31pm",+0.48,62.89,62.89,62.15,4868578
|
||||
"JPM",50.80,"6/11/2007","1:31pm",+0.39,50.41,50.84,50.05,4526800
|
||||
"KO",51.75,"6/11/2007","1:31pm",+0.08,51.67,51.85,51.32,6294152
|
||||
"MCD",51.43,"6/11/2007","1:31pm",+0.02,51.47,51.62,50.98,2901114
|
||||
"MMM",85.59,"6/11/2007","1:31pm",-0.35,85.94,85.98,85.28,1297000
|
||||
"MO",70.34,"6/11/2007","1:31pm",+0.04,70.25,70.50,69.76,4818885
|
||||
"MRK",51.24,"6/11/2007","1:31pm",+1.10,50.30,51.27,50.04,6438900
|
||||
"MSFT",30.125,"6/11/2007","1:36pm",+0.075,30.05,30.25,29.93,23983122
|
||||
"PFE",26.51,"6/11/2007","1:31pm",-0.01,26.50,26.54,26.31,14886127
|
||||
"PG",63.08,"6/11/2007","1:31pm",+0.01,62.80,63.13,62.75,3971046
|
||||
"T",40.31,"6/11/2007","1:31pm",+0.05,40.20,40.33,39.89,7610100
|
||||
"UTX",70.05,"6/11/2007","1:31pm",-0.18,69.85,70.20,69.51,1239200
|
||||
"VZ",43.39,"6/11/2007","1:31pm",+0.32,42.95,43.45,42.88,5198120
|
||||
"WMT",50.00,"6/11/2007","1:31pm",-0.08,49.90,50.12,49.55,6808307
|
||||
"XOM",83.60,"6/11/2007","1:31pm",+0.92,82.68,83.72,82.35,6858500
|
||||
"AA",39.54,"6/11/2007","1:36pm",-0.12,39.67,40.18,39.43,2615380
|
||||
"AIG",71.90,"6/11/2007","1:36pm",+0.37,71.29,71.90,71.15,2648396
|
||||
"AXP",63.27,"6/11/2007","1:36pm",+0.23,62.79,63.28,62.42,2187930
|
||||
"BA",97.88,"6/11/2007","1:36pm",-0.31,98.25,98.79,97.59,1700700
|
||||
"C",53.73,"6/11/2007","1:36pm",+0.40,53.20,53.77,52.81,6558794
|
||||
"CAT",79.20,"6/11/2007","1:36pm",+0.68,78.32,79.46,78.06,1869352
|
||||
"DD",50.89,"6/11/2007","1:36pm",-0.24,51.13,51.21,50.59,2184797
|
||||
"DIS",34.22,"6/11/2007","1:36pm",+0.02,34.28,34.44,34.12,3654250
|
||||
"GE",37.51,"6/11/2007","1:36pm",+0.19,37.07,37.56,37.05,13175301
|
||||
"GM",31.45,"6/11/2007","1:36pm",+0.45,31.00,31.62,30.90,8227251
|
||||
"HD",37.76,"6/11/2007","1:36pm",-0.19,37.78,37.83,37.62,6273567
|
||||
"HON",57.24,"6/11/2007","1:36pm",-0.14,57.25,57.40,56.91,2203642
|
||||
"HPQ",46.17,"6/11/2007","1:36pm",+0.47,45.80,46.29,45.46,6121849
|
||||
"IBM",103.59,"6/11/2007","1:36pm",+0.52,102.87,104.00,102.50,2702104
|
||||
"INTC",22.02,"6/11/2007","1:41pm",+0.19,21.70,22.08,21.69,25010946
|
||||
"JNJ",62.60,"6/11/2007","1:36pm",+0.47,62.89,62.89,62.15,4923778
|
||||
"JPM",50.77,"6/11/2007","1:36pm",+0.36,50.41,50.84,50.05,4660900
|
||||
"KO",51.79,"6/11/2007","1:36pm",+0.12,51.67,51.85,51.32,6365852
|
||||
"MCD",51.42,"6/11/2007","1:36pm",+0.01,51.47,51.62,50.98,2935214
|
||||
"MMM",85.57,"6/11/2007","1:36pm",-0.37,85.94,85.98,85.28,1313500
|
||||
"MO",70.37,"6/11/2007","1:36pm",+0.07,70.25,70.50,69.76,4877785
|
||||
"MRK",51.24,"6/11/2007","1:36pm",+1.10,50.30,51.28,50.04,6515500
|
||||
"MSFT",30.12,"6/11/2007","1:41pm",+0.07,30.05,30.25,29.93,24255316
|
||||
"PFE",26.491,"6/11/2007","1:36pm",-0.029,26.50,26.54,26.31,15109927
|
||||
"PG",63.12,"6/11/2007","1:36pm",+0.05,62.80,63.13,62.75,4002446
|
||||
"T",40.30,"6/11/2007","1:36pm",+0.04,40.20,40.33,39.89,7712400
|
||||
"UTX",70.10,"6/11/2007","1:36pm",-0.13,69.85,70.20,69.51,1247400
|
||||
"VZ",43.40,"6/11/2007","1:36pm",+0.33,42.95,43.45,42.88,5341720
|
||||
"WMT",49.95,"6/11/2007","1:36pm",-0.13,49.90,50.12,49.55,6914407
|
||||
"XOM",83.56,"6/11/2007","1:36pm",+0.88,82.68,83.72,82.35,6917800
|
||||
"AA",39.53,"6/11/2007","1:41pm",-0.13,39.67,40.18,39.43,2647380
|
||||
"AIG",71.87,"6/11/2007","1:41pm",+0.34,71.29,71.90,71.15,2686796
|
||||
"AXP",63.28,"6/11/2007","1:41pm",+0.24,62.79,63.32,62.42,2205330
|
||||
"BA",97.82,"6/11/2007","1:41pm",-0.37,98.25,98.79,97.59,1725200
|
||||
"C",53.67,"6/11/2007","1:41pm",+0.34,53.20,53.77,52.81,6740594
|
||||
"CAT",79.11,"6/11/2007","1:41pm",+0.59,78.32,79.46,78.06,1880652
|
||||
"DD",50.861,"6/11/2007","1:41pm",-0.269,51.13,51.21,50.59,2199797
|
||||
"DIS",34.21,"6/11/2007","1:41pm",+0.01,34.28,34.44,34.12,3734350
|
||||
"GE",37.52,"6/11/2007","1:41pm",+0.20,37.07,37.56,37.05,13566001
|
||||
"GM",31.47,"6/11/2007","1:41pm",+0.47,31.00,31.62,30.90,8330251
|
||||
"HD",37.75,"6/11/2007","1:41pm",-0.20,37.78,37.83,37.62,6627167
|
||||
"HON",57.17,"6/11/2007","1:41pm",-0.21,57.25,57.40,56.91,2246142
|
||||
"HPQ",46.14,"6/11/2007","1:41pm",+0.44,45.80,46.29,45.46,6190049
|
||||
"IBM",103.54,"6/11/2007","1:41pm",+0.47,102.87,104.00,102.50,2715404
|
||||
"INTC",22.02,"6/11/2007","1:46pm",+0.19,21.70,22.08,21.69,25285316
|
||||
"JNJ",62.54,"6/11/2007","1:41pm",+0.41,62.89,62.89,62.15,5098378
|
||||
"JPM",50.77,"6/11/2007","1:41pm",+0.36,50.41,50.84,50.05,5025900
|
||||
"KO",51.725,"6/11/2007","1:41pm",+0.055,51.67,51.85,51.32,6390552
|
||||
"MCD",51.391,"6/11/2007","1:41pm",-0.019,51.47,51.62,50.98,2972614
|
||||
"MMM",85.58,"6/11/2007","1:41pm",-0.36,85.94,85.98,85.28,1337300
|
||||
"MO",70.29,"6/11/2007","1:41pm",-0.01,70.25,70.50,69.76,4907285
|
||||
"MRK",51.21,"6/11/2007","1:41pm",+1.07,50.30,51.28,50.04,6587800
|
||||
"MSFT",30.13,"6/11/2007","1:46pm",+0.08,30.05,30.25,29.93,24497374
|
||||
"PFE",26.49,"6/11/2007","1:41pm",-0.03,26.50,26.54,26.31,15538627
|
||||
"PG",63.13,"6/11/2007","1:41pm",+0.06,62.80,63.14,62.75,4048446
|
||||
"T",40.31,"6/11/2007","1:41pm",+0.05,40.20,40.34,39.89,7832500
|
||||
"UTX",70.07,"6/11/2007","1:41pm",-0.16,69.85,70.20,69.51,1264700
|
||||
"VZ",43.38,"6/11/2007","1:41pm",+0.31,42.95,43.45,42.88,5395220
|
||||
"WMT",49.90,"6/11/2007","1:41pm",-0.18,49.90,50.12,49.55,7009107
|
||||
"XOM",83.53,"6/11/2007","1:41pm",+0.85,82.68,83.72,82.35,7007700
|
||||
"AA",39.55,"6/11/2007","1:46pm",-0.11,39.67,40.18,39.43,2672280
|
||||
"AIG",71.88,"6/11/2007","1:46pm",+0.35,71.29,71.90,71.15,2724196
|
||||
"AXP",63.28,"6/11/2007","1:46pm",+0.24,62.79,63.32,62.42,2214330
|
||||
"BA",97.98,"6/11/2007","1:46pm",-0.21,98.25,98.79,97.59,1773500
|
||||
"C",53.61,"6/11/2007","1:46pm",+0.28,53.20,53.77,52.81,6867594
|
||||
"CAT",79.07,"6/11/2007","1:46pm",+0.55,78.32,79.46,78.06,1923652
|
||||
"DD",50.86,"6/11/2007","1:46pm",-0.27,51.13,51.21,50.59,2221297
|
||||
"DIS",34.201,"6/11/2007","1:46pm",+0.001,34.28,34.44,34.12,3795650
|
||||
"GE",37.52,"6/11/2007","1:46pm",+0.20,37.07,37.56,37.05,13711301
|
||||
"GM",31.50,"6/11/2007","1:46pm",+0.50,31.00,31.62,30.90,8678851
|
||||
"HD",37.77,"6/11/2007","1:46pm",-0.18,37.78,37.83,37.62,6687867
|
||||
"HON",57.162,"6/11/2007","1:46pm",-0.218,57.25,57.40,56.91,2293142
|
||||
"HPQ",46.13,"6/11/2007","1:46pm",+0.43,45.80,46.29,45.46,6259749
|
||||
"IBM",103.57,"6/11/2007","1:46pm",+0.50,102.87,104.00,102.50,2740104
|
||||
"INTC",22.02,"6/11/2007","1:51pm",+0.19,21.70,22.08,21.69,25632092
|
||||
"JNJ",62.54,"6/11/2007","1:46pm",+0.41,62.89,62.89,62.15,5147978
|
||||
"JPM",50.80,"6/11/2007","1:46pm",+0.39,50.41,50.84,50.05,5114400
|
||||
"KO",51.75,"6/11/2007","1:46pm",+0.08,51.67,51.85,51.32,6441352
|
||||
"MCD",51.40,"6/11/2007","1:46pm",-0.01,51.47,51.62,50.98,3049814
|
||||
"MMM",85.59,"6/11/2007","1:46pm",-0.35,85.94,85.98,85.28,1347600
|
||||
"MO",70.28,"6/11/2007","1:46pm",-0.02,70.25,70.50,69.76,4959585
|
||||
"MRK",51.18,"6/11/2007","1:46pm",+1.04,50.30,51.28,50.04,6651700
|
||||
"MSFT",30.11,"6/11/2007","1:51pm",+0.06,30.05,30.25,29.93,24742128
|
||||
"PFE",26.44,"6/11/2007","1:46pm",-0.08,26.50,26.54,26.31,15813727
|
||||
"PG",63.12,"6/11/2007","1:46pm",+0.05,62.80,63.14,62.75,4099546
|
||||
"T",40.3073,"6/11/2007","1:46pm",+0.0473,40.20,40.34,39.89,7901800
|
||||
"UTX",70.10,"6/11/2007","1:46pm",-0.13,69.85,70.20,69.51,1284300
|
||||
"VZ",43.42,"6/11/2007","1:46pm",+0.35,42.95,43.45,42.88,5473215
|
||||
"WMT",49.94,"6/11/2007","1:46pm",-0.14,49.90,50.12,49.55,7155307
|
||||
"XOM",83.66,"6/11/2007","1:46pm",+0.98,82.68,83.72,82.35,7157900
|
||||
"AA",39.55,"6/11/2007","1:51pm",-0.11,39.67,40.18,39.43,2705680
|
||||
"AIG",71.84,"6/11/2007","1:51pm",+0.31,71.29,71.90,71.15,2760896
|
||||
"AXP",63.245,"6/11/2007","1:51pm",+0.205,62.79,63.32,62.42,2226930
|
||||
"BA",97.96,"6/11/2007","1:51pm",-0.23,98.25,98.79,97.59,1793700
|
||||
"C",53.63,"6/11/2007","1:51pm",+0.30,53.20,53.77,52.81,6926294
|
||||
"CAT",79.04,"6/11/2007","1:51pm",+0.52,78.32,79.46,78.06,1958652
|
||||
"DD",50.83,"6/11/2007","1:51pm",-0.30,51.13,51.21,50.59,2232897
|
||||
"DIS",34.20,"6/11/2007","1:50pm",0.00,34.28,34.44,34.12,3823150
|
||||
"GE",37.52,"6/11/2007","1:51pm",+0.20,37.07,37.56,37.05,13797801
|
||||
"GM",31.47,"6/11/2007","1:51pm",+0.47,31.00,31.62,30.90,8801851
|
||||
"HD",37.77,"6/11/2007","1:51pm",-0.18,37.78,37.83,37.62,6761167
|
||||
"HON",57.13,"6/11/2007","1:51pm",-0.25,57.25,57.40,56.91,2327542
|
||||
"HPQ",46.12,"6/11/2007","1:51pm",+0.42,45.80,46.29,45.46,6316649
|
||||
"IBM",103.55,"6/11/2007","1:51pm",+0.48,102.87,104.00,102.50,2777904
|
||||
"INTC",22.03,"6/11/2007","1:56pm",+0.20,21.70,22.08,21.69,26065954
|
||||
"JNJ",62.53,"6/11/2007","1:51pm",+0.40,62.89,62.89,62.15,5210478
|
||||
"JPM",50.81,"6/11/2007","1:51pm",+0.40,50.41,50.84,50.05,5222100
|
||||
"KO",51.75,"6/11/2007","1:51pm",+0.08,51.67,51.85,51.32,6558652
|
||||
"MCD",51.39,"6/11/2007","1:51pm",-0.02,51.47,51.62,50.98,3074814
|
||||
"MMM",85.548,"6/11/2007","1:51pm",-0.392,85.94,85.98,85.28,1372000
|
||||
"MO",70.27,"6/11/2007","1:50pm",-0.03,70.25,70.50,69.76,4986985
|
||||
"MRK",51.16,"6/11/2007","1:51pm",+1.02,50.30,51.28,50.04,6821600
|
||||
"MSFT",30.13,"6/11/2007","1:56pm",+0.08,30.05,30.25,29.93,24970772
|
||||
"PFE",26.448,"6/11/2007","1:51pm",-0.072,26.50,26.54,26.31,16248877
|
||||
"PG",63.13,"6/11/2007","1:51pm",+0.06,62.80,63.14,62.75,4157646
|
||||
"T",40.31,"6/11/2007","1:51pm",+0.05,40.20,40.34,39.89,8018500
|
||||
"UTX",70.15,"6/11/2007","1:51pm",-0.08,69.85,70.20,69.51,1321400
|
||||
"VZ",43.44,"6/11/2007","1:51pm",+0.37,42.95,43.45,42.88,5538015
|
||||
"WMT",49.86,"6/11/2007","1:51pm",-0.22,49.90,50.12,49.55,7285407
|
||||
"XOM",83.56,"6/11/2007","1:51pm",+0.88,82.68,83.72,82.35,7364600
|
||||
"AA",39.53,"6/11/2007","1:56pm",-0.13,39.67,40.18,39.43,2721380
|
||||
"AIG",71.7918,"6/11/2007","1:56pm",+0.2618,71.29,71.90,71.15,2817696
|
||||
"AXP",63.20,"6/11/2007","1:56pm",+0.16,62.79,63.32,62.42,2237230
|
||||
"BA",97.89,"6/11/2007","1:56pm",-0.30,98.25,98.79,97.59,1808600
|
||||
"C",53.60,"6/11/2007","1:56pm",+0.27,53.20,53.77,52.81,7078394
|
||||
"CAT",79.02,"6/11/2007","1:56pm",+0.50,78.32,79.46,78.06,1980452
|
||||
"DD",50.83,"6/11/2007","1:56pm",-0.30,51.13,51.21,50.59,2245697
|
||||
"DIS",34.195,"6/11/2007","1:56pm",-0.005,34.28,34.44,34.12,3863950
|
||||
"GE",37.52,"6/11/2007","1:56pm",+0.20,37.07,37.56,37.05,13986001
|
||||
"GM",31.49,"6/11/2007","1:56pm",+0.49,31.00,31.62,30.90,8849851
|
||||
"HD",37.75,"6/11/2007","1:56pm",-0.20,37.78,37.83,37.62,6819267
|
||||
"HON",57.11,"6/11/2007","1:56pm",-0.27,57.25,57.40,56.91,2354542
|
||||
"HPQ",46.122,"6/11/2007","1:56pm",+0.422,45.80,46.29,45.46,6380249
|
||||
"IBM",103.53,"6/11/2007","1:56pm",+0.46,102.87,104.00,102.50,2802904
|
||||
"INTC",22.01,"6/11/2007","2:01pm",+0.18,21.70,22.08,21.69,26445260
|
||||
"JNJ",62.52,"6/11/2007","1:56pm",+0.39,62.89,62.89,62.15,5265678
|
||||
"JPM",50.77,"6/11/2007","1:56pm",+0.36,50.41,50.84,50.05,5291300
|
||||
"KO",51.7619,"6/11/2007","1:56pm",+0.0919,51.67,51.85,51.32,6606152
|
||||
"MCD",51.36,"6/11/2007","1:56pm",-0.05,51.47,51.62,50.98,3110114
|
||||
"MMM",85.53,"6/11/2007","1:56pm",-0.41,85.94,85.98,85.28,1402100
|
||||
"MO",70.27,"6/11/2007","1:56pm",-0.03,70.25,70.50,69.76,5016285
|
||||
"MRK",51.11,"6/11/2007","1:56pm",+0.97,50.30,51.28,50.04,6899000
|
||||
"MSFT",30.12,"6/11/2007","2:01pm",+0.07,30.05,30.25,29.93,25277544
|
||||
"PFE",26.44,"6/11/2007","1:56pm",-0.08,26.50,26.54,26.31,16455677
|
||||
"PG",63.13,"6/11/2007","1:56pm",+0.06,62.80,63.15,62.75,4256946
|
||||
"T",40.28,"6/11/2007","1:56pm",+0.02,40.20,40.34,39.89,8132800
|
||||
"UTX",70.154,"6/11/2007","1:56pm",-0.076,69.85,70.20,69.51,1344300
|
||||
"VZ",43.47,"6/11/2007","1:56pm",+0.40,42.95,43.47,42.88,5621815
|
||||
"WMT",49.88,"6/11/2007","1:56pm",-0.20,49.90,50.12,49.55,7420907
|
||||
"XOM",83.52,"6/11/2007","1:56pm",+0.84,82.68,83.72,82.35,7459300
|
||||
"AA",39.491,"6/11/2007","2:01pm",-0.169,39.67,40.18,39.43,2763280
|
||||
"AIG",71.77,"6/11/2007","2:01pm",+0.24,71.29,71.90,71.15,2879896
|
||||
"AXP",63.17,"6/11/2007","2:01pm",+0.13,62.79,63.32,62.42,2261630
|
||||
"BA",97.83,"6/11/2007","2:01pm",-0.36,98.25,98.79,97.59,1833100
|
||||
"C",53.55,"6/11/2007","2:01pm",+0.22,53.20,53.77,52.81,7307094
|
||||
"CAT",79.00,"6/11/2007","2:01pm",+0.48,78.32,79.46,78.06,2025552
|
||||
"DD",50.85,"6/11/2007","2:01pm",-0.28,51.13,51.21,50.59,2277197
|
||||
"DIS",34.20,"6/11/2007","2:01pm",0.00,34.28,34.44,34.12,3931450
|
||||
"GE",37.51,"6/11/2007","2:01pm",+0.19,37.07,37.56,37.05,14260601
|
||||
"GM",31.50,"6/11/2007","2:01pm",+0.50,31.00,31.62,30.90,8948751
|
||||
"HD",37.75,"6/11/2007","2:01pm",-0.20,37.78,37.83,37.62,7227367
|
||||
"HON",57.13,"6/11/2007","2:01pm",-0.25,57.25,57.40,56.91,2422142
|
||||
"HPQ",46.08,"6/11/2007","2:01pm",+0.38,45.80,46.29,45.46,6426249
|
||||
"IBM",103.51,"6/11/2007","2:01pm",+0.44,102.87,104.00,102.50,2839204
|
||||
"INTC",22.02,"6/11/2007","2:06pm",+0.19,21.70,22.08,21.69,26751104
|
||||
"JNJ",62.50,"6/11/2007","2:01pm",+0.37,62.89,62.89,62.15,5350858
|
||||
"JPM",50.70,"6/11/2007","2:01pm",+0.29,50.41,50.84,50.05,5396400
|
||||
"KO",51.76,"6/11/2007","2:01pm",+0.09,51.67,51.85,51.32,6660752
|
||||
"MCD",51.3727,"6/11/2007","2:01pm",-0.0373,51.47,51.62,50.98,3168414
|
||||
"MMM",85.58,"6/11/2007","2:01pm",-0.36,85.94,85.98,85.28,1440600
|
||||
"MO",70.24,"6/11/2007","2:01pm",-0.06,70.25,70.50,69.76,5077085
|
||||
"MRK",51.09,"6/11/2007","2:01pm",+0.95,50.30,51.28,50.04,6995800
|
||||
"MSFT",30.12,"6/11/2007","2:06pm",+0.07,30.05,30.25,29.93,25466984
|
||||
"PFE",26.44,"6/11/2007","2:01pm",-0.08,26.50,26.54,26.31,16750477
|
||||
"PG",63.12,"6/11/2007","2:01pm",+0.05,62.80,63.15,62.75,4334646
|
||||
"T",40.27,"6/11/2007","2:01pm",+0.01,40.20,40.34,39.89,8457200
|
||||
"UTX",70.11,"6/11/2007","2:01pm",-0.12,69.85,70.20,69.51,1362600
|
||||
"VZ",43.46,"6/11/2007","2:01pm",+0.39,42.95,43.47,42.88,5698015
|
||||
"WMT",49.79,"6/11/2007","2:01pm",-0.29,49.90,50.12,49.55,7569507
|
||||
"XOM",83.43,"6/11/2007","2:01pm",+0.75,82.68,83.72,82.35,7716900
|
||||
"AA",39.50,"6/11/2007","2:06pm",-0.16,39.67,40.18,39.43,2841280
|
||||
"AIG",71.85,"6/11/2007","2:06pm",+0.32,71.29,71.90,71.15,2945096
|
||||
"AXP",63.23,"6/11/2007","2:06pm",+0.19,62.79,63.32,62.42,2280130
|
||||
"BA",97.92,"6/11/2007","2:06pm",-0.27,98.25,98.79,97.59,1847000
|
||||
"C",53.56,"6/11/2007","2:06pm",+0.23,53.20,53.77,52.81,7466394
|
||||
"CAT",79.11,"6/11/2007","2:06pm",+0.59,78.32,79.46,78.06,2048252
|
||||
"DD",50.86,"6/11/2007","2:06pm",-0.27,51.13,51.21,50.59,2309597
|
||||
"DIS",34.19,"6/11/2007","2:06pm",-0.01,34.28,34.44,34.12,3986950
|
||||
"GE",37.51,"6/11/2007","2:06pm",+0.19,37.07,37.56,37.05,14444801
|
||||
"GM",31.50,"6/11/2007","2:06pm",+0.50,31.00,31.62,30.90,9108451
|
||||
"HD",37.75,"6/11/2007","2:06pm",-0.20,37.78,37.83,37.62,7407867
|
||||
"HON",57.11,"6/11/2007","2:06pm",-0.27,57.25,57.40,56.91,2462942
|
||||
"HPQ",46.08,"6/11/2007","2:06pm",+0.38,45.80,46.29,45.46,6509849
|
||||
"IBM",103.54,"6/11/2007","2:06pm",+0.47,102.87,104.00,102.50,2861904
|
||||
"INTC",22.03,"6/11/2007","2:11pm",+0.20,21.70,22.08,21.69,26974348
|
||||
"JNJ",62.50,"6/11/2007","2:06pm",+0.37,62.89,62.89,62.15,5513358
|
||||
"JPM",50.65,"6/11/2007","2:06pm",+0.24,50.41,50.84,50.05,5565600
|
||||
"KO",51.77,"6/11/2007","2:06pm",+0.10,51.67,51.85,51.32,6687652
|
||||
"MCD",51.41,"6/11/2007","2:06pm",0.00,51.47,51.62,50.98,3209614
|
||||
"MMM",85.53,"6/11/2007","2:06pm",-0.41,85.94,85.98,85.28,1470200
|
||||
"MO",70.29,"6/11/2007","2:06pm",-0.01,70.25,70.50,69.76,5107185
|
||||
"MRK",51.05,"6/11/2007","2:06pm",+0.91,50.30,51.28,50.04,7162100
|
||||
"MSFT",30.13,"6/11/2007","2:11pm",+0.08,30.05,30.25,29.93,25965886
|
||||
"PFE",26.44,"6/11/2007","2:06pm",-0.08,26.50,26.54,26.31,17179996
|
||||
"PG",63.14,"6/11/2007","2:06pm",+0.07,62.80,63.15,62.75,4427046
|
||||
"T",40.26,"6/11/2007","2:06pm",0.00,40.20,40.34,39.89,8620800
|
||||
"UTX",70.12,"6/11/2007","2:06pm",-0.11,69.85,70.20,69.51,1386800
|
||||
"VZ",43.44,"6/11/2007","2:06pm",+0.37,42.95,43.47,42.88,5829840
|
||||
"WMT",49.83,"6/11/2007","2:06pm",-0.25,49.90,50.12,49.55,7695307
|
||||
"XOM",83.44,"6/11/2007","2:06pm",+0.76,82.68,83.72,82.35,7916100
|
||||
"AA",39.53,"6/11/2007","2:11pm",-0.13,39.67,40.18,39.43,2880880
|
||||
"AIG",71.98,"6/11/2007","2:11pm",+0.45,71.29,71.99,71.15,3103496
|
||||
"AXP",63.38,"6/11/2007","2:11pm",+0.34,62.79,63.39,62.42,2308630
|
||||
"BA",97.93,"6/11/2007","2:11pm",-0.26,98.25,98.79,97.59,1881100
|
||||
"C",53.72,"6/11/2007","2:11pm",+0.39,53.20,53.77,52.81,7588294
|
||||
"CAT",79.19,"6/11/2007","2:11pm",+0.67,78.32,79.46,78.06,2131352
|
||||
"DD",50.90,"6/11/2007","2:11pm",-0.23,51.13,51.21,50.59,2340097
|
||||
"DIS",34.21,"6/11/2007","2:11pm",+0.01,34.28,34.44,34.12,4058750
|
||||
"GE",37.57,"6/11/2007","2:11pm",+0.25,37.07,37.57,37.05,14734801
|
||||
"GM",31.57,"6/11/2007","2:11pm",+0.57,31.00,31.62,30.90,9594251
|
||||
"HD",37.76,"6/11/2007","2:11pm",-0.19,37.78,37.83,37.62,7474967
|
||||
"HON",57.19,"6/11/2007","2:11pm",-0.19,57.25,57.40,56.91,2592542
|
||||
"HPQ",46.15,"6/11/2007","2:11pm",+0.45,45.80,46.29,45.46,6633249
|
||||
"IBM",103.59,"6/11/2007","2:11pm",+0.52,102.87,104.00,102.50,2884504
|
||||
"INTC",22.01,"6/11/2007","2:16pm",+0.18,21.70,22.08,21.69,27482520
|
||||
"JNJ",62.53,"6/11/2007","2:11pm",+0.40,62.89,62.89,62.15,5618158
|
||||
"JPM",50.74,"6/11/2007","2:11pm",+0.33,50.41,50.84,50.05,5728500
|
||||
"KO",51.79,"6/11/2007","2:11pm",+0.12,51.67,51.85,51.32,6761552
|
||||
"MCD",51.50,"6/11/2007","2:11pm",+0.09,51.47,51.62,50.98,3260114
|
||||
"MMM",85.57,"6/11/2007","2:11pm",-0.37,85.94,85.98,85.28,1508200
|
||||
"MO",70.34,"6/11/2007","2:11pm",+0.04,70.25,70.50,69.76,5156485
|
||||
"MRK",51.21,"6/11/2007","2:11pm",+1.07,50.30,51.28,50.04,7355400
|
||||
"MSFT",30.16,"6/11/2007","2:16pm",+0.11,30.05,30.25,29.93,26411778
|
||||
"PFE",26.48,"6/11/2007","2:11pm",-0.04,26.50,26.54,26.31,17302396
|
||||
"PG",63.1819,"6/11/2007","2:11pm",+0.1119,62.80,63.19,62.75,4533846
|
||||
"T",40.25,"6/11/2007","2:11pm",-0.01,40.20,40.34,39.89,8839900
|
||||
"UTX",70.20,"6/11/2007","2:11pm",-0.03,69.85,70.20,69.51,1410200
|
||||
"VZ",43.49,"6/11/2007","2:11pm",+0.42,42.95,43.49,42.88,5904936
|
||||
"WMT",49.882,"6/11/2007","2:11pm",-0.198,49.90,50.12,49.55,7831607
|
||||
"XOM",83.51,"6/11/2007","2:11pm",+0.83,82.68,83.72,82.35,8147200
|
||||
"AA",39.49,"6/11/2007","2:16pm",-0.17,39.67,40.18,39.43,2953380
|
||||
"AIG",71.90,"6/11/2007","2:16pm",+0.37,71.29,72.03,71.15,3208796
|
||||
"AXP",63.31,"6/11/2007","2:16pm",+0.27,62.79,63.42,62.42,2337730
|
||||
"BA",97.92,"6/11/2007","2:16pm",-0.27,98.25,98.79,97.59,1921100
|
||||
"C",53.65,"6/11/2007","2:16pm",+0.32,53.20,53.77,52.81,7685194
|
||||
"CAT",79.08,"6/11/2007","2:16pm",+0.56,78.32,79.46,78.06,2163952
|
||||
"DD",50.86,"6/11/2007","2:16pm",-0.27,51.13,51.21,50.59,2355597
|
||||
"DIS",34.20,"6/11/2007","2:16pm",0.00,34.28,34.44,34.12,4227650
|
||||
"GE",37.54,"6/11/2007","2:16pm",+0.22,37.07,37.61,37.05,15329801
|
||||
"GM",31.63,"6/11/2007","2:16pm",+0.63,31.00,31.64,30.90,9900251
|
||||
"HD",37.75,"6/11/2007","2:16pm",-0.20,37.78,37.83,37.62,7614667
|
||||
"HON",57.16,"6/11/2007","2:16pm",-0.22,57.25,57.40,56.91,2626742
|
||||
"HPQ",46.14,"6/11/2007","2:16pm",+0.44,45.80,46.29,45.46,6705149
|
||||
"IBM",103.48,"6/11/2007","2:16pm",+0.41,102.87,104.00,102.50,2932004
|
||||
"INTC",22.01,"6/11/2007","2:21pm",+0.18,21.70,22.08,21.69,27617028
|
||||
"JNJ",62.49,"6/11/2007","2:16pm",+0.36,62.89,62.89,62.15,5748158
|
||||
"JPM",50.71,"6/11/2007","2:16pm",+0.30,50.41,50.84,50.05,5968400
|
||||
"KO",51.75,"6/11/2007","2:16pm",+0.08,51.67,51.85,51.32,6798052
|
||||
"MCD",51.40,"6/11/2007","2:16pm",-0.01,51.47,51.62,50.98,3310514
|
||||
"MMM",85.53,"6/11/2007","2:16pm",-0.41,85.94,85.98,85.28,1531600
|
||||
"MO",70.33,"6/11/2007","2:16pm",+0.03,70.25,70.50,69.76,5204085
|
||||
"MRK",51.17,"6/11/2007","2:16pm",+1.03,50.30,51.28,50.04,7421000
|
||||
"MSFT",30.18,"6/11/2007","2:21pm",+0.13,30.05,30.25,29.93,26830224
|
||||
"PFE",26.46,"6/11/2007","2:16pm",-0.06,26.50,26.54,26.31,17549496
|
||||
"PG",63.18,"6/11/2007","2:16pm",+0.11,62.80,63.20,62.75,4586346
|
||||
"T",40.30,"6/11/2007","2:16pm",+0.04,40.20,40.34,39.89,9022910
|
||||
"UTX",70.192,"6/11/2007","2:16pm",-0.038,69.85,70.25,69.51,1444200
|
||||
"VZ",43.52,"6/11/2007","2:16pm",+0.45,42.95,43.535,42.88,6098036
|
||||
"WMT",49.85,"6/11/2007","2:16pm",-0.23,49.90,50.12,49.55,7900607
|
||||
"XOM",83.37,"6/11/2007","2:16pm",+0.69,82.68,83.85,82.35,8845100
|
||||
"AA",39.46,"6/11/2007","2:21pm",-0.20,39.67,40.18,39.43,3029880
|
||||
"AIG",71.92,"6/11/2007","2:21pm",+0.39,71.29,72.03,71.15,3245196
|
||||
"AXP",63.35,"6/11/2007","2:21pm",+0.31,62.79,63.42,62.42,2353430
|
||||
"BA",97.97,"6/11/2007","2:21pm",-0.22,98.25,98.79,97.59,1944500
|
||||
"C",53.69,"6/11/2007","2:21pm",+0.36,53.20,53.77,52.81,7757194
|
||||
"CAT",79.12,"6/11/2007","2:21pm",+0.60,78.32,79.46,78.06,2214452
|
||||
"DD",50.899,"6/11/2007","2:21pm",-0.231,51.13,51.21,50.59,2373297
|
||||
"DIS",34.23,"6/11/2007","2:21pm",+0.03,34.28,34.44,34.12,4262150
|
||||
"GE",37.52,"6/11/2007","2:21pm",+0.20,37.07,37.61,37.05,15596301
|
||||
"GM",31.68,"6/11/2007","2:21pm",+0.68,31.00,31.69,30.90,10286001
|
||||
"HD",37.7618,"6/11/2007","2:21pm",-0.1882,37.78,37.83,37.62,7739967
|
||||
"HON",57.28,"6/11/2007","2:21pm",-0.10,57.25,57.40,56.91,2699742
|
||||
"HPQ",46.16,"6/11/2007","2:21pm",+0.46,45.80,46.29,45.46,6773849
|
||||
"IBM",103.47,"6/11/2007","2:21pm",+0.40,102.87,104.00,102.50,2958304
|
||||
"INTC",22.008,"6/11/2007","2:26pm",+0.178,21.70,22.08,21.69,28003456
|
||||
"JNJ",62.53,"6/11/2007","2:21pm",+0.40,62.89,62.89,62.15,5814358
|
||||
"JPM",50.721,"6/11/2007","2:21pm",+0.311,50.41,50.84,50.05,6057700
|
||||
"KO",51.77,"6/11/2007","2:21pm",+0.10,51.67,51.85,51.32,6832552
|
||||
"MCD",51.37,"6/11/2007","2:21pm",-0.04,51.47,51.62,50.98,3514114
|
||||
"MMM",85.58,"6/11/2007","2:21pm",-0.36,85.94,85.98,85.28,1584600
|
||||
"MO",70.39,"6/11/2007","2:21pm",+0.09,70.25,70.50,69.76,5237185
|
||||
"MRK",51.25,"6/11/2007","2:21pm",+1.11,50.30,51.28,50.04,7541700
|
||||
"MSFT",30.18,"6/11/2007","2:26pm",+0.13,30.05,30.25,29.93,27263748
|
||||
"PFE",26.45,"6/11/2007","2:21pm",-0.07,26.50,26.54,26.31,17743896
|
||||
"PG",63.17,"6/11/2007","2:21pm",+0.10,62.80,63.20,62.75,4649246
|
||||
"T",40.36,"6/11/2007","2:21pm",+0.10,40.20,40.37,39.89,9204010
|
||||
"UTX",70.20,"6/11/2007","2:21pm",-0.03,69.85,70.25,69.51,1518400
|
||||
"VZ",43.57,"6/11/2007","2:21pm",+0.50,42.95,43.58,42.88,6405057
|
||||
"WMT",49.84,"6/11/2007","2:21pm",-0.24,49.90,50.12,49.55,7979707
|
||||
"XOM",83.46,"6/11/2007","2:21pm",+0.78,82.68,83.85,82.35,9131100
|
||||
"AA",39.46,"6/11/2007","2:26pm",-0.20,39.67,40.18,39.43,3106580
|
||||
"AIG",71.92,"6/11/2007","2:26pm",+0.39,71.29,72.03,71.15,3332596
|
||||
"AXP",63.34,"6/11/2007","2:26pm",+0.30,62.79,63.42,62.42,2372530
|
||||
"BA",97.92,"6/11/2007","2:26pm",-0.27,98.25,98.79,97.59,1972600
|
||||
"C",53.68,"6/11/2007","2:26pm",+0.35,53.20,53.77,52.81,7862194
|
||||
"CAT",79.06,"6/11/2007","2:26pm",+0.54,78.32,79.46,78.06,2621452
|
||||
"DD",50.85,"6/11/2007","2:26pm",-0.28,51.13,51.21,50.59,2400597
|
||||
"DIS",34.21,"6/11/2007","2:26pm",+0.01,34.28,34.44,34.12,4329650
|
||||
"GE",37.48,"6/11/2007","2:26pm",+0.16,37.07,37.61,37.05,15908401
|
||||
"GM",31.76,"6/11/2007","2:26pm",+0.76,31.00,31.77,30.90,10673201
|
||||
"HD",37.76,"6/11/2007","2:26pm",-0.19,37.78,37.83,37.62,7797367
|
||||
"HON",57.23,"6/11/2007","2:26pm",-0.15,57.25,57.40,56.91,2798642
|
||||
"HPQ",46.16,"6/11/2007","2:26pm",+0.46,45.80,46.29,45.46,6850349
|
||||
"IBM",103.52,"6/11/2007","2:26pm",+0.45,102.87,104.00,102.50,2994204
|
||||
"INTC",22.01,"6/11/2007","2:31pm",+0.18,21.70,22.08,21.69,28166784
|
||||
"JNJ",62.52,"6/11/2007","2:26pm",+0.39,62.89,62.89,62.15,5858958
|
||||
"JPM",50.72,"6/11/2007","2:26pm",+0.31,50.41,50.84,50.05,6141900
|
||||
"KO",51.80,"6/11/2007","2:26pm",+0.13,51.67,51.85,51.32,6873652
|
||||
"MCD",51.37,"6/11/2007","2:26pm",-0.04,51.47,51.62,50.98,3549714
|
||||
"MMM",85.55,"6/11/2007","2:26pm",-0.39,85.94,85.98,85.28,1616200
|
||||
"MO",70.3611,"6/11/2007","2:26pm",+0.0611,70.25,70.50,69.76,5288885
|
||||
"MRK",51.26,"6/11/2007","2:26pm",+1.12,50.30,51.28,50.04,7641900
|
||||
"MSFT",30.18,"6/11/2007","2:31pm",+0.13,30.05,30.25,29.93,27477918
|
||||
"PFE",26.45,"6/11/2007","2:26pm",-0.07,26.50,26.54,26.31,17889796
|
||||
"PG",63.13,"6/11/2007","2:26pm",+0.06,62.80,63.20,62.75,4759446
|
||||
"T",40.43,"6/11/2007","2:26pm",+0.17,40.20,40.47,39.89,9441710
|
||||
"UTX",70.15,"6/11/2007","2:26pm",-0.08,69.85,70.25,69.51,1552400
|
||||
"VZ",43.58,"6/11/2007","2:26pm",+0.51,42.95,43.61,42.88,6495057
|
||||
"WMT",49.82,"6/11/2007","2:26pm",-0.26,49.90,50.12,49.55,8014807
|
||||
"XOM",83.40,"6/11/2007","2:26pm",+0.72,82.68,83.85,82.35,9250000
|
||||
"AA",39.43,"6/11/2007","2:31pm",-0.23,39.67,40.18,39.42,3146680
|
||||
"AIG",71.88,"6/11/2007","2:31pm",+0.35,71.29,72.03,71.15,3377696
|
||||
"AXP",63.30,"6/11/2007","2:31pm",+0.26,62.79,63.42,62.42,2396630
|
||||
"BA",97.83,"6/11/2007","2:31pm",-0.36,98.25,98.79,97.59,1992300
|
||||
"C",53.64,"6/11/2007","2:31pm",+0.31,53.20,53.77,52.81,7963894
|
||||
"CAT",79.00,"6/11/2007","2:31pm",+0.48,78.32,79.46,78.06,2657252
|
||||
"DD",50.85,"6/11/2007","2:31pm",-0.28,51.13,51.21,50.59,2419197
|
||||
"DIS",34.20,"6/11/2007","2:31pm",0.00,34.28,34.44,34.12,4416950
|
||||
"GE",37.48,"6/11/2007","2:31pm",+0.16,37.07,37.61,37.05,16180901
|
||||
"GM",31.74,"6/11/2007","2:31pm",+0.74,31.00,31.77,30.90,10859101
|
||||
"HD",37.76,"6/11/2007","2:31pm",-0.19,37.78,37.83,37.62,8074667
|
||||
"HON",57.20,"6/11/2007","2:31pm",-0.18,57.25,57.40,56.91,2836642
|
||||
"HPQ",46.15,"6/11/2007","2:31pm",+0.45,45.80,46.29,45.46,6906449
|
||||
"IBM",103.46,"6/11/2007","2:31pm",+0.39,102.87,104.00,102.50,3014104
|
||||
"INTC",22.01,"6/11/2007","2:36pm",+0.18,21.70,22.08,21.69,28623660
|
||||
"JNJ",62.48,"6/11/2007","2:31pm",+0.35,62.89,62.89,62.15,5916458
|
||||
"JPM",50.68,"6/11/2007","2:31pm",+0.27,50.41,50.84,50.05,6212400
|
||||
"KO",51.75,"6/11/2007","2:31pm",+0.08,51.67,51.85,51.32,6921352
|
||||
"MCD",51.31,"6/11/2007","2:31pm",-0.10,51.47,51.62,50.98,3570814
|
||||
"MMM",85.56,"6/11/2007","2:31pm",-0.38,85.94,85.98,85.28,1632000
|
||||
"MO",70.34,"6/11/2007","2:31pm",+0.04,70.25,70.50,69.76,5316285
|
||||
"MRK",51.25,"6/11/2007","2:31pm",+1.11,50.30,51.35,50.04,7816300
|
||||
"MSFT",30.18,"6/11/2007","2:36pm",+0.13,30.05,30.25,29.93,27776860
|
||||
"PFE",26.43,"6/11/2007","2:31pm",-0.09,26.50,26.54,26.31,18072696
|
||||
"PG",63.09,"6/11/2007","2:31pm",+0.02,62.80,63.20,62.75,4849346
|
||||
"T",40.37,"6/11/2007","2:31pm",+0.11,40.20,40.47,39.89,9632910
|
||||
"UTX",70.15,"6/11/2007","2:31pm",-0.08,69.85,70.25,69.51,1566500
|
||||
"VZ",43.53,"6/11/2007","2:31pm",+0.46,42.95,43.61,42.88,6607457
|
||||
"WMT",49.77,"6/11/2007","2:31pm",-0.31,49.90,50.12,49.55,8080507
|
||||
"XOM",83.35,"6/11/2007","2:31pm",+0.67,82.68,83.85,82.35,9365400
|
||||
"AA",39.39,"6/11/2007","2:36pm",-0.27,39.67,40.18,39.38,3233280
|
||||
"AIG",71.87,"6/11/2007","2:36pm",+0.34,71.29,72.03,71.15,3455896
|
||||
"AXP",63.22,"6/11/2007","2:36pm",+0.18,62.79,63.42,62.42,2427730
|
||||
"BA",97.77,"6/11/2007","2:36pm",-0.42,98.25,98.79,97.59,2005500
|
||||
"C",53.63,"6/11/2007","2:36pm",+0.30,53.20,53.77,52.81,8024794
|
||||
"CAT",79.01,"6/11/2007","2:36pm",+0.49,78.32,79.46,78.06,2680952
|
||||
"DD",50.83,"6/11/2007","2:36pm",-0.30,51.13,51.21,50.59,2435997
|
||||
"DIS",34.19,"6/11/2007","2:36pm",-0.01,34.28,34.44,34.12,4466950
|
||||
"GE",37.44,"6/11/2007","2:36pm",+0.12,37.07,37.61,37.05,16472401
|
||||
"GM",31.70,"6/11/2007","2:36pm",+0.70,31.00,31.79,30.90,11121251
|
||||
"HD",37.75,"6/11/2007","2:36pm",-0.20,37.78,37.83,37.62,8123767
|
||||
"HON",57.16,"6/11/2007","2:36pm",-0.22,57.25,57.40,56.91,2882942
|
||||
"HPQ",46.15,"6/11/2007","2:36pm",+0.45,45.80,46.29,45.46,6971049
|
||||
"IBM",103.42,"6/11/2007","2:36pm",+0.35,102.87,104.00,102.50,3050204
|
||||
"INTC",22.0203,"6/11/2007","2:41pm",+0.1903,21.70,22.08,21.69,28975326
|
||||
"JNJ",62.48,"6/11/2007","2:36pm",+0.35,62.89,62.89,62.15,5985258
|
||||
"JPM",50.67,"6/11/2007","2:36pm",+0.26,50.41,50.84,50.05,6275300
|
||||
"KO",51.75,"6/11/2007","2:36pm",+0.08,51.67,51.85,51.32,7031052
|
||||
"MCD",51.33,"6/11/2007","2:36pm",-0.08,51.47,51.62,50.98,3605014
|
||||
"MMM",85.47,"6/11/2007","2:36pm",-0.47,85.94,85.98,85.28,1656600
|
||||
"MO",70.33,"6/11/2007","2:36pm",+0.03,70.25,70.50,69.76,5350885
|
||||
"MRK",51.20,"6/11/2007","2:36pm",+1.06,50.30,51.35,50.04,7984900
|
||||
"MSFT",30.18,"6/11/2007","2:41pm",+0.13,30.05,30.25,29.93,30696744
|
||||
"PFE",26.44,"6/11/2007","2:36pm",-0.08,26.50,26.54,26.31,18273796
|
||||
"PG",63.09,"6/11/2007","2:36pm",+0.02,62.80,63.20,62.75,4902446
|
||||
"T",40.29,"6/11/2007","2:36pm",+0.03,40.20,40.47,39.89,9901810
|
||||
"UTX",70.11,"6/11/2007","2:36pm",-0.12,69.85,70.25,69.51,1589000
|
||||
"VZ",43.50,"6/11/2007","2:36pm",+0.43,42.95,43.61,42.88,6714857
|
||||
"WMT",49.76,"6/11/2007","2:36pm",-0.32,49.90,50.12,49.55,8135107
|
||||
"XOM",83.33,"6/11/2007","2:36pm",+0.65,82.68,83.85,82.35,9480100
|
||||
"AA",39.39,"6/11/2007","2:41pm",-0.27,39.67,40.18,39.34,3314380
|
||||
"AIG",71.89,"6/11/2007","2:41pm",+0.36,71.29,72.03,71.15,3656596
|
||||
"AXP",63.23,"6/11/2007","2:41pm",+0.19,62.79,63.42,62.42,2445430
|
||||
"BA",97.74,"6/11/2007","2:41pm",-0.45,98.25,98.79,97.59,2031200
|
||||
"C",53.68,"6/11/2007","2:41pm",+0.35,53.20,53.77,52.81,8087994
|
||||
"CAT",79.04,"6/11/2007","2:41pm",+0.52,78.32,79.46,78.06,2712552
|
||||
"DD",50.81,"6/11/2007","2:41pm",-0.32,51.13,51.21,50.59,2447897
|
||||
"DIS",34.19,"6/11/2007","2:41pm",-0.01,34.28,34.44,34.12,4526150
|
||||
"GE",37.47,"6/11/2007","2:41pm",+0.15,37.07,37.61,37.05,16819600
|
||||
"GM",31.65,"6/11/2007","2:41pm",+0.65,31.00,31.79,30.90,11306951
|
||||
"HD",37.76,"6/11/2007","2:41pm",-0.19,37.78,37.83,37.62,8189567
|
||||
"HON",57.13,"6/11/2007","2:41pm",-0.25,57.25,57.40,56.91,2913842
|
||||
"HPQ",46.13,"6/11/2007","2:41pm",+0.43,45.80,46.29,45.46,7057949
|
||||
"IBM",103.44,"6/11/2007","2:41pm",+0.37,102.87,104.00,102.50,3087004
|
||||
"INTC",22.02,"6/11/2007","2:46pm",+0.19,21.70,22.08,21.69,29252326
|
||||
"JNJ",62.48,"6/11/2007","2:41pm",+0.35,62.89,62.89,62.15,6046058
|
||||
"JPM",50.71,"6/11/2007","2:41pm",+0.30,50.41,50.84,50.05,6338800
|
||||
"KO",51.76,"6/11/2007","2:41pm",+0.09,51.67,51.85,51.32,7064552
|
||||
"MCD",51.29,"6/11/2007","2:41pm",-0.12,51.47,51.62,50.98,3648414
|
||||
"MMM",85.50,"6/11/2007","2:41pm",-0.44,85.94,85.98,85.28,1687900
|
||||
"MO",70.34,"6/11/2007","2:41pm",+0.04,70.25,70.50,69.76,5406985
|
||||
"MRK",51.18,"6/11/2007","2:41pm",+1.04,50.30,51.35,50.04,8103700
|
||||
"MSFT",30.22,"6/11/2007","2:46pm",+0.17,30.05,30.25,29.93,31209364
|
||||
"PFE",26.43,"6/11/2007","2:41pm",-0.09,26.50,26.54,26.31,18438496
|
||||
"PG",63.11,"6/11/2007","2:41pm",+0.04,62.80,63.20,62.75,4959446
|
||||
"T",40.27,"6/11/2007","2:41pm",+0.01,40.20,40.47,39.89,10050710
|
||||
"UTX",70.11,"6/11/2007","2:41pm",-0.12,69.85,70.25,69.51,1626700
|
||||
"VZ",43.50,"6/11/2007","2:41pm",+0.43,42.95,43.61,42.88,6863457
|
||||
"WMT",49.79,"6/11/2007","2:41pm",-0.29,49.90,50.12,49.55,8245307
|
||||
"XOM",83.37,"6/11/2007","2:41pm",+0.69,82.68,83.85,82.35,9563400
|
||||
"AA",39.33,"6/11/2007","2:46pm",-0.33,39.67,40.18,39.30,3399080
|
||||
"AIG",71.86,"6/11/2007","2:46pm",+0.33,71.29,72.03,71.15,3740596
|
||||
"AXP",63.20,"6/11/2007","2:46pm",+0.16,62.79,63.42,62.42,2463030
|
||||
"BA",97.78,"6/11/2007","2:46pm",-0.41,98.25,98.79,97.59,2065200
|
||||
"C",53.67,"6/11/2007","2:46pm",+0.34,53.20,53.77,52.81,8200694
|
||||
"CAT",79.00,"6/11/2007","2:46pm",+0.48,78.32,79.46,78.06,2725452
|
||||
"DD",50.80,"6/11/2007","2:46pm",-0.33,51.13,51.21,50.59,2457797
|
||||
"DIS",34.20,"6/11/2007","2:46pm",0.00,34.28,34.44,34.12,4601150
|
||||
"GE",37.48,"6/11/2007","2:46pm",+0.16,37.07,37.61,37.05,17056300
|
||||
"GM",31.65,"6/11/2007","2:46pm",+0.65,31.00,31.79,30.90,11359851
|
||||
"HD",37.75,"6/11/2007","2:46pm",-0.20,37.78,37.83,37.62,8424967
|
||||
"HON",57.18,"6/11/2007","2:46pm",-0.20,57.25,57.40,56.91,2949842
|
||||
"HPQ",46.13,"6/11/2007","2:46pm",+0.43,45.80,46.29,45.46,7195349
|
||||
"IBM",103.49,"6/11/2007","2:46pm",+0.42,102.87,104.00,102.50,3126604
|
||||
"INTC",22.02,"6/11/2007","2:51pm",+0.19,21.70,22.08,21.69,29404310
|
||||
"JNJ",62.485,"6/11/2007","2:46pm",+0.355,62.89,62.89,62.15,6102358
|
||||
"JPM",50.70,"6/11/2007","2:46pm",+0.29,50.41,50.84,50.05,6395400
|
||||
"KO",51.73,"6/11/2007","2:46pm",+0.06,51.67,51.85,51.32,7104652
|
||||
"MCD",51.32,"6/11/2007","2:46pm",-0.09,51.47,51.62,50.98,3708627
|
||||
"MMM",85.50,"6/11/2007","2:46pm",-0.44,85.94,85.98,85.28,1710200
|
||||
"MO",70.28,"6/11/2007","2:46pm",-0.02,70.25,70.50,69.76,5450985
|
||||
"MRK",51.19,"6/11/2007","2:46pm",+1.05,50.30,51.35,50.04,8188500
|
||||
"MSFT",30.16,"6/11/2007","2:51pm",+0.11,30.05,30.25,29.93,31914788
|
||||
"PFE",26.435,"6/11/2007","2:46pm",-0.085,26.50,26.54,26.31,18623136
|
||||
"PG",63.11,"6/11/2007","2:46pm",+0.04,62.80,63.20,62.75,5034546
|
||||
"T",40.29,"6/11/2007","2:46pm",+0.03,40.20,40.47,39.89,10311910
|
||||
"UTX",70.19,"6/11/2007","2:46pm",-0.04,69.85,70.25,69.51,1662900
|
||||
"VZ",43.52,"6/11/2007","2:46pm",+0.45,42.95,43.61,42.88,6914257
|
||||
"WMT",49.76,"6/11/2007","2:46pm",-0.32,49.90,50.12,49.55,8356278
|
||||
"XOM",83.30,"6/11/2007","2:46pm",+0.62,82.68,83.85,82.35,9689200
|
||||
"AA",39.33,"6/11/2007","2:51pm",-0.33,39.67,40.18,39.30,3443580
|
||||
"AIG",71.85,"6/11/2007","2:51pm",+0.32,71.29,72.03,71.15,3833496
|
||||
"AXP",63.26,"6/11/2007","2:51pm",+0.22,62.79,63.42,62.42,2483430
|
||||
"BA",97.66,"6/11/2007","2:51pm",-0.53,98.25,98.79,97.59,2085200
|
||||
"C",53.68,"6/11/2007","2:51pm",+0.35,53.20,53.77,52.81,8317394
|
||||
"CAT",78.99,"6/11/2007","2:51pm",+0.47,78.32,79.46,78.06,2746952
|
||||
"DD",50.80,"6/11/2007","2:51pm",-0.33,51.13,51.21,50.59,2480397
|
||||
"DIS",34.18,"6/11/2007","2:51pm",-0.02,34.28,34.44,34.12,4641650
|
||||
"GE",37.48,"6/11/2007","2:51pm",+0.16,37.07,37.61,37.05,17252900
|
||||
"GM",31.62,"6/11/2007","2:51pm",+0.62,31.00,31.79,30.90,11456251
|
||||
"HD",37.741,"6/11/2007","2:51pm",-0.209,37.78,37.83,37.62,8530067
|
||||
"HON",57.17,"6/11/2007","2:51pm",-0.21,57.25,57.40,56.91,2989342
|
||||
"HPQ",46.08,"6/11/2007","2:51pm",+0.38,45.80,46.29,45.46,7295183
|
||||
"IBM",103.49,"6/11/2007","2:51pm",+0.42,102.87,104.00,102.50,3205604
|
||||
"INTC",22.03,"6/11/2007","2:56pm",+0.20,21.70,22.08,21.69,29768528
|
||||
"JNJ",62.45,"6/11/2007","2:51pm",+0.32,62.89,62.89,62.15,6158558
|
||||
"JPM",50.71,"6/11/2007","2:51pm",+0.30,50.41,50.84,50.05,6486400
|
||||
"KO",51.76,"6/11/2007","2:51pm",+0.09,51.67,51.85,51.32,7135652
|
||||
"MCD",51.30,"6/11/2007","2:51pm",-0.11,51.47,51.62,50.98,3729927
|
||||
"MMM",85.48,"6/11/2007","2:51pm",-0.46,85.94,85.98,85.28,1732900
|
||||
"MO",70.21,"6/11/2007","2:51pm",-0.09,70.25,70.50,69.76,5501285
|
||||
"MRK",51.09,"6/11/2007","2:51pm",+0.95,50.30,51.35,50.04,8318900
|
||||
"MSFT",30.16,"6/11/2007","2:56pm",+0.11,30.05,30.25,29.93,32355638
|
||||
"PFE",26.43,"6/11/2007","2:51pm",-0.09,26.50,26.54,26.31,18879636
|
||||
"PG",63.16,"6/11/2007","2:51pm",+0.09,62.80,63.20,62.75,5127246
|
||||
"T",40.21,"6/11/2007","2:51pm",-0.05,40.20,40.47,39.89,10570410
|
||||
"UTX",70.15,"6/11/2007","2:51pm",-0.08,69.85,70.25,69.51,1681200
|
||||
"VZ",43.49,"6/11/2007","2:51pm",+0.42,42.95,43.61,42.88,6963457
|
||||
"WMT",49.755,"6/11/2007","2:51pm",-0.325,49.90,50.12,49.55,8428278
|
||||
"XOM",83.24,"6/11/2007","2:51pm",+0.56,82.68,83.85,82.35,9787800
|
||||
"AA",39.32,"6/11/2007","2:56pm",-0.34,39.67,40.18,39.30,3480480
|
||||
"AIG",71.86,"6/11/2007","2:56pm",+0.33,71.29,72.03,71.15,3935896
|
||||
"AXP",63.31,"6/11/2007","2:56pm",+0.27,62.79,63.42,62.42,2509230
|
||||
"BA",97.78,"6/11/2007","2:56pm",-0.41,98.25,98.79,97.59,2114200
|
||||
"C",53.70,"6/11/2007","2:56pm",+0.37,53.20,53.77,52.81,8402494
|
||||
"CAT",79.08,"6/11/2007","2:56pm",+0.56,78.32,79.46,78.06,2766652
|
||||
"DD",50.84,"6/11/2007","2:56pm",-0.29,51.13,51.21,50.59,2498497
|
||||
"DIS",34.20,"6/11/2007","2:56pm",0.00,34.28,34.44,34.12,4680250
|
||||
"GE",37.53,"6/11/2007","2:56pm",+0.21,37.07,37.61,37.05,17452700
|
||||
"GM",31.682,"6/11/2007","2:56pm",+0.682,31.00,31.79,30.90,11603351
|
||||
"HD",37.76,"6/11/2007","2:56pm",-0.19,37.78,37.83,37.62,8595367
|
||||
"HON",57.23,"6/11/2007","2:56pm",-0.15,57.25,57.40,56.91,3114842
|
||||
"HPQ",46.091,"6/11/2007","2:56pm",+0.391,45.80,46.29,45.46,7387583
|
||||
"IBM",103.53,"6/11/2007","2:56pm",+0.46,102.87,104.00,102.50,3228704
|
||||
"INTC",22.02,"6/11/2007","3:01pm",+0.19,21.70,22.08,21.69,30262880
|
||||
"JNJ",62.53,"6/11/2007","2:56pm",+0.40,62.89,62.89,62.15,6280508
|
||||
"JPM",50.71,"6/11/2007","2:56pm",+0.30,50.41,50.84,50.05,6635200
|
||||
"KO",51.82,"6/11/2007","2:56pm",+0.15,51.67,51.85,51.32,7174352
|
||||
"MCD",51.35,"6/11/2007","2:56pm",-0.06,51.47,51.62,50.98,3766527
|
||||
"MMM",85.51,"6/11/2007","2:56pm",-0.43,85.94,85.98,85.28,1757600
|
||||
"MO",70.30,"6/11/2007","2:56pm",0.00,70.25,70.50,69.76,5597885
|
||||
"MRK",51.15,"6/11/2007","2:56pm",+1.01,50.30,51.35,50.04,8391000
|
||||
"MSFT",30.16,"6/11/2007","3:01pm",+0.11,30.05,30.25,29.93,32950134
|
||||
"PFE",26.43,"6/11/2007","2:56pm",-0.09,26.50,26.54,26.31,19031536
|
||||
"PG",63.12,"6/11/2007","2:56pm",+0.05,62.80,63.21,62.75,5284846
|
||||
"T",40.26,"6/11/2007","2:56pm",0.00,40.20,40.47,39.89,10708610
|
||||
"UTX",70.21,"6/11/2007","2:56pm",-0.02,69.85,70.27,69.51,1705800
|
||||
"VZ",43.53,"6/11/2007","2:56pm",+0.46,42.95,43.61,42.88,7015557
|
||||
"WMT",49.85,"6/11/2007","2:56pm",-0.23,49.90,50.12,49.55,8513478
|
||||
"XOM",83.315,"6/11/2007","2:56pm",+0.635,82.68,83.85,82.35,9892200
|
||||
"AA",39.308,"6/11/2007","3:01pm",-0.352,39.67,40.18,39.30,3525080
|
||||
"AIG",71.88,"6/11/2007","3:01pm",+0.35,71.29,72.03,71.15,4037796
|
||||
"AXP",63.33,"6/11/2007","3:01pm",+0.29,62.79,63.42,62.42,2525030
|
||||
"BA",97.69,"6/11/2007","3:01pm",-0.50,98.25,98.79,97.59,2139700
|
||||
"C",53.6917,"6/11/2007","3:01pm",+0.3617,53.20,53.77,52.81,8489294
|
||||
"CAT",79.06,"6/11/2007","3:01pm",+0.54,78.32,79.46,78.06,2812752
|
||||
"DD",50.82,"6/11/2007","3:01pm",-0.31,51.13,51.21,50.59,2531197
|
||||
"DIS",34.20,"6/11/2007","3:01pm",0.00,34.28,34.44,34.12,4838550
|
||||
"GE",37.56,"6/11/2007","3:01pm",+0.24,37.07,37.61,37.05,17708200
|
||||
"GM",31.71,"6/11/2007","3:01pm",+0.71,31.00,31.79,30.90,11810951
|
||||
"HD",37.75,"6/11/2007","3:01pm",-0.20,37.78,37.83,37.62,8889067
|
||||
"HON",57.16,"6/11/2007","3:01pm",-0.22,57.25,57.40,56.91,3138242
|
||||
"HPQ",46.09,"6/11/2007","3:01pm",+0.39,45.80,46.29,45.46,7434383
|
||||
"IBM",103.49,"6/11/2007","3:01pm",+0.42,102.87,104.00,102.50,3267604
|
||||
"INTC",22.01,"6/11/2007","3:06pm",+0.18,21.70,22.08,21.69,30788464
|
||||
"JNJ",62.53,"6/11/2007","3:01pm",+0.40,62.89,62.89,62.15,6335808
|
||||
"JPM",50.68,"6/11/2007","3:01pm",+0.27,50.41,50.84,50.05,6741800
|
||||
"KO",51.81,"6/11/2007","3:01pm",+0.14,51.67,51.85,51.32,7223752
|
||||
"MCD",51.28,"6/11/2007","3:01pm",-0.13,51.47,51.62,50.98,3825627
|
||||
"MMM",85.47,"6/11/2007","3:01pm",-0.47,85.94,85.98,85.28,1780900
|
||||
"MO",70.26,"6/11/2007","3:01pm",-0.04,70.25,70.50,69.76,5686085
|
||||
"MRK",51.14,"6/11/2007","3:01pm",+1.00,50.30,51.35,50.04,8454600
|
||||
"MSFT",30.16,"6/11/2007","3:06pm",+0.11,30.05,30.25,29.93,33439226
|
||||
"PFE",26.42,"6/11/2007","3:01pm",-0.10,26.50,26.54,26.31,19178536
|
||||
"PG",63.09,"6/11/2007","3:01pm",+0.02,62.80,63.21,62.75,5381146
|
||||
"T",40.26,"6/11/2007","3:01pm",0.00,40.20,40.47,39.89,10923510
|
||||
"UTX",70.21,"6/11/2007","3:01pm",-0.02,69.85,70.27,69.51,1741900
|
||||
"VZ",43.54,"6/11/2007","3:01pm",+0.47,42.95,43.61,42.88,7071957
|
||||
"WMT",49.7901,"6/11/2007","3:01pm",-0.2899,49.90,50.12,49.55,8630878
|
||||
"XOM",83.29,"6/11/2007","3:01pm",+0.61,82.68,83.85,82.35,9962400
|
||||
"AA",39.33,"6/11/2007","3:06pm",-0.33,39.67,40.18,39.25,3559780
|
||||
"AIG",71.87,"6/11/2007","3:06pm",+0.34,71.29,72.03,71.15,4096696
|
||||
"AXP",63.30,"6/11/2007","3:06pm",+0.26,62.79,63.42,62.42,2545430
|
||||
"BA",97.70,"6/11/2007","3:06pm",-0.49,98.25,98.79,97.58,2170600
|
||||
"C",53.66,"6/11/2007","3:06pm",+0.33,53.20,53.77,52.81,8668794
|
||||
"CAT",79.0221,"6/11/2007","3:06pm",+0.5021,78.32,79.46,78.06,2871952
|
||||
"DD",50.84,"6/11/2007","3:06pm",-0.29,51.13,51.21,50.59,2564597
|
||||
"DIS",34.20,"6/11/2007","3:06pm",0.00,34.28,34.44,34.12,4903050
|
||||
"GE",37.54,"6/11/2007","3:06pm",+0.22,37.07,37.61,37.05,17946600
|
||||
"GM",31.76,"6/11/2007","3:06pm",+0.76,31.00,31.79,30.90,11923751
|
||||
"HD",37.76,"6/11/2007","3:06pm",-0.19,37.78,37.83,37.62,9002967
|
||||
"HON",57.17,"6/11/2007","3:06pm",-0.21,57.25,57.40,56.91,3177742
|
||||
"HPQ",46.10,"6/11/2007","3:06pm",+0.40,45.80,46.29,45.46,7517183
|
||||
"IBM",103.49,"6/11/2007","3:06pm",+0.42,102.87,104.00,102.50,3306904
|
||||
"INTC",22.03,"6/11/2007","3:11pm",+0.20,21.70,22.08,21.69,31162120
|
||||
"JNJ",62.50,"6/11/2007","3:06pm",+0.37,62.89,62.89,62.15,6420308
|
||||
"JPM",50.65,"6/11/2007","3:06pm",+0.24,50.41,50.84,50.05,6851800
|
||||
"KO",51.80,"6/11/2007","3:06pm",+0.13,51.67,51.85,51.32,7265152
|
||||
"MCD",51.29,"6/11/2007","3:06pm",-0.12,51.47,51.62,50.98,3892827
|
||||
"MMM",85.55,"6/11/2007","3:06pm",-0.39,85.94,85.98,85.28,1819600
|
||||
"MO",70.23,"6/11/2007","3:06pm",-0.07,70.25,70.50,69.76,5764585
|
||||
"MRK",51.12,"6/11/2007","3:06pm",+0.98,50.30,51.35,50.04,8577100
|
||||
"MSFT",30.16,"6/11/2007","3:11pm",+0.11,30.05,30.25,29.93,34219296
|
||||
"PFE",26.41,"6/11/2007","3:06pm",-0.11,26.50,26.54,26.31,19446136
|
||||
"PG",63.07,"6/11/2007","3:06pm",0.00,62.80,63.21,62.75,5533446
|
||||
"T",40.23,"6/11/2007","3:06pm",-0.03,40.20,40.47,39.89,11134010
|
||||
"UTX",70.22,"6/11/2007","3:06pm",-0.01,69.85,70.27,69.51,1781800
|
||||
"VZ",43.54,"6/11/2007","3:06pm",+0.47,42.95,43.61,42.88,7163857
|
||||
"WMT",49.795,"6/11/2007","3:06pm",-0.285,49.90,50.12,49.55,8740778
|
||||
"XOM",83.31,"6/11/2007","3:06pm",+0.63,82.68,83.85,82.35,10075100
|
||||
"AA",39.37,"6/11/2007","3:11pm",-0.29,39.67,40.18,39.25,3601080
|
||||
"AIG",71.86,"6/11/2007","3:11pm",+0.33,71.29,72.03,71.15,4171396
|
||||
"AXP",63.28,"6/11/2007","3:11pm",+0.24,62.79,63.42,62.42,2572030
|
||||
"BA",97.70,"6/11/2007","3:11pm",-0.49,98.25,98.79,97.58,2202300
|
||||
"C",53.60,"6/11/2007","3:11pm",+0.27,53.20,53.77,52.81,8910394
|
||||
"CAT",79.10,"6/11/2007","3:11pm",+0.58,78.32,79.46,78.06,2926952
|
||||
"DD",50.84,"6/11/2007","3:11pm",-0.29,51.13,51.21,50.59,2592497
|
||||
"DIS",34.195,"6/11/2007","3:11pm",-0.005,34.28,34.44,34.12,5006550
|
||||
"GE",37.56,"6/11/2007","3:11pm",+0.24,37.07,37.61,37.05,18148100
|
||||
"GM",31.81,"6/11/2007","3:11pm",+0.81,31.00,31.82,30.90,12211151
|
||||
"HD",37.74,"6/11/2007","3:11pm",-0.21,37.78,37.83,37.62,9075667
|
||||
"HON",57.24,"6/11/2007","3:11pm",-0.14,57.25,57.40,56.91,3219242
|
||||
"HPQ",46.12,"6/11/2007","3:11pm",+0.42,45.80,46.29,45.46,7596783
|
||||
"IBM",103.52,"6/11/2007","3:11pm",+0.45,102.87,104.00,102.50,3347004
|
||||
"INTC",22.03,"6/11/2007","3:16pm",+0.20,21.70,22.08,21.69,31499736
|
||||
"JNJ",62.48,"6/11/2007","3:11pm",+0.35,62.89,62.89,62.15,6626708
|
||||
"JPM",50.64,"6/11/2007","3:11pm",+0.23,50.41,50.84,50.05,6957100
|
||||
"KO",51.80,"6/11/2007","3:11pm",+0.13,51.67,51.85,51.32,7305952
|
||||
"MCD",51.28,"6/11/2007","3:11pm",-0.13,51.47,51.62,50.98,3951227
|
||||
"MMM",85.52,"6/11/2007","3:11pm",-0.42,85.94,85.98,85.28,1844000
|
||||
"MO",70.26,"6/11/2007","3:11pm",-0.04,70.25,70.50,69.76,5802685
|
||||
"MRK",51.19,"6/11/2007","3:11pm",+1.05,50.30,51.35,50.04,8710500
|
||||
"MSFT",30.14,"6/11/2007","3:16pm",+0.09,30.05,30.25,29.93,35084560
|
||||
"PFE",26.42,"6/11/2007","3:11pm",-0.10,26.50,26.54,26.31,19904950
|
||||
"PG",63.08,"6/11/2007","3:11pm",+0.01,62.80,63.21,62.75,5640146
|
||||
"T",40.25,"6/11/2007","3:11pm",-0.01,40.20,40.47,39.89,11272135
|
||||
"UTX",70.21,"6/11/2007","3:11pm",-0.02,69.85,70.27,69.51,1802900
|
||||
"VZ",43.56,"6/11/2007","3:11pm",+0.49,42.95,43.61,42.88,7294957
|
||||
"WMT",49.83,"6/11/2007","3:11pm",-0.25,49.90,50.12,49.55,8885778
|
||||
"XOM",83.3328,"6/11/2007","3:11pm",+0.6528,82.68,83.85,82.35,10226000
|
||||
"AA",39.36,"6/11/2007","3:16pm",-0.30,39.67,40.18,39.25,3628080
|
||||
"AIG",71.86,"6/11/2007","3:16pm",+0.33,71.29,72.03,71.15,4253196
|
||||
"AXP",63.27,"6/11/2007","3:16pm",+0.23,62.79,63.42,62.42,2599530
|
||||
"BA",97.62,"6/11/2007","3:16pm",-0.57,98.25,98.79,97.58,2240000
|
||||
"C",53.63,"6/11/2007","3:16pm",+0.30,53.20,53.77,52.81,9064594
|
||||
"CAT",79.1263,"6/11/2007","3:16pm",+0.6063,78.32,79.46,78.06,2961952
|
||||
"DD",50.84,"6/11/2007","3:16pm",-0.29,51.13,51.21,50.59,2614197
|
||||
"DIS",34.19,"6/11/2007","3:16pm",-0.01,34.28,34.44,34.12,5055750
|
||||
"GE",37.58,"6/11/2007","3:16pm",+0.26,37.07,37.61,37.05,18410300
|
||||
"GM",31.79,"6/11/2007","3:16pm",+0.79,31.00,31.82,30.90,12429251
|
||||
"HD",37.73,"6/11/2007","3:16pm",-0.22,37.78,37.83,37.62,9252867
|
||||
"HON",57.22,"6/11/2007","3:16pm",-0.16,57.25,57.40,56.91,3267342
|
||||
"HPQ",46.13,"6/11/2007","3:16pm",+0.43,45.80,46.29,45.46,7732283
|
||||
"IBM",103.52,"6/11/2007","3:16pm",+0.45,102.87,104.00,102.50,3390804
|
||||
"INTC",22.03,"6/11/2007","3:21pm",+0.20,21.70,22.08,21.69,31747072
|
||||
"JNJ",62.46,"6/11/2007","3:16pm",+0.33,62.89,62.89,62.15,6718808
|
||||
"JPM",50.66,"6/11/2007","3:16pm",+0.25,50.41,50.84,50.05,7077000
|
||||
"KO",51.81,"6/11/2007","3:16pm",+0.14,51.67,51.85,51.32,7356152
|
||||
"MCD",51.28,"6/11/2007","3:16pm",-0.13,51.47,51.62,50.98,4004827
|
||||
"MMM",85.50,"6/11/2007","3:16pm",-0.44,85.94,85.98,85.28,1863700
|
||||
"MO",70.24,"6/11/2007","3:16pm",-0.06,70.25,70.50,69.76,5895585
|
||||
"MRK",51.17,"6/11/2007","3:16pm",+1.03,50.30,51.35,50.04,8885200
|
||||
"MSFT",30.18,"6/11/2007","3:21pm",+0.13,30.05,30.25,29.93,35482676
|
||||
"PFE",26.41,"6/11/2007","3:16pm",-0.11,26.50,26.54,26.31,20145150
|
||||
"PG",63.07,"6/11/2007","3:16pm",0.00,62.80,63.21,62.75,5824046
|
||||
"T",40.24,"6/11/2007","3:16pm",-0.02,40.20,40.47,39.89,11393435
|
||||
"UTX",70.20,"6/11/2007","3:16pm",-0.03,69.85,70.27,69.51,1819800
|
||||
"VZ",43.56,"6/11/2007","3:16pm",+0.49,42.95,43.61,42.88,7465357
|
||||
"WMT",49.79,"6/11/2007","3:16pm",-0.29,49.90,50.12,49.55,9009578
|
||||
"XOM",83.29,"6/11/2007","3:16pm",+0.61,82.68,83.85,82.35,10323500
|
||||
"AA",39.41,"6/11/2007","3:21pm",-0.25,39.67,40.18,39.25,3683980
|
||||
"AIG",71.87,"6/11/2007","3:21pm",+0.34,71.29,72.03,71.15,4342596
|
||||
"AXP",63.28,"6/11/2007","3:21pm",+0.24,62.79,63.42,62.42,2631030
|
||||
"BA",97.79,"6/11/2007","3:21pm",-0.40,98.25,98.79,97.58,2283500
|
||||
"C",53.67,"6/11/2007","3:21pm",+0.34,53.20,53.77,52.81,9345794
|
||||
"CAT",79.18,"6/11/2007","3:21pm",+0.66,78.32,79.46,78.06,3018652
|
||||
"DD",50.85,"6/11/2007","3:21pm",-0.28,51.13,51.21,50.59,2681597
|
||||
"DIS",34.189,"6/11/2007","3:21pm",-0.011,34.28,34.44,34.12,5129050
|
||||
"GE",37.59,"6/11/2007","3:21pm",+0.27,37.07,37.61,37.05,18759500
|
||||
"GM",31.83,"6/11/2007","3:21pm",+0.83,31.00,31.84,30.90,12621451
|
||||
"HD",37.74,"6/11/2007","3:21pm",-0.21,37.78,37.83,37.62,9337467
|
||||
"HON",57.25,"6/11/2007","3:21pm",-0.13,57.25,57.40,56.91,3305486
|
||||
"HPQ",46.15,"6/11/2007","3:21pm",+0.45,45.80,46.29,45.46,7875783
|
||||
"IBM",103.58,"6/11/2007","3:21pm",+0.51,102.87,104.00,102.50,3430404
|
||||
"INTC",22.03,"6/11/2007","3:26pm",+0.20,21.70,22.08,21.69,32604206
|
||||
"JNJ",62.44,"6/11/2007","3:21pm",+0.31,62.89,62.89,62.15,6819808
|
||||
"JPM",50.68,"6/11/2007","3:21pm",+0.27,50.41,50.84,50.05,7182200
|
||||
"KO",51.80,"6/11/2007","3:21pm",+0.13,51.67,51.85,51.32,7430852
|
||||
"MCD",51.288,"6/11/2007","3:21pm",-0.122,51.47,51.62,50.98,4044427
|
||||
"MMM",85.54,"6/11/2007","3:21pm",-0.40,85.94,85.98,85.28,1903500
|
||||
"MO",70.28,"6/11/2007","3:21pm",-0.02,70.25,70.50,69.76,5949185
|
||||
"MRK",51.13,"6/11/2007","3:21pm",+0.99,50.30,51.35,50.04,9078900
|
||||
"MSFT",30.15,"6/11/2007","3:26pm",+0.10,30.05,30.25,29.93,36214532
|
||||
"PFE",26.44,"6/11/2007","3:21pm",-0.08,26.50,26.54,26.31,20502150
|
||||
"PG",63.10,"6/11/2007","3:21pm",+0.03,62.80,63.21,62.75,5930246
|
||||
"T",40.26,"6/11/2007","3:21pm",0.00,40.20,40.47,39.89,11546235
|
||||
"UTX",70.28,"6/11/2007","3:21pm",+0.05,69.85,70.27,69.51,1852300
|
||||
"VZ",43.58,"6/11/2007","3:21pm",+0.51,42.95,43.61,42.88,7563557
|
||||
"WMT",49.82,"6/11/2007","3:21pm",-0.26,49.90,50.12,49.55,9134278
|
||||
"XOM",83.37,"6/11/2007","3:21pm",+0.69,82.68,83.85,82.35,10394600
|
||||
"AA",39.36,"6/11/2007","3:26pm",-0.30,39.67,40.18,39.25,3734180
|
||||
"AIG",71.85,"6/11/2007","3:26pm",+0.32,71.29,72.03,71.15,4412096
|
||||
"AXP",63.26,"6/11/2007","3:26pm",+0.22,62.79,63.42,62.42,2656530
|
||||
"BA",97.75,"6/11/2007","3:26pm",-0.44,98.25,98.79,97.58,2318700
|
||||
"C",53.65,"6/11/2007","3:26pm",+0.32,53.20,53.77,52.81,9469794
|
||||
"CAT",79.14,"6/11/2007","3:26pm",+0.62,78.32,79.46,78.06,3063152
|
||||
"DD",50.85,"6/11/2007","3:26pm",-0.28,51.13,51.21,50.59,2734497
|
||||
"DIS",34.20,"6/11/2007","3:26pm",0.00,34.28,34.44,34.12,5218750
|
||||
"GE",37.61,"6/11/2007","3:26pm",+0.29,37.07,37.61,37.05,19343000
|
||||
"GM",31.88,"6/11/2007","3:26pm",+0.88,31.00,31.90,30.90,12954222
|
||||
"HD",37.7382,"6/11/2007","3:26pm",-0.2118,37.78,37.83,37.62,9712367
|
||||
"HON",57.23,"6/11/2007","3:26pm",-0.15,57.25,57.40,56.91,3354186
|
||||
"HPQ",46.15,"6/11/2007","3:26pm",+0.45,45.80,46.29,45.46,7964983
|
||||
"IBM",103.48,"6/11/2007","3:26pm",+0.41,102.87,104.00,102.50,3487304
|
||||
"INTC",22.05,"6/11/2007","3:31pm",+0.22,21.70,22.08,21.69,33024848
|
||||
"JNJ",62.38,"6/11/2007","3:26pm",+0.25,62.89,62.89,62.15,6981631
|
||||
"JPM",50.65,"6/11/2007","3:26pm",+0.24,50.41,50.84,50.05,7366500
|
||||
"KO",51.78,"6/11/2007","3:26pm",+0.11,51.67,51.85,51.32,7479252
|
||||
"MCD",51.21,"6/11/2007","3:26pm",-0.20,51.47,51.62,50.98,4117427
|
||||
"MMM",85.55,"6/11/2007","3:26pm",-0.39,85.94,85.98,85.28,1925800
|
||||
"MO",70.2525,"6/11/2007","3:26pm",-0.0475,70.25,70.50,69.76,6009485
|
||||
"MRK",51.09,"6/11/2007","3:26pm",+0.95,50.30,51.35,50.04,9266600
|
||||
"MSFT",30.165,"6/11/2007","3:31pm",+0.115,30.05,30.25,29.93,36431612
|
||||
"PFE",26.40,"6/11/2007","3:26pm",-0.12,26.50,26.54,26.31,20767150
|
||||
"PG",63.08,"6/11/2007","3:26pm",+0.01,62.80,63.21,62.75,5987546
|
||||
"T",40.25,"6/11/2007","3:26pm",-0.01,40.20,40.47,39.88,13610435
|
||||
"UTX",70.25,"6/11/2007","3:26pm",+0.02,69.85,70.30,69.51,1883100
|
||||
"VZ",43.58,"6/11/2007","3:26pm",+0.51,42.95,43.61,42.88,7711757
|
||||
"WMT",49.80,"6/11/2007","3:26pm",-0.28,49.90,50.12,49.55,9419178
|
||||
"XOM",83.2854,"6/11/2007","3:26pm",+0.6054,82.68,83.85,82.35,10509700
|
||||
"AA",39.37,"6/11/2007","3:31pm",-0.29,39.67,40.18,39.25,3767480
|
||||
"AIG",71.84,"6/11/2007","3:31pm",+0.31,71.29,72.03,71.15,4469996
|
||||
"AXP",63.27,"6/11/2007","3:31pm",+0.23,62.79,63.42,62.42,2681730
|
||||
"BA",97.66,"6/11/2007","3:31pm",-0.53,98.25,98.79,97.58,2347600
|
||||
"C",53.63,"6/11/2007","3:31pm",+0.30,53.20,53.77,52.81,9570094
|
||||
"CAT",79.15,"6/11/2007","3:31pm",+0.63,78.32,79.46,78.06,3108752
|
||||
"DD",50.83,"6/11/2007","3:31pm",-0.30,51.13,51.21,50.59,2758297
|
||||
"DIS",34.20,"6/11/2007","3:31pm",0.00,34.28,34.44,34.12,5359050
|
||||
"GE",37.61,"6/11/2007","3:31pm",+0.29,37.07,37.61,37.05,19495200
|
||||
"GM",31.83,"6/11/2007","3:31pm",+0.83,31.00,31.90,30.90,13338922
|
||||
"HD",37.7275,"6/11/2007","3:31pm",-0.2225,37.78,37.83,37.62,9833219
|
||||
"HON",57.23,"6/11/2007","3:31pm",-0.15,57.25,57.40,56.91,3411886
|
||||
"HPQ",46.16,"6/11/2007","3:31pm",+0.46,45.80,46.29,45.46,8108883
|
||||
"IBM",103.48,"6/11/2007","3:31pm",+0.41,102.87,104.00,102.50,3582104
|
||||
"INTC",22.02,"6/11/2007","3:36pm",+0.19,21.70,22.08,21.69,33811596
|
||||
"JNJ",62.38,"6/11/2007","3:31pm",+0.25,62.89,62.89,62.15,7072531
|
||||
"JPM",50.6225,"6/11/2007","3:31pm",+0.2125,50.41,50.84,50.05,7423100
|
||||
"KO",51.78,"6/11/2007","3:31pm",+0.11,51.67,51.85,51.32,7527052
|
||||
"MCD",51.17,"6/11/2007","3:31pm",-0.24,51.47,51.62,50.98,4207427
|
||||
"MMM",85.50,"6/11/2007","3:31pm",-0.44,85.94,85.98,85.28,1954000
|
||||
"MO",70.26,"6/11/2007","3:31pm",-0.04,70.25,70.50,69.76,6068285
|
||||
"MRK",51.15,"6/11/2007","3:31pm",+1.01,50.30,51.35,50.04,9411500
|
||||
"MSFT",30.14,"6/11/2007","3:36pm",+0.09,30.05,30.25,29.93,37331996
|
||||
"PFE",26.40,"6/11/2007","3:31pm",-0.12,26.50,26.54,26.31,21355550
|
||||
"PG",63.11,"6/11/2007","3:31pm",+0.04,62.80,63.21,62.75,6055546
|
||||
"T",40.24,"6/11/2007","3:31pm",-0.02,40.20,40.47,39.88,13730535
|
||||
"UTX",70.26,"6/11/2007","3:31pm",+0.03,69.85,70.30,69.51,1985400
|
||||
"VZ",43.58,"6/11/2007","3:31pm",+0.51,42.95,43.61,42.88,7833157
|
||||
"WMT",49.80,"6/11/2007","3:31pm",-0.28,49.90,50.12,49.55,9490578
|
||||
"XOM",83.275,"6/11/2007","3:31pm",+0.595,82.68,83.85,82.35,10612200
|
||||
"AA",39.30,"6/11/2007","3:36pm",-0.36,39.67,40.18,39.25,3811480
|
||||
"AIG",71.77,"6/11/2007","3:36pm",+0.24,71.29,72.03,71.15,4581496
|
||||
"AXP",63.155,"6/11/2007","3:36pm",+0.115,62.79,63.42,62.42,2721130
|
||||
"BA",97.56,"6/11/2007","3:36pm",-0.63,98.25,98.79,97.55,2383000
|
||||
"C",53.54,"6/11/2007","3:36pm",+0.21,53.20,53.77,52.81,11390094
|
||||
"CAT",79.04,"6/11/2007","3:36pm",+0.52,78.32,79.46,78.06,3145152
|
||||
"DD",50.76,"6/11/2007","3:36pm",-0.37,51.13,51.21,50.59,2813297
|
||||
"DIS",34.18,"6/11/2007","3:36pm",-0.02,34.28,34.44,34.12,5431250
|
||||
"GE",37.58,"6/11/2007","3:36pm",+0.26,37.07,37.61,37.05,19977600
|
||||
"GM",31.79,"6/11/2007","3:36pm",+0.79,31.00,31.90,30.90,13573622
|
||||
"HD",37.72,"6/11/2007","3:36pm",-0.23,37.78,37.83,37.62,10373519
|
||||
"HON",57.18,"6/11/2007","3:36pm",-0.20,57.25,57.40,56.91,3490736
|
||||
"HPQ",46.10,"6/11/2007","3:36pm",+0.40,45.80,46.29,45.46,8224783
|
||||
"IBM",103.36,"6/11/2007","3:36pm",+0.29,102.87,104.00,102.50,3667004
|
||||
"INTC",22.02,"6/11/2007","3:41pm",+0.19,21.70,22.08,21.69,34379072
|
||||
"JNJ",62.33,"6/11/2007","3:36pm",+0.20,62.89,62.89,62.15,7231131
|
||||
"JPM",50.58,"6/11/2007","3:36pm",+0.17,50.41,50.84,50.05,7580900
|
||||
"KO",51.73,"6/11/2007","3:36pm",+0.06,51.67,51.85,51.32,7588352
|
||||
"MCD",51.17,"6/11/2007","3:36pm",-0.24,51.47,51.62,50.98,4327427
|
||||
"MMM",85.42,"6/11/2007","3:36pm",-0.52,85.94,85.98,85.28,1997700
|
||||
"MO",70.24,"6/11/2007","3:36pm",-0.06,70.25,70.50,69.76,6182685
|
||||
"MRK",51.15,"6/11/2007","3:36pm",+1.01,50.30,51.35,50.04,9552400
|
||||
"MSFT",30.11,"6/11/2007","3:41pm",+0.06,30.05,30.25,29.93,37726620
|
||||
"PFE",26.41,"6/11/2007","3:36pm",-0.11,26.50,26.54,26.31,21924450
|
||||
"PG",63.0627,"6/11/2007","3:36pm",-0.0073,62.80,63.21,62.75,6121146
|
||||
"T",40.23,"6/11/2007","3:36pm",-0.03,40.20,40.47,39.88,13995835
|
||||
"UTX",70.17,"6/11/2007","3:36pm",-0.06,69.85,70.30,69.51,2031100
|
||||
"VZ",43.55,"6/11/2007","3:36pm",+0.48,42.95,43.61,42.88,7990757
|
||||
"WMT",49.811,"6/11/2007","3:36pm",-0.269,49.90,50.12,49.55,9655678
|
||||
"XOM",83.17,"6/11/2007","3:36pm",+0.49,82.68,83.85,82.35,10797200
|
||||
"AA",39.28,"6/11/2007","3:41pm",-0.38,39.67,40.18,39.25,3906080
|
||||
"AIG",71.73,"6/11/2007","3:41pm",+0.20,71.29,72.03,71.15,4691496
|
||||
"AXP",63.15,"6/11/2007","3:41pm",+0.11,62.79,63.42,62.42,2766030
|
||||
"BA",97.50,"6/11/2007","3:41pm",-0.69,98.25,98.79,97.48,2453500
|
||||
"C",53.51,"6/11/2007","3:41pm",+0.18,53.20,53.77,52.81,11535394
|
||||
"CAT",78.98,"6/11/2007","3:41pm",+0.46,78.32,79.46,78.06,3172952
|
||||
"DD",50.73,"6/11/2007","3:41pm",-0.40,51.13,51.21,50.59,2869897
|
||||
"DIS",34.18,"6/11/2007","3:41pm",-0.02,34.28,34.44,34.12,5648050
|
||||
"GE",37.58,"6/11/2007","3:41pm",+0.26,37.07,37.61,37.05,20658300
|
||||
"GM",31.73,"6/11/2007","3:41pm",+0.73,31.00,31.90,30.90,13983822
|
||||
"HD",37.72,"6/11/2007","3:41pm",-0.23,37.78,37.83,37.62,10596819
|
||||
"HON",57.16,"6/11/2007","3:41pm",-0.22,57.25,57.40,56.91,3548036
|
||||
"HPQ",46.072,"6/11/2007","3:41pm",+0.372,45.80,46.29,45.46,8398683
|
||||
"IBM",103.38,"6/11/2007","3:41pm",+0.31,102.87,104.00,102.50,3741504
|
||||
"INTC",21.992,"6/11/2007","3:46pm",+0.162,21.70,22.08,21.69,35566072
|
||||
"JNJ",62.29,"6/11/2007","3:41pm",+0.16,62.89,62.89,62.15,7444481
|
||||
"JPM",50.59,"6/11/2007","3:41pm",+0.18,50.41,50.84,50.05,7794800
|
||||
"KO",51.74,"6/11/2007","3:41pm",+0.07,51.67,51.85,51.32,7758670
|
||||
"MCD",51.25,"6/11/2007","3:41pm",-0.16,51.47,51.62,50.98,4427195
|
||||
"MMM",85.50,"6/11/2007","3:41pm",-0.44,85.94,85.98,85.28,2066900
|
||||
"MO",70.25,"6/11/2007","3:41pm",-0.05,70.25,70.50,69.76,6272385
|
||||
"MRK",51.14,"6/11/2007","3:41pm",+1.00,50.30,51.35,50.04,9785300
|
||||
"MSFT",30.08,"6/11/2007","3:46pm",+0.03,30.05,30.25,29.93,38740488
|
||||
"PFE",26.43,"6/11/2007","3:41pm",-0.09,26.50,26.54,26.31,22573150
|
||||
"PG",63.06,"6/11/2007","3:41pm",-0.01,62.80,63.21,62.75,6204746
|
||||
"T",40.25,"6/11/2007","3:41pm",-0.01,40.20,40.47,39.88,14297535
|
||||
"UTX",70.12,"6/11/2007","3:41pm",-0.11,69.85,70.30,69.51,2068500
|
||||
"VZ",43.56,"6/11/2007","3:41pm",+0.49,42.95,43.61,42.88,8283057
|
||||
"WMT",49.85,"6/11/2007","3:41pm",-0.23,49.90,50.12,49.55,9905878
|
||||
"XOM",83.1801,"6/11/2007","3:41pm",+0.5001,82.68,83.85,82.35,10980200
|
||||
"AA",39.28,"6/11/2007","3:46pm",-0.38,39.67,40.18,39.21,4013480
|
||||
"AIG",71.74,"6/11/2007","3:46pm",+0.21,71.29,72.03,71.15,4893496
|
||||
"AXP",63.13,"6/11/2007","3:46pm",+0.09,62.79,63.42,62.42,2811230
|
||||
"BA",97.50,"6/11/2007","3:46pm",-0.69,98.25,98.79,97.44,2545100
|
||||
"C",53.47,"6/11/2007","3:46pm",+0.14,53.20,53.77,52.81,11812294
|
||||
"CAT",78.98,"6/11/2007","3:46pm",+0.46,78.32,79.46,78.06,3228752
|
||||
"DD",50.78,"6/11/2007","3:46pm",-0.35,51.13,51.21,50.59,2971197
|
||||
"DIS",34.17,"6/11/2007","3:46pm",-0.03,34.28,34.44,34.12,5764350
|
||||
"GE",37.53,"6/11/2007","3:46pm",+0.21,37.07,37.6202,37.05,21261200
|
||||
"GM",31.74,"6/11/2007","3:46pm",+0.74,31.00,31.90,30.90,14221022
|
||||
"HD",37.69,"6/11/2007","3:46pm",-0.26,37.78,37.83,37.62,10829419
|
||||
"HON",57.13,"6/11/2007","3:46pm",-0.25,57.25,57.40,56.91,3612436
|
||||
"HPQ",46.06,"6/11/2007","3:46pm",+0.36,45.80,46.29,45.46,8528683
|
||||
"IBM",103.37,"6/11/2007","3:46pm",+0.30,102.87,104.00,102.50,3846604
|
||||
"INTC",21.97,"6/11/2007","3:51pm",+0.14,21.70,22.08,21.69,37733764
|
||||
"JNJ",62.28,"6/11/2007","3:46pm",+0.15,62.89,62.89,62.15,7592281
|
||||
"JPM",50.57,"6/11/2007","3:46pm",+0.16,50.41,50.84,50.05,7987300
|
||||
"KO",51.71,"6/11/2007","3:46pm",+0.04,51.67,51.85,51.32,7854570
|
||||
"MCD",51.31,"6/11/2007","3:46pm",-0.10,51.47,51.62,50.98,4569195
|
||||
"MMM",85.41,"6/11/2007","3:46pm",-0.53,85.94,85.98,85.28,2120500
|
||||
"MO",70.24,"6/11/2007","3:46pm",-0.06,70.25,70.50,69.76,6364785
|
||||
"MRK",51.11,"6/11/2007","3:46pm",+0.97,50.30,51.35,50.04,9941300
|
||||
"MSFT",30.04,"6/11/2007","3:51pm",-0.01,30.05,30.25,29.93,41124800
|
||||
"PFE",26.41,"6/11/2007","3:46pm",-0.11,26.50,26.54,26.31,23036750
|
||||
"PG",63.05,"6/11/2007","3:46pm",-0.02,62.80,63.21,62.75,6374346
|
||||
"T",40.21,"6/11/2007","3:46pm",-0.05,40.20,40.47,39.88,15620735
|
||||
"UTX",70.19,"6/11/2007","3:46pm",-0.04,69.85,70.30,69.51,2171000
|
||||
"VZ",43.569,"6/11/2007","3:46pm",+0.499,42.95,43.61,42.88,8442057
|
||||
"WMT",49.85,"6/11/2007","3:46pm",-0.23,49.90,50.12,49.55,10081678
|
||||
"XOM",83.14,"6/11/2007","3:46pm",+0.46,82.68,83.85,82.35,11246800
|
||||
"AA",39.29,"6/11/2007","3:51pm",-0.37,39.67,40.18,39.21,4132380
|
||||
"AIG",71.62,"6/11/2007","3:51pm",+0.09,71.29,72.03,71.15,5038729
|
||||
"AXP",63.09,"6/11/2007","3:51pm",+0.05,62.79,63.42,62.42,2861530
|
||||
"BA",97.49,"6/11/2007","3:51pm",-0.70,98.25,98.79,97.43,3063600
|
||||
"C",53.42,"6/11/2007","3:51pm",+0.09,53.20,53.77,52.81,12267394
|
||||
"CAT",78.78,"6/11/2007","3:51pm",+0.26,78.32,79.46,78.06,3287352
|
||||
"DD",50.73,"6/11/2007","3:51pm",-0.40,51.13,51.21,50.59,3033697
|
||||
"DIS",34.135,"6/11/2007","3:51pm",-0.065,34.28,34.44,34.12,5919350
|
||||
"GE",37.49,"6/11/2007","3:51pm",+0.17,37.07,37.6202,37.05,21532000
|
||||
"GM",31.72,"6/11/2007","3:51pm",+0.72,31.00,31.90,30.90,14377693
|
||||
"HD",37.67,"6/11/2007","3:51pm",-0.28,37.78,37.83,37.62,11229419
|
||||
"HON",57.04,"6/11/2007","3:51pm",-0.34,57.25,57.40,56.91,3682536
|
||||
"HPQ",46.00,"6/11/2007","3:51pm",+0.30,45.80,46.29,45.46,8652383
|
||||
"IBM",103.20,"6/11/2007","3:51pm",+0.13,102.87,104.00,102.50,3934104
|
||||
"INTC",21.97,"6/11/2007","3:56pm",+0.14,21.70,22.08,21.69,38481016
|
||||
"JNJ",62.22,"6/11/2007","3:51pm",+0.09,62.89,62.89,62.15,7738581
|
||||
"JPM",50.48,"6/11/2007","3:51pm",+0.07,50.41,50.84,50.05,8466180
|
||||
"KO",51.65,"6/11/2007","3:51pm",-0.02,51.67,51.85,51.32,7921870
|
||||
"MCD",51.26,"6/11/2007","3:51pm",-0.15,51.47,51.62,50.98,5379392
|
||||
"MMM",85.26,"6/11/2007","3:51pm",-0.68,85.94,85.98,85.28,2179200
|
||||
"MO",70.22,"6/11/2007","3:51pm",-0.08,70.25,70.50,69.76,6468485
|
||||
"MRK",51.09,"6/11/2007","3:51pm",+0.95,50.30,51.35,50.04,10126700
|
||||
"MSFT",30.04,"6/11/2007","3:56pm",-0.01,30.05,30.25,29.93,45401260
|
||||
"PFE",26.36,"6/11/2007","3:51pm",-0.16,26.50,26.54,26.31,23619450
|
||||
"PG",63.01,"6/11/2007","3:51pm",-0.06,62.80,63.21,62.75,6447846
|
||||
"T",40.14,"6/11/2007","3:51pm",-0.12,40.20,40.47,39.88,15842235
|
||||
"UTX",70.19,"6/11/2007","3:51pm",-0.04,69.85,70.30,69.51,2300500
|
||||
"VZ",43.51,"6/11/2007","3:51pm",+0.44,42.95,43.61,42.88,8663757
|
||||
"WMT",49.81,"6/11/2007","3:51pm",-0.27,49.90,50.12,49.55,10280178
|
||||
"XOM",82.99,"6/11/2007","3:51pm",+0.31,82.68,83.85,82.35,11476800
|
||||
"AA",39.29,"6/11/2007","3:56pm",-0.37,39.67,40.18,39.21,4279480
|
||||
"AIG",71.63,"6/11/2007","3:56pm",+0.10,71.29,72.03,71.15,5259629
|
||||
"AXP",63.06,"6/11/2007","3:56pm",+0.02,62.79,63.42,62.42,2932030
|
||||
"BA",97.48,"6/11/2007","3:56pm",-0.71,98.25,98.79,97.43,3134100
|
||||
"C",53.46,"6/11/2007","3:56pm",+0.13,53.20,53.77,52.81,12689394
|
||||
"CAT",78.76,"6/11/2007","3:56pm",+0.24,78.32,79.46,78.06,3364652
|
||||
"DD",50.70,"6/11/2007","3:56pm",-0.43,51.13,51.21,50.59,3120297
|
||||
"DIS",34.16,"6/11/2007","3:56pm",-0.04,34.28,34.44,34.12,6061350
|
||||
"GE",37.47,"6/11/2007","3:56pm",+0.15,37.07,37.6202,37.05,22045900
|
||||
"GM",31.72,"6/11/2007","3:56pm",+0.72,31.00,31.90,30.90,14702993
|
||||
"HD",37.67,"6/11/2007","3:56pm",-0.28,37.78,37.83,37.62,11654819
|
||||
"HON",56.95,"6/11/2007","3:56pm",-0.43,57.25,57.40,56.91,3802936
|
||||
"HPQ",45.93,"6/11/2007","3:56pm",+0.23,45.80,46.29,45.46,11355083
|
||||
"IBM",103.01,"6/11/2007","3:56pm",-0.06,102.87,104.00,102.50,4096804
|
||||
"INTC",21.93,"6/11/2007","4:01pm",+0.10,21.70,22.08,21.69,40860336
|
||||
"JNJ",62.29,"6/11/2007","3:56pm",+0.16,62.89,62.89,62.15,7933681
|
||||
"JPM",50.43,"6/11/2007","3:56pm",+0.02,50.41,50.84,50.05,8654880
|
||||
"KO",51.66,"6/11/2007","3:56pm",-0.01,51.67,51.85,51.32,7987870
|
||||
"MCD",51.30,"6/11/2007","3:56pm",-0.11,51.47,51.62,50.98,5548192
|
||||
"MMM",85.18,"6/11/2007","3:56pm",-0.76,85.94,85.98,85.17,2274500
|
||||
"MO",70.22,"6/11/2007","3:56pm",-0.08,70.25,70.50,69.76,6635085
|
||||
"MRK",51.06,"6/11/2007","3:56pm",+0.92,50.30,51.35,50.04,10461500
|
||||
"MSFT",30.02,"6/11/2007","4:00pm",-0.03,30.05,30.25,29.93,46709236
|
||||
"PFE",26.37,"6/11/2007","3:56pm",-0.15,26.50,26.54,26.31,24178850
|
||||
"PG",63.02,"6/11/2007","3:56pm",-0.05,62.80,63.21,62.75,6576946
|
||||
"T",40.0918,"6/11/2007","3:56pm",-0.1682,40.20,40.47,39.88,16125935
|
||||
"UTX",70.17,"6/11/2007","3:56pm",-0.06,69.85,70.30,69.51,2406200
|
||||
"VZ",43.48,"6/11/2007","3:56pm",+0.41,42.95,43.61,42.88,9009057
|
||||
"WMT",49.79,"6/11/2007","3:56pm",-0.29,49.90,50.12,49.55,10441378
|
||||
"XOM",82.96,"6/11/2007","3:56pm",+0.28,82.68,83.85,82.35,11744600
|
||||
"AA",39.30,"6/11/2007","4:01pm",-0.36,39.67,40.18,39.14,4516480
|
||||
"AIG",71.65,"6/11/2007","4:00pm",+0.12,71.29,72.03,71.15,5942029
|
||||
"AXP",63.06,"6/11/2007","4:00pm",+0.02,62.79,63.42,62.42,3050830
|
||||
"BA",97.55,"6/11/2007","4:00pm",-0.64,98.25,98.79,97.42,3300500
|
||||
"C",53.47,"6/11/2007","4:01pm",+0.14,53.20,53.77,52.81,13457894
|
||||
"CAT",78.75,"6/11/2007","4:00pm",+0.23,78.32,79.46,78.06,3456552
|
||||
"DD",50.73,"6/11/2007","3:59pm",-0.40,51.13,51.21,50.59,3162997
|
||||
"DIS",34.15,"6/11/2007","3:59pm",-0.05,34.28,34.44,34.12,6162650
|
||||
"GE",37.46,"6/11/2007","4:01pm",+0.14,37.07,37.6202,37.05,23163100
|
||||
"GM",31.77,"6/11/2007","4:00pm",+0.77,31.00,31.90,30.90,15223093
|
||||
"HD",37.71,"6/11/2007","4:00pm",-0.24,37.78,37.83,37.62,12074119
|
||||
"HON",57.02,"6/11/2007","3:59pm",-0.36,57.25,57.40,56.91,3911336
|
||||
"HPQ",45.89,"6/11/2007","4:00pm",+0.19,45.80,46.29,45.46,11960883
|
||||
"IBM",103.22,"6/11/2007","4:01pm",+0.15,102.87,104.00,102.50,4668204
|
||||
"INTC",21.93,"6/11/2007","4:01pm",+0.10,21.70,22.08,21.69,41134284
|
||||
"JNJ",62.27,"6/11/2007","4:00pm",+0.14,62.89,62.89,62.15,8452146
|
||||
"JPM",50.45,"6/11/2007","4:00pm",+0.04,50.41,50.84,50.05,8869280
|
||||
"KO",51.65,"6/11/2007","3:59pm",-0.02,51.67,51.85,51.32,8049770
|
||||
"MCD",51.25,"6/11/2007","4:01pm",-0.16,51.47,51.62,50.98,5969292
|
||||
"MMM",85.30,"6/11/2007","4:01pm",-0.64,85.94,85.98,85.17,2454400
|
||||
"MO",70.22,"6/11/2007","4:00pm",-0.08,70.25,70.50,69.76,6887785
|
||||
"MRK",51.09,"6/11/2007","3:59pm",+0.95,50.30,51.35,50.04,10623065
|
||||
"MSFT",30.02,"6/11/2007","4:00pm",-0.03,30.05,30.25,29.93,46924636
|
||||
"PFE",26.37,"6/11/2007","4:00pm",-0.15,26.50,26.54,26.31,25287450
|
||||
"PG",63.05,"6/11/2007","4:00pm",-0.02,62.80,63.21,62.75,6920246
|
||||
"T",40.12,"6/11/2007","4:00pm",-0.14,40.20,40.47,39.88,16712535
|
||||
"UTX",70.18,"6/11/2007","4:00pm",-0.05,69.85,70.30,69.51,2660900
|
||||
"VZ",43.47,"6/11/2007","3:59pm",+0.40,42.95,43.61,42.88,9156827
|
||||
"WMT",49.81,"6/11/2007","4:00pm",-0.27,49.90,50.12,49.55,10924878
|
||||
"XOM",83.06,"6/11/2007","4:00pm",+0.38,82.68,83.85,82.35,12427710
|
||||
|
29
Data/missing.csv
Executable file
29
Data/missing.csv
Executable file
@ -0,0 +1,29 @@
|
||||
name,shares,price
|
||||
"AA",15,39.48
|
||||
"AXP",10,62.58
|
||||
"BA",5,98.31
|
||||
"C",,53.08
|
||||
"CAT",15,78.29
|
||||
"DD",10,50.75
|
||||
"DIS",50,N/A
|
||||
"GE",,37.23
|
||||
"GM",15,31.44
|
||||
"HD",20,37.67
|
||||
"HPQ",5,45.81
|
||||
"IBM",10,102.86
|
||||
"INTC",,21.84
|
||||
"JNJ",20,62.25
|
||||
"JPM",10,50.35
|
||||
"KO",5,51.65
|
||||
"MCD",,51.11
|
||||
"MMM",10,85.60
|
||||
"MO",,70.09
|
||||
"MRK",5,50.21
|
||||
"MSFT",20,30.08
|
||||
"PFE",,26.40
|
||||
"PG",5,62.79
|
||||
"T",10,40.03
|
||||
"UTX",8,69.81
|
||||
"VZ",,42.92
|
||||
"WMT",10,49.78
|
||||
"XOM",15,82.50
|
||||
|
8
Data/portfolio.csv
Executable file
8
Data/portfolio.csv
Executable file
@ -0,0 +1,8 @@
|
||||
name,shares,price
|
||||
"AA",100,32.20
|
||||
"IBM",50,91.10
|
||||
"CAT",150,83.44
|
||||
"MSFT",200,51.23
|
||||
"GE",95,40.37
|
||||
"MSFT",50,65.10
|
||||
"IBM",100,70.44
|
||||
|
BIN
Data/portfolio.csv.gz
Executable file
BIN
Data/portfolio.csv.gz
Executable file
Binary file not shown.
7
Data/portfolio.dat
Executable file
7
Data/portfolio.dat
Executable file
@ -0,0 +1,7 @@
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
7
Data/portfolio1.dat
Executable file
7
Data/portfolio1.dat
Executable file
@ -0,0 +1,7 @@
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
5
Data/portfolio2.csv
Executable file
5
Data/portfolio2.csv
Executable file
@ -0,0 +1,5 @@
|
||||
name,shares,price
|
||||
"AA",50,27.10
|
||||
"HPQ",250,43.15
|
||||
"MSFT",25,50.15
|
||||
"GE",125,52.10
|
||||
|
4
Data/portfolio2.dat
Executable file
4
Data/portfolio2.dat
Executable file
@ -0,0 +1,4 @@
|
||||
AA 50 27.10
|
||||
HPQ 250 43.15
|
||||
MSFT 25 50.15
|
||||
GE 125 52.10
|
||||
28
Data/portfolio3.csv
Executable file
28
Data/portfolio3.csv
Executable file
@ -0,0 +1,28 @@
|
||||
"AA",15,39.48
|
||||
"AXP",10,62.58
|
||||
"BA",5,98.31
|
||||
"C",-,53.08
|
||||
"CAT",15,78.29
|
||||
"DD",10,50.75
|
||||
"DIS",-,N/A
|
||||
"GE",-,37.23
|
||||
"GM",15,31.44
|
||||
"HD",20,37.67
|
||||
"HPQ",5,45.81
|
||||
"IBM",10,102.86
|
||||
"INTC",-,21.84
|
||||
"JNJ",20,62.25
|
||||
"JPM",10,50.35
|
||||
"KO",5,51.65
|
||||
"MCD",-,51.11
|
||||
"MMM",10,85.60
|
||||
"MO",-,70.09
|
||||
"MRK",5,50.21
|
||||
"MSFT",20,30.08
|
||||
"PFE",-,26.40
|
||||
"PG",5,62.79
|
||||
"T",10,40.03
|
||||
"UTX",8,69.81
|
||||
"VZ",-,42.92
|
||||
"WMT",10,49.78
|
||||
"XOM",15,82.50
|
||||
|
28
Data/portfolio3.dat
Executable file
28
Data/portfolio3.dat
Executable file
@ -0,0 +1,28 @@
|
||||
AA 15 39.48
|
||||
AXP 10 62.58
|
||||
BA 5 98.31
|
||||
C - 53.08
|
||||
CAT 15 78.29
|
||||
DD 10 50.75
|
||||
DIS - N/A
|
||||
GE - 37.23
|
||||
GM 15 31.44
|
||||
HD 20 37.67
|
||||
HPQ 5 45.81
|
||||
IBM 10 102.86
|
||||
INTC - 21.84
|
||||
JNJ 20 62.25
|
||||
JPM 10 50.35
|
||||
KO 5 51.65
|
||||
MCD - 51.11
|
||||
MMM 10 85.60
|
||||
MO - 70.09
|
||||
MRK 5 50.21
|
||||
MSFT 20 30.08
|
||||
PFE - 26.40
|
||||
PG 5 62.79
|
||||
T 10 40.03
|
||||
UTX 8 69.81
|
||||
VZ - 42.92
|
||||
WMT 10 49.78
|
||||
XOM 15 82.50
|
||||
7
Data/portfolio_noheader.csv
Executable file
7
Data/portfolio_noheader.csv
Executable file
@ -0,0 +1,7 @@
|
||||
"AA",100,32.20
|
||||
"IBM",50,91.10
|
||||
"CAT",150,83.44
|
||||
"MSFT",200,51.23
|
||||
"GE",95,40.37
|
||||
"MSFT",50,65.10
|
||||
"IBM",100,70.44
|
||||
|
30
Data/prices.csv
Normal file
30
Data/prices.csv
Normal file
@ -0,0 +1,30 @@
|
||||
"AA",9.22
|
||||
"AXP",24.85
|
||||
"BA",44.85
|
||||
"BAC",11.27
|
||||
"C",3.72
|
||||
"CAT",35.46
|
||||
"CVX",66.67
|
||||
"DD",28.47
|
||||
"DIS",24.22
|
||||
"GE",13.48
|
||||
"GM",0.75
|
||||
"HD",23.16
|
||||
"HPQ",34.35
|
||||
"IBM",106.28
|
||||
"INTC",15.72
|
||||
"JNJ",55.16
|
||||
"JPM",36.90
|
||||
"KFT",26.11
|
||||
"KO",49.16
|
||||
"MCD",58.99
|
||||
"MMM",57.10
|
||||
"MRK",27.58
|
||||
"MSFT",20.89
|
||||
"PFE",15.19
|
||||
"PG",51.94
|
||||
"T",24.79
|
||||
"UTX",52.61
|
||||
"VZ",29.26
|
||||
"WMT",49.74
|
||||
"XOM",69.35
|
||||
|
183
Data/stocksim.py
Executable file
183
Data/stocksim.py
Executable file
@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env python
|
||||
# stocksim.py
|
||||
#
|
||||
# Stock market simulator. This simulator creates stock market
|
||||
# data and provides it in several different ways:
|
||||
#
|
||||
# 1. Makes periodic updates to a log file stocklog.dat
|
||||
#
|
||||
# The purpose of this module is to provide data to the user
|
||||
# in different ways in order to write interesting Python examples
|
||||
|
||||
import math
|
||||
import time
|
||||
import threading
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
import Queue as queue
|
||||
|
||||
history_file = "dowstocks.csv"
|
||||
|
||||
# Convert a time string such as "4:00pm" to minutes past midnight
|
||||
def minutes(tm):
|
||||
am_pm = tm[-2:]
|
||||
fields = tm[:-2].split(":")
|
||||
hour = int(fields[0])
|
||||
minute = int(fields[1])
|
||||
if hour == 12:
|
||||
hour = 0
|
||||
if am_pm == 'pm':
|
||||
hour += 12
|
||||
return hour*60 + minute
|
||||
|
||||
# Convert time in minutes to a format string
|
||||
def minutes_to_str(m):
|
||||
frac,m = math.modf(m)
|
||||
hours = m//60
|
||||
minutes = m % 60
|
||||
seconds = frac * 60
|
||||
return "%02d:%02d.%02.f" % (hours,minutes,seconds)
|
||||
|
||||
# Read the stock history file as a list of lists
|
||||
def read_history(filename):
|
||||
result = []
|
||||
for line in open(filename):
|
||||
str_fields = line.strip().split(",")
|
||||
fields = [eval(x) for x in str_fields]
|
||||
fields[3] = minutes(fields[3])
|
||||
result.append(fields)
|
||||
return result
|
||||
|
||||
# Format CSV record
|
||||
def csv_record(fields):
|
||||
s = '"%s",%0.2f,"%s","%s",%0.2f,%0.2f,%0.2f,%0.2f,%d' % tuple(fields)
|
||||
return s
|
||||
|
||||
class StockTrack(object):
|
||||
def __init__(self,name):
|
||||
self.name = name
|
||||
self.history = []
|
||||
self.price = 0
|
||||
self.time = 0
|
||||
self.index = 0
|
||||
self.open = 0
|
||||
self.low = 0
|
||||
self.high = 0
|
||||
self.volume = 0
|
||||
self.initial = 0
|
||||
self.change = 0
|
||||
self.date = ""
|
||||
def add_data(self,record):
|
||||
self.history.append(record)
|
||||
def reset(self,time):
|
||||
self.time = time
|
||||
# Sort the history by time
|
||||
self.history.sort(key=lambda t:t[3])
|
||||
# Find the first entry who's time is behind the given time
|
||||
self.index = 0
|
||||
while self.index < len(self.history):
|
||||
if self.history[self.index][3] > time:
|
||||
break
|
||||
self.index += 1
|
||||
self.open = self.history[0][5]
|
||||
self.initial = self.history[0][1] - self.history[0][4]
|
||||
self.date = self.history[0][2]
|
||||
self.update()
|
||||
self.low = self.price
|
||||
self.high = self.price
|
||||
|
||||
# Calculate interpolated value of a given field based on
|
||||
# current time
|
||||
def interpolate(self,field):
|
||||
first = self.history[self.index][field]
|
||||
next = self.history[self.index+1][field]
|
||||
first_t = self.history[self.index][3]
|
||||
next_t = self.history[self.index+1][3]
|
||||
try:
|
||||
slope = (next - first)/(next_t-first_t)
|
||||
return first + slope*(self.time - first_t)
|
||||
except ZeroDivisionError:
|
||||
return first
|
||||
|
||||
# Update all computed values
|
||||
def update(self):
|
||||
self.price = round(self.interpolate(1),2)
|
||||
self.volume = int(self.interpolate(-1))
|
||||
if self.price < self.low:
|
||||
self.low = self.price
|
||||
if self.price >= self.high:
|
||||
self.high = self.price
|
||||
self.change = self.price - self.initial
|
||||
|
||||
# Increment the time by a delta
|
||||
def incr(self,dt):
|
||||
self.time += dt
|
||||
if self.index < (len(self.history) - 2):
|
||||
while self.index < (len(self.history) - 2) and self.time >= self.history[self.index+1][3]:
|
||||
self.index += 1
|
||||
self.update()
|
||||
|
||||
def make_record(self):
|
||||
return [self.name,round(self.price,2),self.date,minutes_to_str(self.time),round(self.change,2),self.open,round(self.high,2),
|
||||
round(self.low,2),self.volume]
|
||||
|
||||
class MarketSimulator(object):
|
||||
def __init__(self):
|
||||
self.stocks = { }
|
||||
self.prices = { }
|
||||
self.time = 0
|
||||
self.observers = []
|
||||
def register(self,observer):
|
||||
self.observers.append(observer)
|
||||
|
||||
def publish(self,record):
|
||||
for obj in self.observers:
|
||||
obj.update(record)
|
||||
def add_history(self,filename):
|
||||
hist = read_history(filename)
|
||||
for record in hist:
|
||||
if record[0] not in self.stocks:
|
||||
self.stocks[record[0]] = StockTrack(record[0])
|
||||
self.stocks[record[0]].add_data(record)
|
||||
|
||||
def reset(self,time):
|
||||
self.time = time
|
||||
for s in list(self.stocks.values()):
|
||||
s.reset(time)
|
||||
|
||||
# Run forever. Dt is in seconds
|
||||
def run(self,dt):
|
||||
for s in self.stocks:
|
||||
self.prices[s] = self.stocks[s].price
|
||||
self.publish(self.stocks[s].make_record())
|
||||
while self.time < 1000:
|
||||
for s in self.stocks:
|
||||
self.stocks[s].incr(dt/60.0) # Increment is in minutes
|
||||
if self.stocks[s].price != self.prices[s]:
|
||||
self.prices[s] = self.stocks[s].price
|
||||
self.publish(self.stocks[s].make_record())
|
||||
time.sleep(dt)
|
||||
self.time += (dt/60.0)
|
||||
|
||||
|
||||
class BasicPrinter(object):
|
||||
def update(self,record):
|
||||
print(csv_record(record))
|
||||
|
||||
class LogPrinter(object):
|
||||
def __init__(self,filename):
|
||||
self.f = open(filename,"w")
|
||||
def update(self,record):
|
||||
self.f.write(csv_record(record)+"\n")
|
||||
self.f.flush()
|
||||
|
||||
m = MarketSimulator()
|
||||
m.add_history(history_file)
|
||||
m.reset(minutes("9:30am"))
|
||||
m.register(BasicPrinter())
|
||||
m.register(LogPrinter("stocklog.csv"))
|
||||
m.run(1)
|
||||
|
||||
|
||||
|
||||
6
Data/words.txt
Normal file
6
Data/words.txt
Normal file
@ -0,0 +1,6 @@
|
||||
look into my eyes
|
||||
look into my eyes
|
||||
the eyes the eyes the eyes
|
||||
not around the eyes
|
||||
don't look around the eyes
|
||||
look into my eyes you're under
|
||||
37
Exercises/README.md
Normal file
37
Exercises/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Advanced Python Mastery
|
||||
|
||||
Copyright (C) 2007-2023
|
||||
David Beazley (dave@dabeaz.com)
|
||||
http://www.dabeaz.com
|
||||
|
||||
Welcome to the Python Mastery course. This
|
||||
directory, `pythonmaster` is where you find support files
|
||||
related to the class exercises. It is also where you will be doing
|
||||
your work.
|
||||
|
||||
This course requires the use of Python 3.6 or newer. If you are
|
||||
using Python 2, most of the material still applies, but you will
|
||||
have to make minor code modifications here and there.
|
||||
|
||||
- link:PythonMastery.pdf[`PythonMastery.pdf`] is a PDF that contains
|
||||
all of the presentation slides.
|
||||
|
||||
- The link:Exercises/index.html[`Exercises/`] folder is where you
|
||||
find all the class exercises.
|
||||
|
||||
- The `Data/` folder is where you find data files, scripts, and
|
||||
other files used by the exercises.
|
||||
|
||||
- The `Solutions/` folder contains complete solution code for
|
||||
various exercises. Each problem has its own directory. For example,
|
||||
the solution to exercise 3.2 can be found in the `Solution/3_2/` directory.
|
||||
|
||||
Every attempt has been made to make sure exercises work. However, it's
|
||||
possible that you will find typos or minor mistakes. If you find any
|
||||
errors, please let me know so that I can fix them for future editions
|
||||
of the course.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
93
Exercises/ex1_1.md
Normal file
93
Exercises/ex1_1.md
Normal file
@ -0,0 +1,93 @@
|
||||
\[ [Index](index.md) | []() | [Exercise 1.2](ex1_2.md) \]
|
||||
|
||||
# Exercise 1.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Make sure Python is installed correctly on your machine
|
||||
- Start the interactive interpreter
|
||||
- Edit and run a small program
|
||||
|
||||
*Files Created:* `art.py`
|
||||
|
||||
## (a) Launch Python
|
||||
|
||||
Start Python3 on your machine. Make sure you can type simple
|
||||
statements such as the "hello world" program:
|
||||
|
||||
```python
|
||||
>>> print('Hello World')
|
||||
Hello World
|
||||
>>>
|
||||
```
|
||||
|
||||
In much of this course, you'll want to make sure you can work from
|
||||
the interactive REPL like this. If you're working from a different
|
||||
environment such as IPython or Jupyter Notebooks, that's fine.
|
||||
|
||||
## (b) Some Generative Art
|
||||
|
||||
Create the following program and put it in a file called `art.py`:
|
||||
|
||||
```python
|
||||
# art.py
|
||||
|
||||
import sys
|
||||
import random
|
||||
|
||||
chars = '\|/'
|
||||
|
||||
def draw(rows, columns):
|
||||
for r in rows:
|
||||
print(''.join(random.choice(chars) for _ in range(columns)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
raise SystemExit("Usage: art.py rows columns")
|
||||
draw(int(sys.argv[1]), int(sys.argv[2]))
|
||||
```
|
||||
|
||||
Make sure you can run this program from the command line or a terminal.
|
||||
|
||||
```
|
||||
bash % python3 art.py 10 20
|
||||
```
|
||||
|
||||
If you run the above command, you'll get a crash and traceback message.
|
||||
Go fix the problem and run the program again. You should get output like
|
||||
this:
|
||||
|
||||
```
|
||||
bash % python3 art.py 10 20
|
||||
||||/\||//\//\|||\|\
|
||||
///||\/||\//|\\|\\/\
|
||||
|\////|//|||\//|/\||
|
||||
|//\||\/|\///|\|\|/|
|
||||
|/|//|/|/|\\/\/\||//
|
||||
|\/\|\//\\//\|\||\\/
|
||||
|||\\\\/\\\|/||||\/|
|
||||
\\||\\\|\||||////\\|
|
||||
//\//|/|\\|\//\|||\/
|
||||
\\\|/\\|/|\\\|/|/\/|
|
||||
bash %
|
||||
```
|
||||
|
||||
### Important Note
|
||||
|
||||
It is absolutely essential that you are able to edit, run, and debug
|
||||
ordinary Python programs for the rest of this course. The choice
|
||||
of editor, IDE, or operating system doesn't matter as long as you
|
||||
are able to experiment interactively and create normal Python source
|
||||
files that can execute from the command line.
|
||||
|
||||
|
||||
\[ [Solution](soln1_1.md) | [Index](index.md) | [Exercise 1.2](ex1_2.md) \]
|
||||
|
||||
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
412
Exercises/ex1_2.md
Normal file
412
Exercises/ex1_2.md
Normal file
@ -0,0 +1,412 @@
|
||||
\[ [Index](index.md) | [Exercise 1.1](ex1_1.md) | [Exercise 1.3](ex1_3.md) \]
|
||||
|
||||
|
||||
# Exercise 1.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Manipulate various built-in Python objects
|
||||
|
||||
*Files Created:* None
|
||||
|
||||
## Part 1 : Numbers
|
||||
|
||||
Numerical calculations work about like you would expect in Python.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> 3 + 4*5
|
||||
23
|
||||
>>> 23.45 / 1e-02
|
||||
2345.0
|
||||
>>>
|
||||
```
|
||||
|
||||
Be aware that integer division is different in Python 2 and Python 3.
|
||||
|
||||
```python
|
||||
>>> 7 / 4 # In python 2, this truncates to 1
|
||||
1.75
|
||||
>>> 7 // 4 # Truncating division
|
||||
1
|
||||
>>>
|
||||
```
|
||||
|
||||
If you want Python 3 behavior in Python 2, do this:
|
||||
|
||||
```python
|
||||
>>> from __future__ import division
|
||||
>>> 7 / 4
|
||||
1.75
|
||||
>>> 7 // 4 # Truncating division
|
||||
1
|
||||
>>>
|
||||
```
|
||||
|
||||
Numbers have a small set of methods, many of which are actually quite
|
||||
recent and overlooked by even experienced Python programmers. Try some of them.
|
||||
|
||||
```python
|
||||
>>> x = 1172.5
|
||||
>>> x.as_integer_ratio()
|
||||
(2345, 2)
|
||||
>>> x.is_integer()
|
||||
False
|
||||
>>> y = 12345
|
||||
>>> y.numerator
|
||||
12345
|
||||
>>> y.denominator
|
||||
1
|
||||
>>> y.bit_length()
|
||||
14
|
||||
>>>
|
||||
```
|
||||
|
||||
## Part 2 : String Manipulation
|
||||
|
||||
Define a string containing a series of stock ticker symbols like this:
|
||||
|
||||
```python
|
||||
>>> symbols = 'AAPL IBM MSFT YHOO SCO'
|
||||
```
|
||||
|
||||
Now, let's experiment with different string operations:
|
||||
|
||||
### (a) Extracting individual characters and substrings
|
||||
|
||||
Strings are arrays of characters. Try extracting a few characters:
|
||||
|
||||
```python
|
||||
>>> symbols[0]
|
||||
'A'
|
||||
>>> symbols[1]
|
||||
'A'
|
||||
>>> symbols[2]
|
||||
'P'
|
||||
>>> symbols[-1] # Last character
|
||||
'O'
|
||||
>>> symbols[-2] # 2nd from last character
|
||||
'C'
|
||||
>>>
|
||||
```
|
||||
|
||||
Try taking a few slices:
|
||||
|
||||
```python
|
||||
>>> symbols[:4]
|
||||
'AAPL'
|
||||
>>> symbols[-3:]
|
||||
'SCO'
|
||||
>>> symbols[5:8]
|
||||
'IBM'
|
||||
>>>
|
||||
```
|
||||
|
||||
### (b) Strings as read-only objects
|
||||
|
||||
Strings are read-only. Verify this by trying to change the first character of `symbols` to a lower-case 'a'.
|
||||
|
||||
```python
|
||||
>>> symbols[0] = 'a'
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: 'str' object does not support item assignment
|
||||
>>>
|
||||
```
|
||||
|
||||
### (c) String concatenation
|
||||
|
||||
Although string data is read-only, you can always reassign a variable to a newly created string.
|
||||
Try the following statement which concatenates a new symbol "GOOG" to the end of `symbols`:
|
||||
|
||||
```python
|
||||
>>> symbols += ' GOOG'
|
||||
>>> symbols
|
||||
... look at the result ...
|
||||
```
|
||||
|
||||
Now, try adding "HPQ" to the beginning of `symbols` like this:
|
||||
|
||||
```python
|
||||
>>> symbols = 'HPQ ' + symbols
|
||||
>>> symbols
|
||||
... look at the result ...
|
||||
```
|
||||
|
||||
It should be noted in both of these examples, the original string `symbols` is _NOT_
|
||||
being modified "in place." Instead, a completely new string is created. The variable name `symbols` is
|
||||
just bound to the result. Afterwards, the old string is destroyed since it's not being used anymore.
|
||||
|
||||
|
||||
### (d) Membership testing (substring testing)
|
||||
|
||||
Experiment with the `in` operator to check for substrings. At
|
||||
the interactive prompt, try these operations:
|
||||
|
||||
```python
|
||||
>>> 'IBM' in symbols
|
||||
True
|
||||
>>> 'AA' in symbols
|
||||
True
|
||||
>>> 'CAT' in symbols
|
||||
False
|
||||
>>>
|
||||
```
|
||||
|
||||
Make sure you understand why the check for "AA" returned `True`.
|
||||
|
||||
### (e) String Methods
|
||||
|
||||
At the Python interactive prompt, try experimenting with some of the
|
||||
string methods.
|
||||
|
||||
```python
|
||||
>>> symbols.lower()
|
||||
'hpq aapl ibm msft yhoo sco goog'
|
||||
>>> symbols
|
||||
'HPQ AAPL IBM MSFT YHOO SCO GOOG'
|
||||
```
|
||||
|
||||
Remember, strings are always read-only. If you want to save the result of an operation, you
|
||||
need to place it in a variable:
|
||||
|
||||
```python
|
||||
>>> lowersyms = symbols.lower()
|
||||
>>> lowersyms
|
||||
'hpq aapl ibm msft yhoo sco goog'
|
||||
>>>
|
||||
```
|
||||
|
||||
Try some more operations:
|
||||
|
||||
```python
|
||||
>>> symbols.find('MSFT')
|
||||
13
|
||||
>>> symbols[13:17]
|
||||
'MSFT'
|
||||
>>> symbols = symbols.replace('SCO','')
|
||||
>>> symbols
|
||||
'HPQ AAPL IBM MSFT YHOO GOOG'
|
||||
>>>
|
||||
```
|
||||
|
||||
## Part 3 : List Manipulation
|
||||
|
||||
In the first part, you worked with strings containing stock symbols. For example:
|
||||
|
||||
```python
|
||||
>>> symbols = 'HPQ AAPL IBM MSFT YHOO GOOG'
|
||||
>>>
|
||||
```
|
||||
|
||||
Define the above variable and split it into a list of names using the `split()` operation of strings:
|
||||
|
||||
```python
|
||||
>>> symlist = symbols.split()
|
||||
>>> symlist
|
||||
['HPQ', 'AAPL', 'IBM', 'MSFT', 'YHOO', 'GOOG' ]
|
||||
>>>
|
||||
```
|
||||
|
||||
### (a) Extracting and reassigning list elements
|
||||
|
||||
Lists work like arrays where you can look up and
|
||||
modify elements by numerical index. Try a few lookups:
|
||||
|
||||
```python
|
||||
>>> symlist[0]
|
||||
'HPQ'
|
||||
>>> symlist[1]
|
||||
'AAPL'
|
||||
>>> symlist[-1]
|
||||
'GOOG'
|
||||
>>> symlist[-2]
|
||||
'YHOO'
|
||||
>>>
|
||||
```
|
||||
|
||||
Try reassigning one of the items:
|
||||
|
||||
```python
|
||||
>>> symlist[2] = 'AIG'
|
||||
>>> symlist
|
||||
['HPQ', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'GOOG' ]
|
||||
>>>
|
||||
```
|
||||
|
||||
### (b) Looping over list items
|
||||
|
||||
The `for` loop works by looping over data in a sequence such as a list. Check this out
|
||||
by typing the following loop and watching what happens:
|
||||
|
||||
```python
|
||||
>>> for s in symlist:
|
||||
print('s =', s)
|
||||
|
||||
... look at the output ...
|
||||
```
|
||||
|
||||
### (c) Membership tests
|
||||
|
||||
Use the `in` operator to check if `'AIG'`,`'AA'`, and `'CAT'` are in the list of symbols.
|
||||
|
||||
```python
|
||||
>>> 'AIG' in symlist
|
||||
True
|
||||
>>> 'AA' in symlist
|
||||
False
|
||||
>>>
|
||||
```
|
||||
|
||||
### (d) Appending, inserting, and deleting items
|
||||
|
||||
Use the `append()` method to add the symbol `'RHT'` to end of `symlist`.
|
||||
|
||||
```python
|
||||
>>> symlist.append('RHT')
|
||||
>>> symlist
|
||||
['HPQ', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'GOOG', 'RHT']
|
||||
>>>
|
||||
```
|
||||
|
||||
Use the `insert()` method to
|
||||
insert the symbol `'AA'` as the second item in the list.
|
||||
|
||||
```python
|
||||
>>> symlist.insert(1,'AA')
|
||||
>>> symlist
|
||||
['HPQ', 'AA', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'GOOG', 'RHT']
|
||||
>>>
|
||||
```
|
||||
|
||||
Use the `remove()` method to remove `'MSFT'` from the list.
|
||||
|
||||
```python
|
||||
>>> symlist.remove('MSFT')
|
||||
>>> symlist
|
||||
['HPQ', 'AA', 'AAPL', 'AIG', 'YHOO', 'GOOG', 'RHT']
|
||||
```
|
||||
|
||||
Try calling `remove()` again to see what happens if the item can't be found.
|
||||
|
||||
```python
|
||||
>>> symlist.remove('MSFT')
|
||||
... watch what happens ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Use the `index()` method to find the position of `'YHOO'` in the list.
|
||||
|
||||
```python
|
||||
>>> symlist.index('YHOO')
|
||||
4
|
||||
>>> symlist[4]
|
||||
'YHOO'
|
||||
>>>
|
||||
```
|
||||
|
||||
### (e) List sorting
|
||||
|
||||
Want to sort a list? Use the `sort()` method. Try it out:
|
||||
|
||||
```python
|
||||
>>> symlist.sort()
|
||||
>>> symlist
|
||||
['AA', 'AAPL', 'AIG', 'GOOG', 'HPQ', 'RHT', 'YHOO']
|
||||
>>>
|
||||
```
|
||||
|
||||
Want to sort in reverse? Try this:
|
||||
|
||||
```python
|
||||
>>> symlist.sort(reverse=True)
|
||||
>>> symlist
|
||||
['YHOO', 'RHT', 'HPQ', 'GOOG', 'AIG', 'AAPL', 'AA']
|
||||
>>>
|
||||
```
|
||||
|
||||
Note: Sorting a list modifies its contents "in-place." That is, the
|
||||
elements of the list are shuffled around, but no new list is created
|
||||
as a result.
|
||||
|
||||
|
||||
### (f) Lists of anything
|
||||
|
||||
Lists can contain any kind of object, including other lists (e.g., nested
|
||||
lists). Try this out:
|
||||
|
||||
```python
|
||||
>>> nums = [101,102,103]
|
||||
>>> items = [symlist, nums]
|
||||
>>> items
|
||||
[['YHOO', 'RHT', 'HPQ', 'GOOG', 'AIG', 'AAPL', 'AA'], [101, 102, 103]]
|
||||
```
|
||||
|
||||
Pay close attention to the above output. `items` is a list
|
||||
with two elements. Each element is list.
|
||||
|
||||
Try some nested list lookups:
|
||||
|
||||
```python
|
||||
>>> items[0]
|
||||
['YHOO', 'RHT', 'HPQ', 'GOOG', 'AIG', 'AAPL', 'AA']
|
||||
>>> items[0][1]
|
||||
'RHT'
|
||||
>>> items[0][1][2]
|
||||
'T'
|
||||
>>> items[1]
|
||||
[101, 102, 103]
|
||||
>>> items[1][1]
|
||||
102
|
||||
>>>
|
||||
```
|
||||
|
||||
## Part 4 : Dictionaries
|
||||
|
||||
In last few parts, you've simply worked with stock symbols. However,
|
||||
suppose you wanted to map stock symbols to other data such as the
|
||||
price? Use a dictionary:
|
||||
|
||||
```python
|
||||
>>> prices = { 'IBM': 91.1, 'GOOG': 490.1, 'AAPL':312.23 }
|
||||
>>>
|
||||
```
|
||||
|
||||
A dictionary maps keys to values. Here's how to access:
|
||||
|
||||
```python
|
||||
>>> prices['IBM']
|
||||
91.1
|
||||
>>> prices['IBM'] = 123.45
|
||||
>>> prices['HPQ'] = 26.15
|
||||
>>> prices
|
||||
{'GOOG': 490.1, 'AAPL': 312.23, 'IBM': 123.45, 'HPQ': 26.15}
|
||||
>>>
|
||||
```
|
||||
|
||||
To get a list of keys, use this:
|
||||
|
||||
```python
|
||||
>>> list(prices)
|
||||
['GOOG', 'AAPL', 'IBM', 'HPQ']
|
||||
>>>
|
||||
```
|
||||
|
||||
To delete a value, use `del`
|
||||
|
||||
```python
|
||||
>>> del prices['AAPL']
|
||||
>>> prices
|
||||
{'GOOG': 490.1, 'IBM': 123.45, 'HPQ': 26.15}
|
||||
>>>
|
||||
```
|
||||
|
||||
|
||||
\[ [Solution](soln1_2.md) | [Index](index.md) | [Exercise 1.1](ex1_1.md) | [Exercise 1.3](ex1_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
41
Exercises/ex1_3.md
Normal file
41
Exercises/ex1_3.md
Normal file
@ -0,0 +1,41 @@
|
||||
\[ [Index](index.md) | [Exercise 1.2](ex1_2.md) | [Exercise 1.4](ex1_4.md) \]
|
||||
|
||||
# Exercise 1.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Review basic file I/O
|
||||
|
||||
*Files Created:* `pcost.py`
|
||||
|
||||
## (a) Working with files
|
||||
|
||||
The file `Data/portfolio.dat` contains a list of lines with information
|
||||
on a portfolio of stocks. The file looks like this:
|
||||
|
||||
```
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
```
|
||||
|
||||
The first column is the stock name, the second column is the number of
|
||||
shares, and the third column is the purchase price of a single share.
|
||||
|
||||
Write a program called `pcost.py` that opens this file, reads
|
||||
all lines, and calculates how much it cost to purchase all of the shares
|
||||
in the portfolio. To do this, compute the sum of the second column
|
||||
multiplied by the third column.
|
||||
|
||||
\[ [Solution](soln1_3.md) | [Index](index.md) | [Exercise 1.2](ex1_2.md) | [Exercise 1.4](ex1_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
87
Exercises/ex1_4.md
Normal file
87
Exercises/ex1_4.md
Normal file
@ -0,0 +1,87 @@
|
||||
\[ [Index](index.md) | [Exercise 1.3](ex1_3.md) | [Exercise 1.5](ex1_5.md) \]
|
||||
|
||||
# Exercise 1.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Review of how to define simple functions
|
||||
- Exception handling
|
||||
|
||||
*Files Created:* None
|
||||
|
||||
*Files Modified:* `pcost.py`
|
||||
|
||||
## (a) Defining a function
|
||||
|
||||
Take the program `pcost.py` that you wrote in the last exercise and
|
||||
convert it into a function `portfolio_cost(filename)` that takes a
|
||||
filename as input, reads the portfolio data in that file, and returns
|
||||
the total cost of the portfolio as a floating point number. Once you
|
||||
written the function, have your program call the function by simply
|
||||
adding this statement at the end:
|
||||
|
||||
```python
|
||||
print(portfolio_cost('Data/portfolio.dat'))
|
||||
```
|
||||
|
||||
Run your program and make sure it produces the same output as
|
||||
before.
|
||||
|
||||
|
||||
## (b) Adding Error Handling
|
||||
|
||||
When writing programs that process data, it is common to encounter
|
||||
errors related to bad data (malformed, missing fields, etc.). Modify
|
||||
your `pcost.py` program to read the data file `Data/portfolio3.dat`
|
||||
and run it (hint: it should crash).
|
||||
|
||||
Modify your function slightly so that it is able to recover from lines
|
||||
with bad data. For example, the conversion functions `int()` and
|
||||
`float()` raise a `ValueError` exception if they can't convert the
|
||||
input. Use `try` and `except` to catch and print a warning message
|
||||
about lines that can't be parsed. For example:
|
||||
|
||||
```
|
||||
Couldn't parse: 'C - 53.08\n'
|
||||
Reason: invalid literal for int() with base 10: '-'
|
||||
Couldn't parse: 'DIS - 34.20\n'
|
||||
Reason: invalid literal for int() with base 10: '-'
|
||||
...
|
||||
```
|
||||
|
||||
Try running your program on the `Data/portfolio3.dat` file
|
||||
again. It should run successfully despite printed warning messages.
|
||||
|
||||
## (c) Interactive Experimentation
|
||||
|
||||
Run your `pcost.py` program and call the
|
||||
`portfolio_cost()` function directly from the interactive
|
||||
interpreter.
|
||||
|
||||
```python
|
||||
>>> portfolio_cost('Data/portfolio.dat')
|
||||
44671.15
|
||||
>>> portfolio_cost('Data/portfolio2.dat')
|
||||
19908.75
|
||||
>>>
|
||||
```
|
||||
|
||||
Note: To do this, you might have to run python using the `-i`
|
||||
option. For example:
|
||||
|
||||
```
|
||||
bash % python3 -i pcost.py
|
||||
```
|
||||
|
||||
We are going to be writing a lot of programs where you define
|
||||
functions and experiment interactively. Make sure you know how to do
|
||||
this.
|
||||
|
||||
\[ [Solution](soln1_4.md) | [Index](index.md) | [Exercise 1.3](ex1_3.md) | [Exercise 1.5](ex1_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
53
Exercises/ex1_5.md
Normal file
53
Exercises/ex1_5.md
Normal file
@ -0,0 +1,53 @@
|
||||
\[ [Index](index.md) | [Exercise 1.4](ex1_4.md) | [Exercise 1.6](ex1_6.md) \]
|
||||
|
||||
# Exercise 1.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Review of how to define a simple object
|
||||
|
||||
*Files Created:* `stock.py`
|
||||
|
||||
## (a) Defining a simple object
|
||||
|
||||
Create a file `stock.py` and define the following class:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
```
|
||||
|
||||
Once you have done this, run your program and experiment with your new
|
||||
`Stock` object:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG',100,490.10)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.price
|
||||
490.1
|
||||
>>> s.cost()
|
||||
49010.0
|
||||
>>> print('%10s %10d %10.2f' % (s.name, s.shares, s.price))
|
||||
GOOG 100 490.10
|
||||
>>> t = Stock('IBM', 50, 91.5)
|
||||
>>> t.cost()
|
||||
4575.0
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln1_5.md) | [Index](index.md) | [Exercise 1.4](ex1_4.md) | [Exercise 1.6](ex1_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
85
Exercises/ex1_6.md
Normal file
85
Exercises/ex1_6.md
Normal file
@ -0,0 +1,85 @@
|
||||
\[ [Index](index.md) | [Exercise 1.5](ex1_5.md) | [Exercise 2.1](ex2_1.md) \]
|
||||
|
||||
# Exercise 1.6
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Defining modules
|
||||
- Using the import statement
|
||||
|
||||
*Files Created:* None
|
||||
|
||||
**Note:**
|
||||
For this exercise involving modules, it is
|
||||
critically important to make sure you are running Python in a proper
|
||||
environment. You may need to check the value of `sys.path` if you
|
||||
can't get import statements to work. Ask for assistance if everything
|
||||
seems broken.
|
||||
|
||||
Before starting this exercise, first restart your Python interpreter session. If using IDLE, click on
|
||||
the shell window and look for a menu option "Shell > Restart Shell". You should get a message like this:
|
||||
|
||||
```python
|
||||
>>> ##################== RESTART ##################==
|
||||
>>>
|
||||
```
|
||||
|
||||
If you are using Unix, simply exit Python and restart the interpreter.
|
||||
|
||||
## (a) Using the import statement
|
||||
|
||||
In previous exercises, you wrote two programs `pcost.py` and
|
||||
`stock.py`. Use the `import` statement to load these
|
||||
programs and use their functionality:
|
||||
|
||||
```python
|
||||
>>> import pcost
|
||||
44671.15
|
||||
>>> pcost.portfolio_cost('Data/portfolio2.dat')
|
||||
19908.75
|
||||
>>> from stock import Stock
|
||||
>>> s = Stock('GOOG', 100, 490.10)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.cost()
|
||||
49010.0
|
||||
>>>
|
||||
```
|
||||
|
||||
If you can't get the above statements to work, you might have placed
|
||||
your programs in a funny directory. Make sure you are running Python
|
||||
in the same directory as your files or that the directory is included
|
||||
on `sys.path`.
|
||||
|
||||
|
||||
## (b) Main Module
|
||||
|
||||
In your `pcost.py` program, the last statement called a
|
||||
function and printed out the result. Modify the program so that this
|
||||
step only occurs if the program is run as the main program. Now,
|
||||
try running the program two ways:
|
||||
|
||||
First, run the program as main:
|
||||
|
||||
```
|
||||
bash % python3 pcost.py
|
||||
44671.25
|
||||
bash %
|
||||
```
|
||||
|
||||
Next, run the program as a library import. You should not see any
|
||||
output.
|
||||
|
||||
```python
|
||||
>>> import pcost
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln1_6.md) | [Index](index.md) | [Exercise 1.5](ex1_5.md) | [Exercise 2.1](ex2_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
195
Exercises/ex2_1.md
Normal file
195
Exercises/ex2_1.md
Normal file
@ -0,0 +1,195 @@
|
||||
\[ [Index](index.md) | [Exercise 1.6](ex1_6.md) | [Exercise 2.2](ex2_2.md) \]
|
||||
|
||||
# Exercise 2.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Figure out the most memory-efficient way to store a lot of data.
|
||||
- Learn about different ways of representing records including tuples,
|
||||
dictionaries, classes, and named tuples.
|
||||
|
||||
In this exercise, we look at different choices for representing data
|
||||
structures with an eye towards memory use and efficiency. A lot of
|
||||
people use Python to perform various kinds of data analysis so knowing
|
||||
about different options and their tradeoffs is useful information.
|
||||
|
||||
## (a) Stuck on the bus
|
||||
|
||||
The file `Data/ctabus.csv` is a CSV file containing
|
||||
daily ridership data for the Chicago Transit Authority (CTA) bus
|
||||
system from January 1, 2001 to August 31, 2013. It contains
|
||||
approximately 577000 rows of data. Use Python to view a few lines
|
||||
of data to see what it looks like:
|
||||
|
||||
```python
|
||||
>>> f = open('Data/ctabus.csv')
|
||||
>>> next(f)
|
||||
'route,date,daytype,rides\n'
|
||||
>>> next(f)
|
||||
'3,01/01/2001,U,7354\n'
|
||||
>>> next(f)
|
||||
'4,01/01/2001,U,9288\n'
|
||||
>>>
|
||||
```
|
||||
|
||||
There are 4 columns of data.
|
||||
|
||||
- route: Column 0. The bus route name.
|
||||
- date: Column 1. A date string of the form MM/DD/YYYY.
|
||||
- daytype: Column 2. A day type code (U=Sunday/Holiday, A=Saturday, W=Weekday)
|
||||
- rides: Column 3. Total number of riders (integer)
|
||||
|
||||
The `rides` column records the total number of people who boarded a
|
||||
bus on that route on a given day. Thus, from the example, 7354 people
|
||||
rode the number 3 bus on January 1, 2001.
|
||||
|
||||
## (b) Basic memory use of text
|
||||
|
||||
Let's get a baseline of the memory required to work with this
|
||||
datafile. First, restart Python and try a very simple experiment of
|
||||
simply grabbing the file and storing its data in a single string:
|
||||
|
||||
```python
|
||||
>>> # --- RESTART
|
||||
>>> import tracemalloc
|
||||
>>> f = open('Data/ctabus.csv')
|
||||
>>> tracemalloc.start()
|
||||
>>> data = f.read()
|
||||
>>> len(data)
|
||||
12361039
|
||||
>>> current, peak = tracemalloc.get_traced_memory()
|
||||
>>> current
|
||||
12369664
|
||||
>>> peak
|
||||
24730766
|
||||
>>>
|
||||
```
|
||||
|
||||
Your results might vary somewhat, but you should see current
|
||||
memory use in the range of 12MB with a peak of 24MB.
|
||||
|
||||
What happens if you read the entire file into a list of strings
|
||||
instead? Restart Python and try this:
|
||||
|
||||
```python
|
||||
>>> # --- RESTART
|
||||
>>> import tracemalloc
|
||||
>>> f = open('Data/ctabus.csv')
|
||||
>>> tracemalloc.start()
|
||||
>>> lines = f.readlines()
|
||||
>>> len(lines)
|
||||
577564
|
||||
>>> current, peak = tracemalloc.get_traced_memory()
|
||||
>>> current
|
||||
45828030
|
||||
>>> peak
|
||||
45867371
|
||||
>>>
|
||||
```
|
||||
|
||||
You should see the memory use go up significantly into the range of 40-50MB.
|
||||
Point to ponder: what might be the source of that extra overhead?
|
||||
|
||||
## (c) A List of Tuples
|
||||
|
||||
In practice, you might read the data into a list and convert each line
|
||||
into some other data structure. Here is a program `readrides.py` that
|
||||
reads the entire file into a list of tuples using the `csv` module:
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
|
||||
import csv
|
||||
|
||||
def read_rides_as_tuples(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of tuples
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = (route, date, daytype, rides)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
if __name__ == '__main__':
|
||||
import tracemalloc
|
||||
tracemalloc.start()
|
||||
rows = read_rides_as_tuples('Data/ctabus.csv')
|
||||
print('Memory Use: Current %d, Peak %d' % tracemalloc.get_traced_memory())
|
||||
```
|
||||
|
||||
Run this program using `python3 -i readrides.py` and look at the
|
||||
resulting contents of `rows`. You should get a list of tuples like
|
||||
this:
|
||||
|
||||
```python
|
||||
>>> len(rows)
|
||||
577563
|
||||
>>> rows[0]
|
||||
('3', '01/01/2001', 'U', 7354)
|
||||
>>> rows[1]
|
||||
('4', '01/01/2001', 'U', 9288)
|
||||
```
|
||||
|
||||
Look at the resulting memory use. It should be substantially higher
|
||||
than in part (b).
|
||||
|
||||
## (d) Memory Use of Other Data Structures
|
||||
|
||||
Python has many different choices for representing data structures.
|
||||
For example:
|
||||
|
||||
```python
|
||||
# A tuple
|
||||
row = (route, date, daytype, rides)
|
||||
|
||||
# A dictionary
|
||||
row = {
|
||||
'route': route,
|
||||
'date': date,
|
||||
'daytype': daytype,
|
||||
'rides': rides,
|
||||
}
|
||||
|
||||
# A class
|
||||
class Row:
|
||||
def __init__(self, route, date, daytype, rides):
|
||||
self.route = route
|
||||
self.date = date
|
||||
self.daytype = daytype
|
||||
self.rides = rides
|
||||
|
||||
# A named tuple
|
||||
from collections import namedtuple
|
||||
Row = namedtuple('Row', ['route', 'date', 'daytype', 'rides'])
|
||||
|
||||
# A class with __slots__
|
||||
class Row:
|
||||
__slots__ = ['route', 'date', 'daytype', 'rides']
|
||||
def __init__(self, route, date, daytype, rides):
|
||||
self.route = route
|
||||
self.date = date
|
||||
self.daytype = daytype
|
||||
self.rides = rides
|
||||
```
|
||||
Your task is as follows: Create different versions of the `read_rides()` function
|
||||
that use each of these data structures to represent a single row of data.
|
||||
Then, find out the resulting memory use of each option. Find out which
|
||||
approach offers the most efficient storage if you were working with a lot
|
||||
of data all at once.
|
||||
|
||||
\[ [Solution](soln2_1.md) | [Index](index.md) | [Exercise 1.6](ex1_6.md) | [Exercise 2.2](ex2_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
192
Exercises/ex2_2.md
Normal file
192
Exercises/ex2_2.md
Normal file
@ -0,0 +1,192 @@
|
||||
\[ [Index](index.md) | [Exercise 2.1](ex2_1.md) | [Exercise 2.3](ex2_3.md) \]
|
||||
|
||||
# Exercise 2.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Work with various containers
|
||||
- List/Set/Dict Comprehensions
|
||||
- Collections module
|
||||
- Data analysis challenge
|
||||
|
||||
Most Python programmers are generally familiar with lists, dictionaries,
|
||||
tuples, and other basic datatypes. In this exercise, we'll put that
|
||||
knowledge to work to solve various data analysis problems.
|
||||
|
||||
## (a) Preliminaries
|
||||
|
||||
To get started, let's review some basics with a slightly simpler dataset--
|
||||
a portfolio of stock holdings. Create a file `readport.py` and put this
|
||||
code in it:
|
||||
|
||||
```python
|
||||
# readport.py
|
||||
|
||||
import csv
|
||||
|
||||
# A function that reads a file into a list of dicts
|
||||
def read_portfolio(filename):
|
||||
portfolio = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = {
|
||||
'name' : row[0],
|
||||
'shares' : int(row[1]),
|
||||
'price' : float(row[2])
|
||||
}
|
||||
portfolio.append(record)
|
||||
return portfolio
|
||||
```
|
||||
|
||||
This file reads some simple stock market data in the file `Data/portfolio.csv`. Use
|
||||
the function to read the file and look at the results:
|
||||
|
||||
```python
|
||||
>>> portfolio = read_portfolio('Data/portfolio.csv')
|
||||
>>> from pprint import pprint
|
||||
>>> pprint(portfolio)
|
||||
[{'name': 'AA', 'price': 32.2, 'shares': 100},
|
||||
{'name': 'IBM', 'price': 91.1, 'shares': 50},
|
||||
{'name': 'CAT', 'price': 83.44, 'shares': 150},
|
||||
{'name': 'MSFT', 'price': 51.23, 'shares': 200},
|
||||
{'name': 'GE', 'price': 40.37, 'shares': 95},
|
||||
{'name': 'MSFT', 'price': 65.1, 'shares': 50},
|
||||
{'name': 'IBM', 'price': 70.44, 'shares': 100}]
|
||||
>>>
|
||||
```
|
||||
|
||||
In this data, each row consists of a stock name, a number of held
|
||||
shares, and a purchase price. There are multiple entries for
|
||||
certain stock names such as MSFT and IBM.
|
||||
|
||||
## (b) Comprehensions
|
||||
|
||||
List, set, and dictionary comprehensions can be a useful tool for manipulating
|
||||
data. For example, try these operations:
|
||||
|
||||
```python
|
||||
>>> # Find all holdings more than 100 shares
|
||||
>>> [s for s in portfolio if s['shares'] > 100]
|
||||
[{'name': 'CAT', 'shares': 150, 'price': 83.44},
|
||||
{'name': 'MSFT', 'shares': 200, 'price': 51.23}]
|
||||
|
||||
>>> # Compute total cost (shares * price)
|
||||
>>> sum([s['shares']*s['price'] for s in portfolio])
|
||||
44671.15
|
||||
>>>
|
||||
|
||||
>>> # Find all unique stock names (set)
|
||||
>>> { s['name'] for s in portfolio }
|
||||
{'MSFT', 'IBM', 'AA', 'GE', 'CAT'}
|
||||
>>>
|
||||
|
||||
>>> # Count the total shares of each of stock
|
||||
>>> totals = { s['name']: 0 for s in portfolio }
|
||||
>>> for s in portfolio:
|
||||
totals[s['name']] += s['shares']
|
||||
|
||||
>>> totals
|
||||
{'AA': 100, 'IBM': 150, 'CAT': 150, 'MSFT': 250, 'GE': 95}
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Collections
|
||||
|
||||
The `collections` module has a variety of classes for more specialized data
|
||||
manipulation. For example, the last example could be solved with a `Counter` like this:
|
||||
|
||||
```python
|
||||
>>> from collections import Counter
|
||||
>>> totals = Counter()
|
||||
>>> for s in portfolio:
|
||||
totals[s['name']] += s['shares']
|
||||
|
||||
>>> totals
|
||||
Counter({'MSFT': 250, 'IBM': 150, 'CAT': 150, 'AA': 100, 'GE': 95})
|
||||
>>>
|
||||
```
|
||||
|
||||
Counters are interesting in that they support other kinds of operations such as ranking
|
||||
and mathematics. For example:
|
||||
|
||||
```python
|
||||
>>> # Get the two most common holdings
|
||||
>>> totals.most_common(2)
|
||||
[('MSFT', 250), ('IBM', 150)]
|
||||
>>>
|
||||
|
||||
>>> # Adding counters together
|
||||
>>> more = Counter()
|
||||
>>> more['IBM'] = 75
|
||||
>>> more['AA'] = 200
|
||||
>>> more['ACME'] = 30
|
||||
>>> more
|
||||
Counter({'AA': 200, 'IBM': 75, 'ACME': 30})
|
||||
>>> totals
|
||||
Counter({'MSFT': 250, 'IBM': 150, 'CAT': 150, 'AA': 100, 'GE': 95})
|
||||
>>> totals + more
|
||||
Counter({'AA': 300, 'MSFT': 250, 'IBM': 225, 'CAT': 150, 'GE': 95, 'ACME': 30})
|
||||
>>>
|
||||
```
|
||||
|
||||
The `defaultdict` object can be used to group data. For example, suppose
|
||||
you want to make it easy to find all matching entries for a given name such as
|
||||
IBM. Try this:
|
||||
|
||||
```python
|
||||
>>> from collections import defaultdict
|
||||
>>> byname = defaultdict(list)
|
||||
>>> for s in portfolio:
|
||||
byname[s['name']].append(s)
|
||||
|
||||
>>> byname['IBM']
|
||||
[{'name': 'IBM', 'shares': 50, 'price': 91.1}, {'name': 'IBM', 'shares': 100, 'price': 70.44}]
|
||||
>>> byname['AA']
|
||||
[{'name': 'AA', 'shares': 100, 'price': 32.2}]
|
||||
>>>
|
||||
```
|
||||
|
||||
The key feature that makes this work is that a defaultdict
|
||||
automatically initializes elements for you--allowing an insertion of a
|
||||
new element and an `append()` operation to be combined together.
|
||||
|
||||
## (c) Data Analysis Challenge
|
||||
|
||||
In the last exercise you just wrote some code to read CSV-data related
|
||||
to the Chicago Transit Authority. For example, you can grab the data
|
||||
as dictionaries like this:
|
||||
|
||||
```python
|
||||
>>> import readrides
|
||||
>>> rows = readrides.read_rides_as_dicts('Data/ctabus.csv')
|
||||
>>>
|
||||
```
|
||||
|
||||
It would be a shame to do all of that work and then do nothing with
|
||||
the data.
|
||||
|
||||
In this exercise, you task is this: write a program to answer the
|
||||
following three questions:
|
||||
|
||||
1. How many bus routes exist in Chicago?
|
||||
|
||||
2. How many people rode the number 22 bus on February 2, 2011? What about any route on any date of your choosing?
|
||||
|
||||
3. What is the total number of rides taken on each bus route?
|
||||
|
||||
4. What five bus routes had the greatest ten-year increase in ridership from 2001 to 2011?
|
||||
|
||||
You are free to use any technique whatsoever to answer the above
|
||||
questions as long as it's part of the Python standard library (i.e.,
|
||||
built-in datatypes, standard library modules, etc.).
|
||||
|
||||
\[ [Solution](soln2_2.md) | [Index](index.md) | [Exercise 2.1](ex2_1.md) | [Exercise 2.3](ex2_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
365
Exercises/ex2_3.md
Normal file
365
Exercises/ex2_3.md
Normal file
@ -0,0 +1,365 @@
|
||||
\[ [Index](index.md) | [Exercise 2.2](ex2_2.md) | [Exercise 2.4](ex2_4.md) \]
|
||||
|
||||
# Exercise 2.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Iterate like a pro
|
||||
|
||||
*Files Modified:* None.
|
||||
|
||||
Iteration is an essential Python skill. In this exercise, we look at
|
||||
a number of common iteration idioms.
|
||||
|
||||
Start the exercise by grabbing some rows of data from a CSV file.
|
||||
|
||||
```python
|
||||
>>> import csv
|
||||
>>> f = open('Data/portfolio.csv')
|
||||
>>> f_csv = csv.reader(f)
|
||||
>>> headers = next(f_csv)
|
||||
>>> headers
|
||||
['name', 'shares', 'price']
|
||||
>>> rows = list(f_csv)
|
||||
>>> from pprint import pprint
|
||||
>>> pprint(rows)
|
||||
[['AA', '100', '32.20'],
|
||||
['IBM', '50', '91.10'],
|
||||
['CAT', '150', '83.44'],
|
||||
['MSFT', '200', '51.23'],
|
||||
['GE', '95', '40.37'],
|
||||
['MSFT', '50', '65.10'],
|
||||
['IBM', '100', '70.44']]
|
||||
>>>
|
||||
```
|
||||
|
||||
## (a) Basic Iteration and Unpacking
|
||||
|
||||
The `for` statement iterates over any sequence of data. For example:
|
||||
|
||||
```python
|
||||
>>> for row in rows:
|
||||
print(row)
|
||||
|
||||
['AA', '100', '32.20']
|
||||
['IBM', '50', '91.10']
|
||||
['CAT', '150', '83.44']
|
||||
['MSFT', '200', '51.23']
|
||||
['GE', '95', '40.37']
|
||||
['MSFT', '50', '65.10']
|
||||
['IBM', '100', '70.44']
|
||||
>>>
|
||||
```
|
||||
|
||||
Unpack the values into separate variables if you need to:
|
||||
|
||||
```python
|
||||
>>> for name, shares, price in rows:
|
||||
print(name, shares, price)
|
||||
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
It's somewhat common to use `_` or `__` as a throw-away variable if you don't care
|
||||
about one or more of the values. For example:
|
||||
|
||||
```python
|
||||
>>> for name, _, price in rows:
|
||||
print(name, price)
|
||||
|
||||
AA 32.20
|
||||
IBM 91.10
|
||||
CAT 83.44
|
||||
MSFT 51.23
|
||||
GE 40.37
|
||||
MSFT 65.10
|
||||
IBM 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
If you don't know how many values are being unpacked, you can use `*` as a wildcard.
|
||||
Try this experiment in grouping the data by name:
|
||||
|
||||
```python
|
||||
>>> from collections import defaultdict
|
||||
>>> byname = defaultdict(list)
|
||||
>>> for name, *data in rows:
|
||||
byname[name].append(data)
|
||||
|
||||
>>> byname['IBM']
|
||||
[['50', '91.10'], ['100', '70.44']]
|
||||
>>> byname['CAT']
|
||||
[['150', '83.44']]
|
||||
>>> for shares, price in byname['IBM']:
|
||||
print(shares, price)
|
||||
|
||||
50 91.10
|
||||
100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Counting with enumerate()
|
||||
|
||||
`enumerate()` is a useful function if you ever need to keep a counter
|
||||
or index while iterating. For example, suppose you wanted an extra row
|
||||
number:
|
||||
|
||||
```python
|
||||
>>> for rowno, row in enumerate(rows):
|
||||
print(rowno, row)
|
||||
|
||||
0 ['AA', '100', '32.20']
|
||||
1 ['IBM', '50', '91.10']
|
||||
2 ['CAT', '150', '83.44']
|
||||
3 ['MSFT', '200', '51.23']
|
||||
4 ['GE', '95', '40.37']
|
||||
5 ['MSFT', '50', '65.10']
|
||||
6 ['IBM', '100', '70.44']
|
||||
>>>
|
||||
```
|
||||
|
||||
You can combine this with unpacking if you're careful about how you structure it:
|
||||
|
||||
```python
|
||||
>>> for rowno, (name, shares, price) in enumerate(rows):
|
||||
print(rowno, name, shares, price)
|
||||
|
||||
0 AA 100 32.20
|
||||
1 IBM 50 91.10
|
||||
2 CAT 150 83.44
|
||||
3 MSFT 200 51.23
|
||||
4 GE 95 40.37
|
||||
5 MSFT 50 65.10
|
||||
6 IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Using the zip() function
|
||||
|
||||
The `zip()` function is most commonly used to pair data. For example,
|
||||
recall that you created a `headers` variable:
|
||||
|
||||
```python
|
||||
>>> headers
|
||||
['name', 'shares', 'price']
|
||||
>>>
|
||||
```
|
||||
|
||||
This might be useful to combine with the other row data:
|
||||
|
||||
```python
|
||||
>>> row = rows[0]
|
||||
>>> row
|
||||
['AA', '100', '32.20']
|
||||
>>> for col, val in zip(headers, row):
|
||||
print(col, val)
|
||||
|
||||
name AA
|
||||
shares 100
|
||||
price 32.20
|
||||
>>>
|
||||
```
|
||||
|
||||
Or maybe you can use it to make a dictionary:
|
||||
|
||||
```python
|
||||
>>> dict(zip(headers, row))
|
||||
{'name': 'AA', 'shares': '100', 'price': '32.20'}
|
||||
>>>
|
||||
```
|
||||
|
||||
Or maybe a sequence of dictionaries:
|
||||
|
||||
```python
|
||||
>>> for row in rows:
|
||||
record = dict(zip(headers, row))
|
||||
print(record)
|
||||
|
||||
{'name': 'AA', 'shares': '100', 'price': '32.20'}
|
||||
{'name': 'IBM', 'shares': '50', 'price': '91.10'}
|
||||
{'name': 'CAT', 'shares': '150', 'price': '83.44'}
|
||||
{'name': 'MSFT', 'shares': '200', 'price': '51.23'}
|
||||
{'name': 'GE', 'shares': '95', 'price': '40.37'}
|
||||
{'name': 'MSFT', 'shares': '50', 'price': '65.10'}
|
||||
{'name': 'IBM', 'shares': '100', 'price': '70.44'}
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Generator Expressions
|
||||
|
||||
A generator expression is almost exactly the same as a list
|
||||
comprehension except that it does not create a list. Instead, it
|
||||
creates an object that produces the results incrementally--typically
|
||||
for consumption by iteration. Try a simple example:
|
||||
|
||||
```python
|
||||
>>> nums = [1,2,3,4,5]
|
||||
>>> squares = (x*x for x in nums)
|
||||
>>> squares
|
||||
<generator object <genexpr> at 0x37caa8>
|
||||
>>> for n in squares:
|
||||
print(n)
|
||||
|
||||
1
|
||||
4
|
||||
9
|
||||
16
|
||||
25
|
||||
>>>
|
||||
```
|
||||
|
||||
You will notice that a generator expression can only be used once.
|
||||
Watch what happens if you do the for-loop again:
|
||||
|
||||
```python
|
||||
>>> for n in squares:
|
||||
print(n)
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
You can manually get the results one-at-a-time if you use the
|
||||
`next()` function. Try this:
|
||||
|
||||
```python
|
||||
>>> squares = (x*x for x in nums)
|
||||
>>> next(squares)
|
||||
1
|
||||
>>> next(squares)
|
||||
4
|
||||
>>> next(squares)
|
||||
9
|
||||
>>>
|
||||
```
|
||||
|
||||
Keeping typing `next()` to see what happens when there is no
|
||||
more data.
|
||||
|
||||
If the task you are performing is more complicated, you can
|
||||
still take advantage of generators by writing a generator function
|
||||
and using the `yield` statement instead.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> def squares(nums):
|
||||
for x in nums:
|
||||
yield x*x
|
||||
|
||||
>>> for n in squares(nums):
|
||||
print(n)
|
||||
|
||||
1
|
||||
4
|
||||
9
|
||||
16
|
||||
25
|
||||
>>>
|
||||
```
|
||||
|
||||
We'll return to generator functions a little later in the course--for now,
|
||||
just view such functions as having the interesting property of feeding
|
||||
values to the `for`-statement.
|
||||
|
||||
## (e) Generator Expressions and Reduction Functions
|
||||
|
||||
Generator expressions are especially useful for feeding data into
|
||||
functions such as `sum()`, `min()`, `max()`,
|
||||
`any()`, etc. Try some examples using the portfolio data from
|
||||
earlier. Carefully observe that these examples are missing some
|
||||
extra square brackets ([]) that appeared when using list comprehensions.
|
||||
|
||||
```python
|
||||
>>> from readport import read_portfolio
|
||||
>>> portfolio = read_portfolio('Data/portfolio.csv')
|
||||
>>> sum(s['shares']*s['price'] for s in portfolio)
|
||||
44671.15
|
||||
>>> min(s['shares'] for s in portfolio)
|
||||
50
|
||||
>>> any(s['name'] == 'IBM' for s in portfolio)
|
||||
True
|
||||
>>> all(s['name'] == 'IBM' for s in portfolio)
|
||||
False
|
||||
>>> sum(s['shares'] for s in portfolio if s['name'] == 'IBM')
|
||||
150
|
||||
>>>
|
||||
```
|
||||
|
||||
Here is an subtle use of a generator expression in making comma
|
||||
separated values:
|
||||
|
||||
```python
|
||||
>>> s = ('GOOG',100,490.10)
|
||||
>>> ','.join(s)
|
||||
... observe that it fails ...
|
||||
>>> ','.join(str(x) for x in s) # This works
|
||||
'GOOG,100,490.1'
|
||||
>>>
|
||||
```
|
||||
|
||||
The syntax in the above examples takes some getting used to, but the
|
||||
critical point is that none of the operations ever create a fully
|
||||
populated list of results. This gives you a big memory savings. However,
|
||||
you do need to make sure you don't go overboard with the syntax.
|
||||
|
||||
## (f) Saving a lot of memory
|
||||
|
||||
In link:ex2_1.html[Exercise 2.1] you wrote a function
|
||||
`read_rides_as_dicts()` that read the CTA bus data into a list of
|
||||
dictionaries. Using it requires a lot of memory. For example,
|
||||
let's find the day on which the route 22 bus had the greatest
|
||||
ridership:
|
||||
|
||||
```python
|
||||
>>> import tracemalloc
|
||||
>>> tracemalloc.start()
|
||||
>>> import readrides
|
||||
>>> rows = readrides.read_rides_as_dicts('Data/ctabus.csv')
|
||||
>>> rt22 = [row for row in rows if row['route'] == '22']
|
||||
>>> max(rt22, key=lambda row: row['rides'])
|
||||
{'date': '06/11/2008', 'route': '22', 'daytype': 'W', 'rides': 26896}
|
||||
>>> tracemalloc.get_traced_memory()
|
||||
... look at result. Should be around 220MB
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, let's try an example involving generators. Restart Python
|
||||
and try this:
|
||||
|
||||
```python
|
||||
>>> # RESTART
|
||||
>>> import tracemalloc
|
||||
>>> tracemalloc.start()
|
||||
>>> import csv
|
||||
>>> f = open('Data/ctabus.csv')
|
||||
>>> f_csv = csv.reader(f)
|
||||
>>> headers = next(f_csv)
|
||||
>>> rows = (dict(zip(headers,row)) for row in f_csv)
|
||||
>>> rt22 = (row for row in rows if row['route'] == '22')
|
||||
>>> max(rt22, key=lambda row: int(row['rides']))
|
||||
{'date': '06/11/2008', 'route': '22', 'daytype': 'W', 'rides': 26896}
|
||||
>>> tracemalloc.get_traced_memory()
|
||||
... look at result. Should be a LOT smaller than before
|
||||
>>>
|
||||
```
|
||||
|
||||
Keep in mind that you just processed the entire dataset as if it was
|
||||
stored as a sequence of dictionaries. Yet, nowhere did you actually
|
||||
create and store a list of dictionaries. Not all problems can be
|
||||
structured in this way, but if you can work with data in an
|
||||
iterative manner, generator expressions can save a huge amount of memory.
|
||||
|
||||
\[ [Solution](soln2_3.md) | [Index](index.md) | [Exercise 2.2](ex2_2.md) | [Exercise 2.4](ex2_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
449
Exercises/ex2_4.md
Normal file
449
Exercises/ex2_4.md
Normal file
@ -0,0 +1,449 @@
|
||||
\[ [Index](index.md) | [Exercise 2.3](ex2_3.md) | [Exercise 2.5](ex2_5.md) \]
|
||||
|
||||
# Exercise 2.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Make a new primitive type
|
||||
|
||||
In most programs, you use the primitive types such as `int`, `float`,
|
||||
and `str` to represent data. However, you're not limited to just those
|
||||
types. The standard library has modules such as the `decimal` and
|
||||
`fractions` module that implement new primitive types. You can also
|
||||
make your own types as long as you understand the underlying protocols
|
||||
which make Python objects work. In this exercise, we'll make a new
|
||||
primitive type. There are a lot of little details to worry about, but
|
||||
this will give you a general sense for what's required.
|
||||
|
||||
## (a) Mutable Integers
|
||||
|
||||
Python integers are normally immutable. However, suppose you wanted to
|
||||
make a mutable integer object. Start off by making a class like this:
|
||||
|
||||
```python
|
||||
# mutint.py
|
||||
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
```
|
||||
|
||||
Try it out:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> a
|
||||
<__main__.MutInt object at 0x10e79d408>
|
||||
>>> a.value
|
||||
3
|
||||
>>> a.value = 42
|
||||
>>> a.value
|
||||
42
|
||||
>>> a + 10
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: unsupported operand type(s) for +: 'MutInt' and 'int'
|
||||
>>>
|
||||
```
|
||||
|
||||
That's all very exciting except that nothing really works with this
|
||||
new `MutInt` object. Printing is horrible, none of the math
|
||||
operators work, and it's basically rather useless. Well, except for
|
||||
the fact that its value is mutable--it does have that.
|
||||
|
||||
## (b) Fixing output
|
||||
|
||||
You can fix output by giving the object methods such as `__str__()`,
|
||||
`__repr__()`, and `__format__()`. For example:
|
||||
|
||||
```python
|
||||
# mint.py
|
||||
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
def __repr__(self):
|
||||
return f'MutInt({self.value!r})'
|
||||
|
||||
def __format__(self, fmt):
|
||||
return format(self.value, fmt)
|
||||
```
|
||||
|
||||
Try it out:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> print(a)
|
||||
3
|
||||
>>> a
|
||||
MutInt(3)
|
||||
>>> f'The value is {a:*^10d}'
|
||||
The value is ****3*****
|
||||
>>> a.value = 42
|
||||
>>> a
|
||||
MutInt(42)
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Math Operators
|
||||
|
||||
You can make an object work with various math operators if you implement the
|
||||
appropriate methods for it. However, it's your responsibility to
|
||||
recognize other types of data and implement the appropriate conversion
|
||||
code. Modify the `MutInt` class by giving it an `__add__()` method
|
||||
as follows:
|
||||
|
||||
```python
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return MutInt(self.value + other.value)
|
||||
elif isinstance(other, int):
|
||||
return MutInt(self.value + other)
|
||||
else:
|
||||
return NotImplemented
|
||||
```
|
||||
|
||||
With this change, you should find that you can add both integers and
|
||||
mutable integers. The result is a `MutInt` instance. Adding
|
||||
other kinds of numbers results in an error:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> b = a + 10
|
||||
>>> b
|
||||
MutInt(13)
|
||||
>>> b.value = 23
|
||||
>>> c = a + b
|
||||
>>> c
|
||||
MutInt(26)
|
||||
>>> a + 3.5
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: unsupported operand type(s) for +: 'MutInt' and 'float'
|
||||
>>>
|
||||
```
|
||||
|
||||
One problem with the code is that it doesn't work when the order of operands
|
||||
is reversed. Consider:
|
||||
|
||||
```python
|
||||
>>> a + 10
|
||||
MutInt(13)
|
||||
>>> 10 + a
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: unsupported operand type(s) for +: 'int' and 'MutInt'
|
||||
>>>
|
||||
```
|
||||
|
||||
This is occurring because the `int` type has no knowledge of `MutInt`
|
||||
and it's confused. This can be fixed by adding an `__radd__()` method. This
|
||||
method is called if the first attempt to call `__add__()` didn't work with the
|
||||
provided object.
|
||||
|
||||
```python
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return MutInt(self.value + other.value)
|
||||
elif isinstance(other, int):
|
||||
return MutInt(self.value + other)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
__radd__ = __add__ # Reversed operands
|
||||
```
|
||||
|
||||
With this change, you'll find that addition works:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> a + 10
|
||||
MutInt(13)
|
||||
>>> 10 + a
|
||||
MutInt(13)
|
||||
>>>
|
||||
```
|
||||
|
||||
Since our integer is mutable, you can also make it recognize the in-place
|
||||
add-update operator `+=` by implementing the `__iadd__()` method:
|
||||
|
||||
```python
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
|
||||
def __iadd__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
self.value += other.value
|
||||
return self
|
||||
elif isinstance(other, int):
|
||||
self.value += other
|
||||
return self
|
||||
else:
|
||||
return NotImplemented
|
||||
```
|
||||
|
||||
This allows for interesting uses like this:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> b = a
|
||||
>>> a += 10
|
||||
>>> a
|
||||
MutInt(13)
|
||||
>>> b # Notice that b also changes
|
||||
MutInt(13)
|
||||
>>>
|
||||
```
|
||||
|
||||
That might seem kind of strange that `b` also changes, but there are subtle features like
|
||||
this with built-in Python objects. For example:
|
||||
|
||||
```python
|
||||
>>> a = [1,2,3]
|
||||
>>> b = a
|
||||
>>> a += [4,5]
|
||||
>>> a
|
||||
[1, 2, 3, 4, 5]
|
||||
>>> b
|
||||
[1, 2, 3, 4, 5]
|
||||
|
||||
>>> c = (1,2,3)
|
||||
>>> d = c
|
||||
>>> c += (4,5)
|
||||
>>> c
|
||||
(1, 2, 3, 4, 5)
|
||||
>>> d # Explain difference from lists
|
||||
(1, 2, 3)
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Comparisons
|
||||
|
||||
One problem is that comparisons still don't work. For example:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> b = MutInt(3)
|
||||
>>> a == b
|
||||
False
|
||||
>>> a == 3
|
||||
False
|
||||
>>>
|
||||
```
|
||||
|
||||
You can fix this by adding an `__eq__()` method. Further methods such
|
||||
as `__lt__()`, `__le__()`, `__gt__()`, `__ge__()` can be used to
|
||||
implement other comparisons. For example:
|
||||
|
||||
```python
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return self.value == other.value
|
||||
elif isinstance(other, int):
|
||||
return self.value == other
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return self.value < other.value
|
||||
elif isinstance(other, int):
|
||||
return self.value < other
|
||||
else:
|
||||
return NotImplemented
|
||||
```
|
||||
|
||||
Try it:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> b = MutInt(3)
|
||||
>>> a == b
|
||||
True
|
||||
>>> c = MutInt(4)
|
||||
>>> a < c
|
||||
True
|
||||
>>> a <= c
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: '<=' not supported between instances of 'MutInt' and 'MutInt'
|
||||
>>>
|
||||
```
|
||||
|
||||
The reason the `<=` operator is failing is that no `__le__()` method was provided.
|
||||
You could code it separately, but an easier way to get it is to use the `@total_ordering`
|
||||
decorator:
|
||||
|
||||
```python
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return self.value == other.value
|
||||
elif isinstance(other, int):
|
||||
return self.value == other
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return self.value < other.value
|
||||
elif isinstance(other, int):
|
||||
return self.value < other
|
||||
else:
|
||||
return NotImplemented
|
||||
```
|
||||
|
||||
`@total_ordering` fills in the missing comparison methods for you as long as
|
||||
you minimally provide an equality operator and one of the other relations.
|
||||
|
||||
## (e) Conversions
|
||||
|
||||
Your new primitive type is almost complete. You might want to give it
|
||||
the ability to work with some common conversions. For example:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> int(a)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: int() argument must be a string, a bytes-like object or a number, not 'MutInt'
|
||||
>>> float(a)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: float() argument must be a string, a bytes-like object or a number, not 'MutInt'
|
||||
>>>
|
||||
```
|
||||
|
||||
You can give your class an `__int__()` and `__float__()` method to fix this:
|
||||
|
||||
```python
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
|
||||
def __int__(self):
|
||||
return self.value
|
||||
|
||||
def __float__(self):
|
||||
return float(self.value)
|
||||
```
|
||||
|
||||
Now, you can properly convert:
|
||||
|
||||
```python
|
||||
>>> a = MutInt(3)
|
||||
>>> int(a)
|
||||
3
|
||||
>>> float(a)
|
||||
3.0
|
||||
>>>
|
||||
```
|
||||
|
||||
As a general rule, Python never automatically converts data though. Thus, even though you
|
||||
gave the class an `__int__()` method, `MutInt` is still not going to work in all
|
||||
situations when an integer might be expected. For example, indexing:
|
||||
|
||||
```python
|
||||
>>> names = ['Dave', 'Guido', 'Paula', 'Thomas', 'Lewis']
|
||||
>>> a = MutInt(1)
|
||||
>>> names[a]
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: list indices must be integers or slices, not MutInt
|
||||
>>>
|
||||
```
|
||||
|
||||
This can be fixed by giving `MutInt` an `__index__()` method that produces an integer.
|
||||
Modify the class like this:
|
||||
|
||||
```python
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
...
|
||||
|
||||
def __int__(self):
|
||||
return self.value
|
||||
|
||||
__index__ = __int__ # Make indexing work
|
||||
```
|
||||
|
||||
|
||||
**Discussion**
|
||||
|
||||
Making a new primitive datatype is actually one of the most complicated
|
||||
programming tasks in Python. There are a lot of edge cases and low-level
|
||||
issues to worry about--especially with regard to how your type interacts
|
||||
with other Python types. Probably the key thing to keep in mind is that
|
||||
you can customize almost every aspect of how an object interacts with the
|
||||
rest of Python if you know the underlying protocols. If you're going to
|
||||
do this, it's advisable to look at the existing code for something similar
|
||||
to what you're trying to make.
|
||||
|
||||
\[ [Solution](soln2_4.md) | [Index](index.md) | [Exercise 2.3](ex2_3.md) | [Exercise 2.5](ex2_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
324
Exercises/ex2_5.md
Normal file
324
Exercises/ex2_5.md
Normal file
@ -0,0 +1,324 @@
|
||||
\[ [Index](index.md) | [Exercise 2.4](ex2_4.md) | [Exercise 2.6](ex2_6.md) \]
|
||||
|
||||
# Exercise 2.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Look at memory allocation behavior of lists and dicts
|
||||
- Make a custom container
|
||||
|
||||
*Files Created:* None
|
||||
|
||||
## (a) List growth
|
||||
|
||||
Python lists are highly optimized for performing `append()`
|
||||
operations. Each time a list grows, it grabs a larger chunk of memory
|
||||
than it actually needs with the expectation that more data will be
|
||||
added to the list later. If new items are added and space is
|
||||
available, the `append()` operation stores the item without
|
||||
allocating more memory.
|
||||
|
||||
Experiment with this feature of lists by using
|
||||
the `sys.getsizeof()` function on a list and appending a few
|
||||
more items.
|
||||
|
||||
```python
|
||||
>>> import sys
|
||||
>>> items = []
|
||||
>>> sys.getsizeof(items)
|
||||
64
|
||||
>>> items.append(1)
|
||||
>>> sys.getsizeof(items)
|
||||
96
|
||||
>>> items.append(2)
|
||||
>>> sys.getsizeof(items) # Notice how the size does not increase
|
||||
96
|
||||
>>> items.append(3)
|
||||
>>> sys.getsizeof(items) # It still doesn't increase here
|
||||
96
|
||||
>>> items.append(4)
|
||||
>>> sys.getsizeof(items) # Not yet.
|
||||
96
|
||||
>>> items.append(5)
|
||||
>>> sys.getsizeof(items) # Notice the size has jumped
|
||||
128
|
||||
>>>
|
||||
```
|
||||
|
||||
A list stores its items by reference. So, the memory required for
|
||||
each item is a single memory address. On a 64-bit machine, an address
|
||||
is typically 8 bytes. However, if Python has been compiled for
|
||||
32-bits, it might be 4 bytes and the numbers for the above example
|
||||
will be half of what's shown.
|
||||
|
||||
## (b) Dictionary/Class Growth
|
||||
|
||||
Python dictionaries (and classes) allow up to 5 values to be stored
|
||||
before their reserved memory doubles. Investigate by making a dictionary
|
||||
and adding a few more values to it:
|
||||
|
||||
```python
|
||||
>>> row = { 'route': '22', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354 }
|
||||
>>> sys.getsizeof(row)
|
||||
>>> sys.getsizeof(row)
|
||||
240
|
||||
>>> row['a'] = 1
|
||||
>>> sys.getsizeof(row)
|
||||
240
|
||||
>>> row['b'] = 2
|
||||
>>> sys.getsizeof(row)
|
||||
368
|
||||
>>>
|
||||
```
|
||||
|
||||
Does the memory go down if you delete the item you just added?
|
||||
|
||||
Food for thought: If you are creating large numbers of records,
|
||||
representing each record as a dictionary might not be the most
|
||||
efficient approach--you could be paying a heavy price for the convenience
|
||||
of having a dictionary. It might be better to consider the use of tuples,
|
||||
named tuples, or classes that define `__slots__`.
|
||||
|
||||
## (c) Changing Your Orientation (to Columns)
|
||||
|
||||
You can often save a lot of memory if you change your view of data.
|
||||
For example, what happens if you read all of the bus data into a
|
||||
columns using this function?
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
|
||||
...
|
||||
|
||||
def read_rides_as_columns(filename):
|
||||
'''
|
||||
Read the bus ride data into 4 lists, representing columns
|
||||
'''
|
||||
routes = []
|
||||
dates = []
|
||||
daytypes = []
|
||||
numrides = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
routes.append(row[0])
|
||||
dates.append(row[1])
|
||||
daytypes.append(row[2])
|
||||
numrides.append(int(row[3]))
|
||||
return dict(routes=routes, dates=dates, daytypes=daytypes, numrides=numrides)
|
||||
```
|
||||
|
||||
In theory, this function should save a lot of memory. Let's analyze it before trying it.
|
||||
|
||||
First, the datafile contained 577563 rows of data where each row contained
|
||||
four values. If each row is stored as a dictionary, then those dictionaries
|
||||
are minimally 240 bytes in size.
|
||||
|
||||
```python
|
||||
>>> nrows = 577563 # Number of rows in original file
|
||||
>>> nrows * 240
|
||||
138615120
|
||||
>>>
|
||||
```
|
||||
|
||||
So, that's 138MB just for the dictionaries themselves. This does not
|
||||
include any of the values actually stored in the dictionaries.
|
||||
|
||||
By switching to columns, the data is stored in 4 separate lists.
|
||||
Each list requires 8 bytes per item to store a pointer. So, here's
|
||||
a rough estimate of the list requirements:
|
||||
|
||||
```python
|
||||
>>> nrows * 4 * 8
|
||||
18482016
|
||||
>>>
|
||||
```
|
||||
|
||||
That's about 18MB in list overhead. So, switching to a column orientation
|
||||
should save about 120MB of memory solely from eliminating all of the extra information that
|
||||
needs to be stored in dictionaries.
|
||||
|
||||
Try using this function to read the bus data and look at the memory use.
|
||||
|
||||
```python
|
||||
>>> import tracemalloc
|
||||
>>> tracemalloc.start()
|
||||
>>> columns = read_rides_as_columns('Data/ctabus.csv')
|
||||
>>> tracemalloc.get_traced_memory()
|
||||
... look at the result ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Does the result reflect the expected savings in memory from our rough calculations above?
|
||||
|
||||
## (d) Making a Custom Container - The Great Fake Out
|
||||
|
||||
Storing the data in columns offers a much better memory savings, but
|
||||
the data is now rather weird to work with. In fact, none of our
|
||||
earlier analysis code from [Exercise 2.2](ex2_2.md) can work
|
||||
with columns. The reason everything is broken is that you've broken
|
||||
the data abstraction that was used in earlier exercises--namely the
|
||||
assumption that data is stored as a list of dictionaries.
|
||||
|
||||
This can be fixed if you're willing to make a custom container object
|
||||
that "fakes" it. Let's do that.
|
||||
|
||||
The earlier analysis code assumes the data is stored in a sequence of
|
||||
records. Each record is represented as a dictionary. Let's start
|
||||
by making a new "Sequence" class. In this class, we store the
|
||||
four columns of data that were being using in the `read_rides_as_columns()`
|
||||
function.
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
|
||||
import collections
|
||||
...
|
||||
class RideData(collections.Sequence):
|
||||
def __init__(self):
|
||||
self.routes = [] # Columns
|
||||
self.dates = []
|
||||
self.daytypes = []
|
||||
self.numrides = []
|
||||
```
|
||||
|
||||
Try creating a `RideData` instance. You'll find that it fails with an
|
||||
error message like this:
|
||||
|
||||
```python
|
||||
>>> records = RideData()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Can't instantiate abstract class RideData with abstract methods __getitem__, __len__
|
||||
>>>
|
||||
```
|
||||
|
||||
Carefully read the error message. It tells us what we need to
|
||||
implement. Let's add a `__len__()` and `__getitem__()` method. In the
|
||||
`__getitem__()` method, we'll make a dictionary. In addition, we'll
|
||||
create an `append()` method that takes a dictionary and unpacks it
|
||||
into 4 separate `append()` operations.
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
...
|
||||
|
||||
class RideData(collections.Sequence):
|
||||
def __init__(self):
|
||||
# Each value is a list with all of the values (a column)
|
||||
self.routes = []
|
||||
self.dates = []
|
||||
self.daytypes = []
|
||||
self.numrides = []
|
||||
|
||||
def __len__(self):
|
||||
# All lists assumed to have the same length
|
||||
return len(self.routes)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return { 'route': self.routes[index],
|
||||
'date': self.dates[index],
|
||||
'daytype': self.daytypes[index],
|
||||
'rides': self.numrides[index] }
|
||||
|
||||
def append(self, d):
|
||||
self.routes.append(d['route'])
|
||||
self.dates.append(d['date'])
|
||||
self.daytypes.append(d['daytype'])
|
||||
self.numrides.append(d['rides'])
|
||||
```
|
||||
|
||||
If you've done this correctly, you should be able to drop this object into
|
||||
the previously written `read_rides_as_dicts()` function. It involves
|
||||
changing only one line of code:
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
...
|
||||
|
||||
def read_rides_as_dicts(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of dicts
|
||||
'''
|
||||
records = RideData() # <--- CHANGE THIS
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = {
|
||||
'route': route,
|
||||
'date': date,
|
||||
'daytype': daytype,
|
||||
'rides' : rides
|
||||
}
|
||||
records.append(record)
|
||||
return records
|
||||
```
|
||||
|
||||
If you've done this right, old code should work exactly as it did before.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> rows = readrides.read_rides_as_dicts('Data/ctabus.csv')
|
||||
>>> rows
|
||||
<readrides.RideData object at 0x10f5054a8>
|
||||
>>> len(rows)
|
||||
577563
|
||||
>>> rows[0]
|
||||
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
|
||||
>>> rows[1]
|
||||
{'route': '4', 'date': '01/01/2001', 'daytype': 'U', 'rides': 9288}
|
||||
>>> rows[2]
|
||||
{'route': '6', 'date': '01/01/2001', 'daytype': 'U', 'rides': 6048}
|
||||
>>>
|
||||
```
|
||||
|
||||
Run your earlier CTA code from [Exercise 2.2](ex2_2.md). It
|
||||
should work without modification, but use substantially less memory.
|
||||
|
||||
## (e) Challenge
|
||||
|
||||
What happens when you take a slice of ride data?
|
||||
|
||||
```python
|
||||
>>> r = rows[0:10]
|
||||
>>> r
|
||||
... look at result ...
|
||||
>>>
|
||||
```
|
||||
|
||||
It's probably going to look a little crazy. Can you modify
|
||||
the `RideData` class so that it produces a proper slice that
|
||||
looks like a list of dictionaries? For example, like this:
|
||||
|
||||
```python
|
||||
>>> rows = readrides.read_rides_as_columns('Data/ctabus.csv')
|
||||
>>> rows
|
||||
<readrides.RideData object at 0x10f5054a8>
|
||||
>>> len(rows)
|
||||
577563
|
||||
>>> r = rows[0:10]
|
||||
>>> r
|
||||
<readrides.RideData object at 0x10f5068c8>
|
||||
>>> len(r)
|
||||
10
|
||||
>>> r[0]
|
||||
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
|
||||
>>> r[1]
|
||||
{'route': '4', 'date': '01/01/2001', 'daytype': 'U', 'rides': 9288}
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln2_5.md) | [Index](index.md) | [Exercise 2.4](ex2_4.md) | [Exercise 2.6](ex2_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
303
Exercises/ex2_6.md
Normal file
303
Exercises/ex2_6.md
Normal file
@ -0,0 +1,303 @@
|
||||
\[ [Index](index.md) | [Exercise 2.5](ex2_5.md) | [Exercise 3.1](ex3_1.md) \]
|
||||
|
||||
# Exercise 2.6
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Explore the power of having first-class objects.
|
||||
- Better understand Python's memory model
|
||||
|
||||
*Files Created:* `reader.py`
|
||||
|
||||
In previous exercises, you wrote various functions for reading CSV data in different
|
||||
files. Surely this is a problem that could be generalized in some way. In this
|
||||
exercise, we explore that idea.
|
||||
|
||||
## (a) First-class Data
|
||||
|
||||
In the file `Data/portfolio.csv`, you read data organized as columns
|
||||
that look like this:
|
||||
|
||||
```python
|
||||
"AA",100,32.20
|
||||
"IBM",50,91.10
|
||||
...
|
||||
```
|
||||
|
||||
In previous code, this data was processed by hard-coding all of the
|
||||
type conversions. For example:
|
||||
|
||||
```python
|
||||
rows = csv.reader(f)
|
||||
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 operations. Make a Python list that contains the
|
||||
conversions you want to carry out on each column:
|
||||
|
||||
```python
|
||||
>>> coltypes = [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.
|
||||
|
||||
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']
|
||||
>>>
|
||||
```
|
||||
|
||||
Zip the column types with the row and look at the result:
|
||||
|
||||
```python
|
||||
>>> r = list(zip(coltypes, row))
|
||||
>>> r
|
||||
[(<class 'str'>, 'AA'), (<class 'int'>, '100'), (<class 'float'>,'32.20')]
|
||||
>>>
|
||||
```
|
||||
|
||||
You will notice that this has paired a type conversion with a value. For example, `int` is
|
||||
paired with the value `'100'`. Now, try this:
|
||||
|
||||
```python
|
||||
>>> record = [func(val) for func, val in zip(coltypes, row)]
|
||||
>>> record
|
||||
['AA', 100, 32.2]
|
||||
>>>
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
You can take it a step further and make dictionaries by using the column
|
||||
headers. For example:
|
||||
|
||||
```python
|
||||
>>> dict(zip(headers, record))
|
||||
{'name': 'AA', 'shares': 100, 'price': 32.2}
|
||||
>>>
|
||||
```
|
||||
|
||||
If you prefer, you can perform all of these steps at once using a
|
||||
dictionary comprehension:
|
||||
|
||||
```python
|
||||
>>> { name:func(val) for name, func, val in zip(headers, coltypes, row) }
|
||||
{'name': 'AA', 'shares': 100, 'price': 32.2}
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Making a Parsing Utility Function
|
||||
|
||||
Create a new file `reader.py`. In that file, define a
|
||||
utility function `read_csv_as_dicts()` that reads a file of CSV
|
||||
data into a list of dictionaries where the user specifies
|
||||
the type conversions for each column.
|
||||
|
||||
Here is how it should work:
|
||||
|
||||
```python
|
||||
>>> import reader
|
||||
>>> portfolio = reader.read_csv_as_dicts('Data/portfolio.csv', [str,int,float])
|
||||
>>> for s in portfolio:
|
||||
print(s)
|
||||
|
||||
{'name': 'AA', 'shares': 100, 'price': 32.2}
|
||||
{'name': 'IBM', 'shares': 50, 'price': 91.1}
|
||||
{'name': 'CAT', 'shares': 150, 'price': 83.44}
|
||||
{'name': 'MSFT', 'shares': 200, 'price': 51.23}
|
||||
{'name': 'GE', 'shares': 95, 'price': 40.37}
|
||||
{'name': 'MSFT', 'shares': 50, 'price': 65.1}
|
||||
{'name': 'IBM', 'shares': 100, 'price': 70.44}
|
||||
>>>
|
||||
```
|
||||
|
||||
Or, if you wanted to read the CTA data:
|
||||
|
||||
```python
|
||||
>>> rows = reader.read_csv_as_dicts('Data/ctabus.csv', [str,str,str,int])
|
||||
>>> len(rows)
|
||||
577563
|
||||
>>> rows[0]
|
||||
{'daytype': 'U', 'route': '3', 'rides': 7354, 'date': '01/01/2001'}
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Memory Revisited
|
||||
|
||||
In the CTA bus data, we determined that there were 181 unique bus routes.
|
||||
|
||||
```python
|
||||
>>> routes = { row['route'] for row in rows }
|
||||
>>> len(routes)
|
||||
181
|
||||
>>>
|
||||
```
|
||||
|
||||
Question: How many unique route string objects are contained in the ride data?
|
||||
Instead of building a set of routes, build a set of object ids instead:
|
||||
|
||||
```python
|
||||
>>> routeids = { id(row['route']) for row in rows }
|
||||
>>> len(routeids)
|
||||
542305
|
||||
>>>
|
||||
```
|
||||
|
||||
Think about this for a moment--there are only 181 distinct route
|
||||
names, but the resulting list of dictionaries contains 542305
|
||||
different route strings. Maybe this is something that could be fixed
|
||||
with a bit of caching or object reuse. As it turns out, Python has
|
||||
a function that can be used to cache strings, `sys.intern()`. For example:
|
||||
|
||||
```python
|
||||
>>> a = 'hello world'
|
||||
>>> b = 'hello world'
|
||||
>>> a is b
|
||||
False
|
||||
>>> import sys
|
||||
>>> a = sys.intern(a)
|
||||
>>> b = sys.intern(b)
|
||||
>>> a is b
|
||||
True
|
||||
>>>
|
||||
```
|
||||
|
||||
Restart Python and try this:
|
||||
|
||||
```python
|
||||
>>> # ------------------ RESTART ---------```
|
||||
>>> import tracemalloc
|
||||
>>> tracemalloc.start()
|
||||
>>> from sys import intern
|
||||
>>> import reader
|
||||
>>> rows = reader.read_csv_as_dicts('Data/ctabus.csv', [intern, str, str, int])
|
||||
>>> routeids = { id(row['route']) for row in rows }
|
||||
>>> len(routeids)
|
||||
181
|
||||
>>>
|
||||
```
|
||||
|
||||
Take a look at the memory use.
|
||||
|
||||
```python
|
||||
>>> tracemalloc.get_traced_memory()
|
||||
... look at result ...
|
||||
>>>
|
||||
```
|
||||
|
||||
The memory should drop quite a bit. Observation: There is a lot of repetition
|
||||
involving dates as well. What happens if you also cache the date strings?
|
||||
|
||||
```python
|
||||
>>> # ------------------ RESTART ---------```
|
||||
>>> import tracemalloc
|
||||
>>> tracemalloc.start()
|
||||
>>> from sys import intern
|
||||
>>> import reader
|
||||
>>> rows = reader.read_csv_as_dicts('Data/ctabus.csv', [intern, intern, str, int])
|
||||
>>> tracemalloc.get_traced_memory()
|
||||
... look at result ...
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Special Challenge Project
|
||||
|
||||
In [Exercise 2.5](ex2_5.md), we created a class `RideData` that
|
||||
stored all of the bus data in columns, but actually presented the data
|
||||
to a user as a sequence of dictionaries. It saved a lot of memory
|
||||
through various forms of magic.
|
||||
|
||||
Can you generalize that idea? Specifically, can you make a general
|
||||
purpose function `read_csv_as_columns()` that works like this:
|
||||
|
||||
```python
|
||||
>>> data = read_csv_as_columns('Data/ctabus.csv', types=[str, str, str, int])
|
||||
>>> data
|
||||
<__main__.DataCollection object at 0x102b45048>
|
||||
>>> len(data)
|
||||
577563
|
||||
>>> data[0]
|
||||
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
|
||||
>>> data[1]
|
||||
{'route': '4', 'date': '01/01/2001', 'daytype': 'U', 'rides': 9288}
|
||||
>>> data[2]
|
||||
{'route': '6', 'date': '01/01/2001', 'daytype': 'U', 'rides': 6048}
|
||||
>>>
|
||||
```
|
||||
|
||||
This function is supposed to be general purpose--you can give it any file and
|
||||
a list of the column types and it will read the data. The data is read into
|
||||
a class `DataCollection` that stores the data as columns internally. The data
|
||||
presents itself as a sequence of dictionaries when accessed however.
|
||||
|
||||
Try using this function with the string interning trick in the last part. How
|
||||
much memory does it take to store all of the ride data now? Can you still use
|
||||
this function with your earlier CTA analysis code?
|
||||
|
||||
## (e) Deep Thought
|
||||
|
||||
In this exercise, you have written two functions, `read_csv_as_dicts()` and
|
||||
`read_csv_as_columns()`. These functions present data to the user in the same way.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> data1 = read_csv_as_dicts('Data/ctabus.csv', [str, str, str, int])
|
||||
>>> len(data1)
|
||||
577563
|
||||
>>> data1[0]
|
||||
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
|
||||
>>> data1[1]
|
||||
{'route': '4', 'date': '01/01/2001', 'daytype': 'U', 'rides': 9288}
|
||||
>>>
|
||||
|
||||
>>> data2 = read_csv_as_columns('Data/ctabus.csv', [str, str, str, int])
|
||||
>>> len(data2)
|
||||
577563
|
||||
>>> data2[0]
|
||||
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
|
||||
>>> data2[1]
|
||||
{'route': '4', 'date': '01/01/2001', 'daytype': 'U', 'rides': 9288}
|
||||
>>>
|
||||
```
|
||||
|
||||
In fact, you can use either function in the CTA data analysis code
|
||||
that you wrote. Yet, under the covers completely different things are
|
||||
happening. The `read_csv_as_columns()` function is storing the data
|
||||
in a different representation. It's relying on Python sequence
|
||||
protocols (magic methods) to present information to you in a more useful
|
||||
way.
|
||||
|
||||
This is really part of a much bigger programming concept of "Data
|
||||
Abstraction". When writing programs, the way in which data is
|
||||
presented is often more important than how the data is actually put
|
||||
together under the hood. Although we're presenting the data as a
|
||||
sequence of dictionaries, there's a great deal of flexibility in
|
||||
how that actually happens behind the scenes. That's a powerful
|
||||
idea and something to think about when writing your own programs.
|
||||
|
||||
\[ [Solution](soln2_6.md) | [Index](index.md) | [Exercise 2.5](ex2_5.md) | [Exercise 3.1](ex3_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
102
Exercises/ex3_1.md
Normal file
102
Exercises/ex3_1.md
Normal file
@ -0,0 +1,102 @@
|
||||
\[ [Index](index.md) | [Exercise 2.6](ex2_6.md) | [Exercise 3.2](ex3_2.md) \]
|
||||
|
||||
# Exercise 3.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Define a simple class
|
||||
|
||||
*Files Modified:* `stock.py`
|
||||
|
||||
In link:ex1_5.html[Exercise 1.5], you defined a simple class
|
||||
`Stock` for representing a holding of stock. In this exercise,
|
||||
we're simply going to add a few features to that class as well as
|
||||
write some utility functions.
|
||||
|
||||
## (a) Adding a new method
|
||||
|
||||
Add a new method `sell(nshares)` to Stock that sells a certain number
|
||||
of shares by decrementing the share count. Have it work like this:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG',100,490.10)
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.sell(25)
|
||||
>>> s.shares
|
||||
75
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Reading a portfolio
|
||||
|
||||
Add a function `read_portfolio()` to your `stock.py` program that
|
||||
reads a file of portfolio data into a list of `Stock` objects. Here's how it should work:
|
||||
|
||||
```python
|
||||
>>> portfolio = read_portfolio('Data/portfolio.csv')
|
||||
>>> for s in portfolio:
|
||||
print(s)
|
||||
|
||||
<__main__.Stock object at 0x3902f0>
|
||||
<__main__.Stock object at 0x390270>
|
||||
<__main__.Stock object at 0x390330>
|
||||
<__main__.Stock object at 0x390370>
|
||||
<__main__.Stock object at 0x3903b0>
|
||||
<__main__.Stock object at 0x3903f0>
|
||||
<__main__.Stock object at 0x390430>
|
||||
>>>
|
||||
```
|
||||
|
||||
You already wrote a similar function as part of
|
||||
link:ex2_3.html[Exercise 2.3]. Design discussion: Should
|
||||
`read_portfolio()` be a separate function or part of the class
|
||||
definition?
|
||||
|
||||
## (c) Printing a Table
|
||||
|
||||
Table the data read in part (b) and use it to make a nicely formatted
|
||||
table. For example:
|
||||
|
||||
```python
|
||||
>>> portfolio = read_portfolio('Data/portfolio.csv')
|
||||
>>> for s in portfolio:
|
||||
print('%10s %10d %10.2f' % (s.name, s.shares, s.price))
|
||||
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
Take this code and put it in a function `print_portfolio()` that
|
||||
produces the same output, but additionally adds some table headers.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> portfolio = read_portfolio('Data/portfolio.csv')
|
||||
>>> print_portfolio(portfolio)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln3_1.md) | [Index](index.md) | [Exercise 2.6](ex2_6.md) | [Exercise 3.2](ex3_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
170
Exercises/ex3_2.md
Normal file
170
Exercises/ex3_2.md
Normal file
@ -0,0 +1,170 @@
|
||||
\[ [Index](index.md) | [Exercise 3.1](ex3_1.md) | [Exercise 3.3](ex3_3.md) \]
|
||||
|
||||
# Exercise 3.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about attribute access
|
||||
- Learn how use getattr(), setattr(), and related functions.
|
||||
- Experiment with bound methods.
|
||||
|
||||
|
||||
*Files Created:* `tableformat.py`
|
||||
|
||||
## (a) The Three Operations
|
||||
|
||||
The entire Python object system consists of just three core operations: getting, setting, and deleting
|
||||
of attributes. Normally, these are accessed via the dot (.) like this:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.name # get
|
||||
'GOOG'
|
||||
>>> s.shares = 50 # set
|
||||
>>> del s.shares # delete
|
||||
>>>
|
||||
```
|
||||
|
||||
The three operations are also available as functions. For example:
|
||||
|
||||
```python
|
||||
>>> getattr(s, 'name') # Same as s.name
|
||||
'GOOG'
|
||||
>>> setattr(s, 'shares', 50) # Same as s.shares = 50
|
||||
>>> delattr(s, 'shares') # Same as del s.shares
|
||||
>>>
|
||||
```
|
||||
|
||||
An additional function `hasattr()` can be used to probe an object for the existence of an attribute:
|
||||
|
||||
```python
|
||||
>>> hasattr(s, 'name')
|
||||
True
|
||||
>>> hasattr(s, 'blah')
|
||||
False
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Using getattr()
|
||||
|
||||
The `getattr()` function is extremely useful for
|
||||
writing code that processes objects in an extremely generic way. To
|
||||
illustrate, consider this example which prints out a set of
|
||||
user-defined attributes:
|
||||
|
||||
```python
|
||||
>>> s= Stock('GOOG', 100, 490.1)
|
||||
>>> fields = ['name','shares','price']
|
||||
>>> for name in fields:
|
||||
print(name, getattr(s, name))
|
||||
|
||||
name GOOG
|
||||
shares 100
|
||||
price 490.1
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Table Output
|
||||
|
||||
In link:ex3_1.html[Exercise 3.1], you wrote a function `print_portfolio()`
|
||||
that made a nicely formatted table. That function was custom tailored
|
||||
to a list of `Stock` objects. However, it can be completely generalized
|
||||
to work with any list of objects using the technique in part (b).
|
||||
|
||||
Create a new module called `tableformat.py`. In that program,
|
||||
write a function `print_table()` that takes a sequence (list) of objects,
|
||||
a list of attribute names, and prints a nicely formatted table. For example:
|
||||
|
||||
```python
|
||||
>>> import stock
|
||||
>>> import tableformat
|
||||
>>> portfolio = stock.read_portfolio('Data/portfolio.csv')
|
||||
>>> tableformat.print_table(portfolio, ['name','shares','price'])
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
|
||||
>>> tableformat.print_table(portfolio,['shares','name'])
|
||||
shares name
|
||||
---------- ----------
|
||||
100 AA
|
||||
50 IBM
|
||||
150 CAT
|
||||
200 MSFT
|
||||
95 GE
|
||||
50 MSFT
|
||||
100 IBM
|
||||
>>>
|
||||
```
|
||||
|
||||
For simplicity, just have the `print_table()` function print each field in
|
||||
a 10-character wide column.
|
||||
|
||||
## (d) Bound Methods
|
||||
|
||||
It may be surprising, but method calls are layered onto the machinery used
|
||||
for simple attributes. Essentially, a method is an attribute that
|
||||
executes when you add the required parentheses () to call it like a function. For
|
||||
example:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG',100,490.10)
|
||||
>>> s.cost # Looks up the method
|
||||
<bound method Stock.cost of <__main__.Stock object at 0x409530>>
|
||||
>>> s.cost() # Looks up and calls the method
|
||||
49010.0
|
||||
|
||||
>>> # Same operations using getattr()
|
||||
>>> getattr(s, 'cost')
|
||||
<bound method Stock.cost of <__main__.Stock object at 0x409530>>
|
||||
>>> getattr(s, 'cost')()
|
||||
49010.0
|
||||
>>>
|
||||
```
|
||||
|
||||
A bound method is attached to the object where it came from. If that
|
||||
object is modified, the method will see the modifications. You can
|
||||
view the original object by inspecting the `__self__` attribute
|
||||
of the method.
|
||||
|
||||
```python
|
||||
>>> c = s.cost
|
||||
>>> c()
|
||||
49010.0
|
||||
>>> s.shares = 75
|
||||
>>> c()
|
||||
36757.5
|
||||
>>> c.__self__
|
||||
<__main__.Stock object at 0x409530>
|
||||
>>> c.__func__
|
||||
<function cost at 0x37cc30>
|
||||
>>> c.__func__(c.__self__) # This is what happens behind the scenes of calling c()
|
||||
36757.5
|
||||
>>>
|
||||
```
|
||||
|
||||
Try it with the `sell()` method just to make sure you
|
||||
understand the mechanics:
|
||||
|
||||
```python
|
||||
>>> f = s.sell
|
||||
>>> f.__func__(f.__self__, 25) # Same as s.sell(25)
|
||||
>>> s.shares
|
||||
50
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln3_2.md) | [Index](index.md) | [Exercise 3.1](ex3_1.md) | [Exercise 3.3](ex3_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
234
Exercises/ex3_3.md
Normal file
234
Exercises/ex3_3.md
Normal file
@ -0,0 +1,234 @@
|
||||
\[ [Index](index.md) | [Exercise 3.2](ex3_2.md) | [Exercise 3.4](ex3_4.md) \]
|
||||
|
||||
# Exercise 3.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about class variables and class methods
|
||||
|
||||
*Files Modified:* `stock.py`, `reader.py`
|
||||
|
||||
Instances of the `Stock` class defined in the previous exercise are
|
||||
normally created as follows:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>>
|
||||
```
|
||||
|
||||
However, the `read_portfolio()` function also creates instances from rows
|
||||
of data read from files. For example, code such as the following is used:
|
||||
|
||||
```python
|
||||
>>> import csv
|
||||
>>> f = open('Data/portfolio.csv')
|
||||
>>> rows = csv.reader(f)
|
||||
>>> headers = next(rows)
|
||||
>>> row = next(rows)
|
||||
>>> row
|
||||
['AA', '100', '32.20']
|
||||
>>> s = Stock(row[0], int(row[1]), float(row[2]))
|
||||
>>>
|
||||
```
|
||||
|
||||
## (a) Alternate constructors
|
||||
|
||||
Perhaps the creation of a `Stock` from a row of raw data is better handled
|
||||
by an alternate constructor. Modify the `Stock` class so that it has
|
||||
a `types` class variable and `from_row()` class method like this:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
types = (str, int, float)
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls.types, row)]
|
||||
return cls(*values)
|
||||
...
|
||||
```
|
||||
|
||||
Here's how to test it:
|
||||
|
||||
```python
|
||||
>>> row = ['AA', '100', '32.20']
|
||||
>>> s = Stock.from_row(row)
|
||||
>>> s.name
|
||||
'AA'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.price
|
||||
32.2
|
||||
>>> s.cost()
|
||||
3220.0000000000005
|
||||
>>>
|
||||
```
|
||||
|
||||
Carefully observe how the string values in the row have been converted to a proper type.
|
||||
|
||||
## (b) Class variables and inheritance
|
||||
|
||||
Class variables such as `types` are sometimes used as a customization mechanism
|
||||
when inheritance is used. For example, in the `Stock` class, the types can be
|
||||
easily changed in a subclass. Try this example which changes the `price` attribute
|
||||
to a `Decimal` instance (which is often better suited to financial calculations):
|
||||
|
||||
```python
|
||||
>>> from decimal import Decimal
|
||||
>>> class DStock(Stock):
|
||||
types = (str, int, Decimal)
|
||||
|
||||
>>> row = ['AA', '100', '32.20']
|
||||
>>> s = DStock.from_row(row)
|
||||
>>> s.price
|
||||
Decimal('32.20')
|
||||
>>> s.cost()
|
||||
Decimal('3220.0')
|
||||
>>>
|
||||
```
|
||||
|
||||
**Design Discussion**
|
||||
|
||||
The problem being addressed in this exercise concerns the conversion of data read
|
||||
from a file. Would it make sense to perform these conversions in the `__init__()`
|
||||
method of the `Stock` class instead? For example:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = str(name)
|
||||
self.shares = int(shares)
|
||||
self.price = float(price)
|
||||
```
|
||||
|
||||
By doing this, you would convert a row of data as follows:
|
||||
|
||||
```python
|
||||
>>> row = ['AA', '100', '32.20']
|
||||
>>> s = Stock(*row)
|
||||
>>> s.name
|
||||
'AA'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.price
|
||||
32.2
|
||||
>>>
|
||||
```
|
||||
|
||||
Is this good or bad? What are your thoughts? On the whole, I think
|
||||
it's a questionable design since it mixes two different things
|
||||
together--the creation of an instance and the conversion of data.
|
||||
Plus, the implicit conversions in `__init__()` limit flexibility and
|
||||
might introduce weird bugs if a user isn't paying careful attention.
|
||||
|
||||
## (c) Reading Objects
|
||||
|
||||
Change the `read_portfolio()` function to use the new `Stock.from_row()`
|
||||
method that you wrote.
|
||||
|
||||
Point of thought: Which version of code do you like better? The
|
||||
version of `read_portfolio()` that performed the type conversions or
|
||||
the one that performs conversions in the `Stock.from_row()` method?
|
||||
Think about data abstraction.
|
||||
|
||||
Can you modify `read_portfolio()` to create objects using a class other
|
||||
than `Stock`? For example, can the user select the class that they want?
|
||||
|
||||
## (d) Generalizing
|
||||
|
||||
A useful feature of class-methods is that you can use them to put a
|
||||
highly uniform instance creation interface on a wide variety of
|
||||
classes and write general purpose utility functions that use them.
|
||||
|
||||
Earlier, you created a file `reader.py` that had some functions for
|
||||
reading CSV data. Add the following `read_csv_as_instances()`
|
||||
function to the file which accepts a class as input and uses the class
|
||||
`from_row()` method to create a list of instances:
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
...
|
||||
|
||||
def read_csv_as_instances(filename, cls):
|
||||
'''
|
||||
Read a CSV file into a list of instances
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
records.append(cls.from_row(row))
|
||||
return records
|
||||
```
|
||||
|
||||
Get rid of the `read_portfolio()` function--you don't need that anymore. If you want to
|
||||
read a list of `Stock` objects, do this:
|
||||
|
||||
```python
|
||||
>>> # Read a portfolio of Stock instances
|
||||
>>> from reader import read_csv_as_instances
|
||||
>>> from stock import Stock
|
||||
>>> portfolio = read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
>>> portfolio
|
||||
[<__main__.Stock object at 0x100674748>,
|
||||
<__main__.Stock object at 0x1006746d8>,
|
||||
<__main__.Stock object at 0x1006747b8>,
|
||||
<__main__.Stock object at 0x100674828>,
|
||||
<__main__.Stock object at 0x100674898>,
|
||||
<__main__.Stock object at 0x100674908>,
|
||||
<__main__.Stock object at 0x100674978>]
|
||||
>>>
|
||||
```
|
||||
|
||||
Here is another example of how you might use `read_csv_as_instances()` with a completely different class:
|
||||
|
||||
```python
|
||||
>>> class Row:
|
||||
def __init__(self, route, date, daytype, numrides):
|
||||
self.route = route
|
||||
self.date = date
|
||||
self.daytype = daytype
|
||||
self.numrides = numrides
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
return cls(row[0], row[1], row[2], int(row[3]))
|
||||
|
||||
>>> rides = read_csv_as_instances('Data/ctabus.csv', Row)
|
||||
>>> len(rides)
|
||||
577563
|
||||
>>>
|
||||
```
|
||||
|
||||
**Discussion**
|
||||
|
||||
This exercise illustrates the two most common uses of class variables
|
||||
and class methods. Class variables are often used to hold a global
|
||||
parameter (e.g., a configuration setting) that is shared across all
|
||||
instances. Sometimes subclasses will inherit from the base class and
|
||||
override the setting to change behavior.
|
||||
|
||||
Class methods are most commonly used to implement alternate
|
||||
constructors as shown. A common way to spot such class methods is to
|
||||
look for the word "from" in the name. For example, here is an example
|
||||
on built-in dictionaries:
|
||||
|
||||
```python
|
||||
>>> d = dict.fromkeys(['a','b','c'], 0) # class method
|
||||
>>> d
|
||||
{'a': 0, 'c': 0, 'b': 0}
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln3_3.md) | [Index](index.md) | [Exercise 3.2](ex3_2.md) | [Exercise 3.4](ex3_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
136
Exercises/ex3_4.md
Normal file
136
Exercises/ex3_4.md
Normal file
@ -0,0 +1,136 @@
|
||||
\[ [Index](index.md) | [Exercise 3.3](ex3_3.md) | [Exercise 3.5](ex3_5.md) \]
|
||||
|
||||
# Exercise 3.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to encapsulate object internals using private
|
||||
attributes, properties, and slots
|
||||
|
||||
*Files Modified:* `stock.py`
|
||||
|
||||
## (a) Private attributes
|
||||
|
||||
As a general rule, attributes that are internal to a class should have a leading underscore.
|
||||
In the previous exercise, the `Stock` class had a `types` class variable that was
|
||||
used for converting rows of data. Change the code so that this variable has a leading
|
||||
underscore on it.
|
||||
|
||||
## (b) Properties for computed attributes
|
||||
|
||||
Earlier, you defined a class `Stock`. For example:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG',100,490.10)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.price
|
||||
490.1
|
||||
>>> s.cost()
|
||||
49010.0
|
||||
>>>
|
||||
```
|
||||
|
||||
Using a property, turn `cost()` into an attribute that no longer requires the parentheses. For example:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.cost # Property. Computes the cost
|
||||
49010.0
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Enforcing Validation Rules
|
||||
|
||||
Using properties and private attributes, modify the `shares` attribute
|
||||
of the `Stock` class so that it can only be assigned a non-negative
|
||||
integer value. In addition, modify the `price` attribute so that it
|
||||
can only be assigned a non-negative floating point value.
|
||||
|
||||
The new object should work almost exactly the same as
|
||||
the old one except for extra type and value checking.
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.10)
|
||||
>>> s.shares = 50 # OK
|
||||
>>> s.shares = '50'
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Expected integer
|
||||
>>> s.shares = -10
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: shares must be >= 0
|
||||
|
||||
>>> s.price = 123.45 # OK
|
||||
>>> s.price = '123.45'
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Expected float
|
||||
>>> s.price = -10.0
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: price must be >= 0
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Adding `__slots__`
|
||||
|
||||
Modify your new `Stock` class to use `__slots__`. You will find that
|
||||
you have to use a different set of attribute names than
|
||||
before--specifically, you will have to list the private attribute
|
||||
names (e.g., if a property is storing a value in an attribute
|
||||
`_shares`, that is the name you list in `__slots__`). Verify that the
|
||||
class still works and that you can no longer add new attributes.
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.10)
|
||||
>>> s.spam = 42
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
AttributeError: 'Stock' object has no attribute 'spam'
|
||||
>>>
|
||||
```
|
||||
|
||||
## (e) Reconciling Types
|
||||
|
||||
In the current `Stock` class, there is a `_types` class variable
|
||||
that gives conversions when reading from a file, but there are also
|
||||
properties that are enforcing types. Who is in charge of this show?
|
||||
Fix the property definitions so that they use the types specified
|
||||
in the `_types` class variable. Make sure the properties work
|
||||
when types are changed via subclassing. For example:
|
||||
|
||||
```python
|
||||
>>> from decimal import Decimal
|
||||
>>> class DStock(Stock):
|
||||
_types = (str, int, Decimal)
|
||||
|
||||
>>> s = DStock('AA', 50, Decimal('91.1'))
|
||||
>>> s.price = 92.3
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Expected a Decimal
|
||||
>>>
|
||||
```
|
||||
|
||||
**Discussion**
|
||||
|
||||
The resulting `Stock` class at the end of this exercise is a muddled
|
||||
mess of properties, type checking, constructors, and other details.
|
||||
Imagine how unpleasant it would be to maintain code that featured
|
||||
dozens or hundreds of such class definitions.
|
||||
|
||||
We're going to figure out how to simplify things considerably, but it's
|
||||
going to take some time and some more advanced techniques. Stay tuned.
|
||||
|
||||
\[ [Solution](soln3_4.md) | [Index](index.md) | [Exercise 3.3](ex3_3.md) | [Exercise 3.5](ex3_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
197
Exercises/ex3_5.md
Normal file
197
Exercises/ex3_5.md
Normal file
@ -0,0 +1,197 @@
|
||||
\[ [Index](index.md) | [Exercise 3.4](ex3_4.md) | [Exercise 3.6](ex3_6.md) \]
|
||||
|
||||
# Exercise 3.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to use inheritance to write extensible code.
|
||||
- See a practical use of inheritance by writing a program that must
|
||||
output data in a variety of user-selectable formats such as plain-text,
|
||||
CSV, and HTML.
|
||||
|
||||
*Files Modified:* `tableformat.py`
|
||||
|
||||
One major use of classes in Python is in writing code that be
|
||||
extended/adapted in various ways. To illustrate, in
|
||||
link:ex3_2.html[Exercise 3.2] you created a function `print_table()`
|
||||
that made tables. You used this to make output from the `portfolio`
|
||||
list. For example:
|
||||
|
||||
```python
|
||||
>>> import stock
|
||||
>>> import reader
|
||||
>>> import tableformat
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> tableformat.print_table(portfolio, ['name','shares','price'])
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
Suppose you wanted the `print_table()` function to be able to
|
||||
make tables in any number of output formats such as CSV, XML, HTML,
|
||||
Excel, etc. Trying to modify the function to support all of those
|
||||
output formats at once would be painful. A better way to do this
|
||||
involves moving the output-related formatting code to a class and using
|
||||
inheritance to implement different output formats.
|
||||
|
||||
## (a) Defining a generic formatter class
|
||||
|
||||
Add the following class definition to the `tableformat.py` file:
|
||||
|
||||
```python
|
||||
class TableFormatter:
|
||||
def headings(self, headers):
|
||||
raise NotImplementedError()
|
||||
|
||||
def row(self, rowdata):
|
||||
raise NotImplementedError()
|
||||
```
|
||||
|
||||
Now, modify the `print_table()` function so that it accepts a `TableFormatter` instance
|
||||
and invokes methods on it to produce output:
|
||||
|
||||
```python
|
||||
def print_table(records, fields, formatter):
|
||||
formatter.headings(fields)
|
||||
for r in records:
|
||||
rowdata = [getattr(r, fieldname) for fieldname in fields]
|
||||
formatter.row(rowdata)
|
||||
```
|
||||
|
||||
These two classes are meant to be used together. For example:
|
||||
|
||||
```
|
||||
>>> import stock, reader, tableformat
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> formatter = tableformat.TableFormatter()
|
||||
>>> tableformat.print_table(portfolio, ['name', 'shares', 'price'], formatter)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
NotImplementedError
|
||||
>>>
|
||||
```
|
||||
|
||||
For now, it doesn't do much of anything interesting. You'll fix this in the next section.
|
||||
|
||||
## (b) Implementing a concrete formatter
|
||||
|
||||
The `TableFormatter` isn't meant to be used by itself. Instead, it is merely a base
|
||||
for other classes that will implement the formatting. Add the following class to
|
||||
`tableformat.py`:
|
||||
|
||||
```python
|
||||
class TextTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print(' '.join('%10s' % h for h in headers))
|
||||
print(('-'*10 + ' ')*len(headers))
|
||||
|
||||
def row(self, rowdata):
|
||||
print(' '.join('%10s' % d for d in rowdata))
|
||||
```
|
||||
|
||||
Now, use your new class as follows:
|
||||
|
||||
```python
|
||||
>>> import stock, reader, tableformat
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> formatter = tableformat.TextTableFormatter()
|
||||
>>> tableformat.print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Adding More Implementations
|
||||
|
||||
Create a class `CSVTableFormatter` that allows output to be generated in CSV format:
|
||||
|
||||
```python
|
||||
>>> import stock, reader, tableformat
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> formatter = tableformat.CSVTableFormatter()
|
||||
>>> tableformat.print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name,shares,price
|
||||
AA,100,32.2
|
||||
IBM,50,91.1
|
||||
CAT,150,83.44
|
||||
MSFT,200,51.23
|
||||
GE,95,40.37
|
||||
MSFT,50,65.1
|
||||
IBM,100,70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
Create a class `HTMLTableFormatter` that generates output in HTML format:
|
||||
|
||||
```python
|
||||
>>> import stock, reader, tableformat
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> formatter = tableformat.HTMLTableFormatter()
|
||||
>>> tableformat.print_table(portfolio, ['name','shares','price'], formatter)
|
||||
<tr> <th>name</th> <th>shares</th> <th>price</th> </tr>
|
||||
<tr> <td>AA</td> <td>100</td> <td>32.2</td> </tr>
|
||||
<tr> <td>IBM</td> <td>50</td> <td>91.1</td> </tr>
|
||||
<tr> <td>CAT</td> <td>150</td> <td>83.44</td> </tr>
|
||||
<tr> <td>MSFT</td> <td>200</td> <td>51.23</td> </tr>
|
||||
<tr> <td>GE</td> <td>95</td> <td>40.37</td> </tr>
|
||||
<tr> <td>MSFT</td> <td>50</td> <td>65.1</td> </tr>
|
||||
<tr> <td>IBM</td> <td>100</td> <td>70.44</td> </tr>
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Making it Easier To Choose
|
||||
|
||||
One problem with using inheritance is the added complexity of picking
|
||||
different classes to use (e.g., remembering the names, using the right
|
||||
`import` statements, etc.). A factory function can simplify this. Add
|
||||
a function `create_formatter()` to your `tableformat.py` file that
|
||||
allows a user to more easily make a formatter by specifying a format such as `'text'`, `'csv'`, or `'html'`. For example:
|
||||
|
||||
```python
|
||||
>>> from tableformat import create_formatter, print_table
|
||||
>>> formatter = create_formatter('html')
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
<tr> <th>name</th> <th>shares</th> <th>price</th> </tr>
|
||||
<tr> <td>AA</td> <td>100</td> <td>32.2</td> </tr>
|
||||
<tr> <td>IBM</td> <td>50</td> <td>91.1</td> </tr>
|
||||
<tr> <td>CAT</td> <td>150</td> <td>83.44</td> </tr>
|
||||
<tr> <td>MSFT</td> <td>200</td> <td>51.23</td> </tr>
|
||||
<tr> <td>GE</td> <td>95</td> <td>40.37</td> </tr>
|
||||
<tr> <td>MSFT</td> <td>50</td> <td>65.1</td> </tr>
|
||||
<tr> <td>IBM</td> <td>100</td> <td>70.44</td> </tr>
|
||||
>>>
|
||||
```
|
||||
|
||||
**Discussion**
|
||||
|
||||
The `TableFormatter` class in this exercise is an example of something known
|
||||
as an "Abstract Base Class." It's not something that's meant to be used directly.
|
||||
Instead, it's serving as a kind of interface specification for a program component--in
|
||||
this case the various output formats. Essentially, the code that produces the table
|
||||
will be programmed against the abstract base class with the expectation that a user
|
||||
will provide a suitable implementation. As long as all of the required methods
|
||||
have been implemented, it should all just "work" (fingers crossed).
|
||||
|
||||
\[ [Solution](soln3_5.md) | [Index](index.md) | [Exercise 3.4](ex3_4.md) | [Exercise 3.6](ex3_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
173
Exercises/ex3_6.md
Normal file
173
Exercises/ex3_6.md
Normal file
@ -0,0 +1,173 @@
|
||||
\[ [Index](index.md) | [Exercise 3.5](ex3_5.md) | [Exercise 3.7](ex3_7.md) \]
|
||||
|
||||
# Exercise 3.6
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to customize the behavior of objects by redefining special methods.
|
||||
- Change the way that user-defined objects get printed
|
||||
- Make objects comparable
|
||||
- Create a context manager
|
||||
|
||||
*Files Created:* None
|
||||
|
||||
*Files Modified:* `stock.py`
|
||||
|
||||
## (a) Better output for representing objects
|
||||
|
||||
All Python objects have two string representations. The first
|
||||
representation is created by string conversion via `str()`
|
||||
(which is called by `print`). The string representation is
|
||||
usually a nicely formatted version of the object meant for humans.
|
||||
The second representation is a code representation of the object
|
||||
created by `repr()` (or simply by viewing a value in the
|
||||
interactive shell). The code representation typically shows you the
|
||||
code that you have to type to get the object. Here is an example
|
||||
that illustrates using dates:
|
||||
|
||||
```python
|
||||
>>> from datetime import date
|
||||
>>> d = date(2008, 7, 5)
|
||||
>>> print(d) # uses str()
|
||||
2008-07-05
|
||||
>>> d # uses repr()
|
||||
datetime.date(2008, 7, 5)
|
||||
>>>
|
||||
```
|
||||
|
||||
There are several techniques for obtaining the `repr()` string
|
||||
in output:
|
||||
|
||||
```python
|
||||
>>> print('The date is', repr(d))
|
||||
The date is datetime.date(2008, 7, 5)
|
||||
>>> print(f'The date is {d!r}')
|
||||
The date is datetime.date(2008, 7, 5)
|
||||
>>> print('The date is %r' % d)
|
||||
The date is datetime.date(2008, 7, 5)
|
||||
>>>
|
||||
```
|
||||
|
||||
Modify the `Stock` object that you've created so that
|
||||
the `__repr__()` method
|
||||
produces more useful output. For example:
|
||||
|
||||
```python
|
||||
>>> goog = Stock('GOOG', 100, 490.10)
|
||||
>>> goog
|
||||
Stock('GOOG', 100, 490.1)
|
||||
>>>
|
||||
```
|
||||
|
||||
See what happens when you read a portfolio of stocks and view the
|
||||
resulting list after you have made these changes. For example:
|
||||
|
||||
```python
|
||||
>>> import stock, reader
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> portfolio
|
||||
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44), Stock('MSFT', 200, 51.23),
|
||||
Stock('GE', 95, 40.37), Stock('MSFT', 50, 65.1), Stock('IBM', 100, 70.44)]
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Making objects comparable
|
||||
|
||||
What happens if you create two identical `Stock` objects and try to compare them? Find out:
|
||||
|
||||
```python
|
||||
>>> a = Stock('GOOG', 100, 490.1)
|
||||
>>> b = Stock('GOOG', 100, 490.1)
|
||||
>>> a == b
|
||||
False
|
||||
>>>
|
||||
```
|
||||
|
||||
You can fix this by giving the `Stock` class an `__eq__()` method. For example:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
...
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Stock) and ((self.name, self.shares, self.price) ==
|
||||
(other.name, other.shares, other.price))
|
||||
...
|
||||
```
|
||||
|
||||
Make this change and try comparing two objects again.
|
||||
|
||||
## (c) A Context Manager
|
||||
|
||||
In link:ex3_5.html[Exercise 3.5], you made it possible for users to make
|
||||
nicely formatted tables. For example:
|
||||
|
||||
```python
|
||||
>>> from tableformat import create_formatter
|
||||
>>> formatter = create_formatter('text')
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
One issue with the code is that all tables are printed to standard out
|
||||
(`sys.stdout`). Suppose you wanted to redirect the output to a file
|
||||
or some other location. In the big picture, you might modify all of
|
||||
the table formatting code to allow a different output file. However,
|
||||
in a pinch, you could also solve this with a context manager.
|
||||
|
||||
Define the following context manager:
|
||||
|
||||
```python
|
||||
>>> import sys
|
||||
>>> class redirect_stdout:
|
||||
def __init__(self, out_file):
|
||||
self.out_file = out_file
|
||||
def __enter__(self):
|
||||
self.stdout = sys.stdout
|
||||
sys.stdout = self.out_file
|
||||
return self.out_file
|
||||
def __exit__(self, ty, val, tb):
|
||||
sys.stdout = self.stdout
|
||||
```
|
||||
|
||||
This context manager works by making a temporary patch to `sys.stdout` to cause
|
||||
all output to redirect to a different file. On exit, the patch is reverted.
|
||||
Try it out:
|
||||
|
||||
```python
|
||||
>>> from tableformat import create_formatter
|
||||
>>> formatter = create_formatter('text')
|
||||
>>> with redirect_stdout(open('out.txt', 'w')) as file:
|
||||
tableformat.print_table(portfolio, ['name','shares','price', formatter)
|
||||
file.close()
|
||||
|
||||
>>> # Inspect the file
|
||||
>>> print(open('out.txt').read())
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln3_6.md) | [Index](index.md) | [Exercise 3.5](ex3_5.md) | [Exercise 3.7](ex3_7.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
143
Exercises/ex3_7.md
Normal file
143
Exercises/ex3_7.md
Normal file
@ -0,0 +1,143 @@
|
||||
\[ [Index](index.md) | [Exercise 3.6](ex3_6.md) | [Exercise 3.8](ex3_8.md) \]
|
||||
|
||||
# Exercise 3.7
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Type checking and interfaces
|
||||
- Abstract base classes
|
||||
|
||||
*Files Modified:* `tableformat.py`
|
||||
|
||||
In link:ex3_5.html[Exercise 3.5], we modified the `tableformat.py` file to have a `TableFormatter`
|
||||
class and to use various subclasses for different output formats. In this exercise, we extend that
|
||||
code a bit more.
|
||||
|
||||
## (a) Interfaces and type checking
|
||||
|
||||
Modify the `print_table()` function so that it checks if the
|
||||
supplied formatter instance inherits from `TableFormatter`. If
|
||||
not, raise a `TypeError`.
|
||||
|
||||
Your new code should catch situations like this:
|
||||
|
||||
```python
|
||||
>>> import stock, reader, tableformat
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> class MyFormatter:
|
||||
def headings(self,headers): pass
|
||||
def row(self,rowdata): pass
|
||||
|
||||
>>> tableformat.print_table(portfolio, ['name','shares','price'], MyFormatter())
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Expected a TableFormatter
|
||||
>>>
|
||||
```
|
||||
|
||||
Adding a check like this might add some degree of safety to the program. However you should
|
||||
still be aware that type-checking is rather weak in Python. There is no guarantee that the
|
||||
object passed as a formatter will work correctly even if it happens to inherit from the
|
||||
proper base class. This next part addresses that issue.
|
||||
|
||||
## (b) Abstract Base Classes
|
||||
|
||||
Modify the `TableFormatter` base class so that it is defined as a proper
|
||||
abstract base class using the `abc` module. Once you have done that, try
|
||||
this experiment:
|
||||
|
||||
```python
|
||||
>>> class NewFormatter(TableFormatter):
|
||||
def headers(self, headings):
|
||||
pass
|
||||
def row(self, rowdata):
|
||||
pass
|
||||
|
||||
>>> f = NewFormatter()
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: Can't instantiate abstract class NewFormatter with abstract methods headings
|
||||
>>>
|
||||
```
|
||||
|
||||
Here, the abstract base class caught a spelling error in the class--the fact that
|
||||
the `headings()` method was incorrectly given as `headers()`.
|
||||
|
||||
## (c) Algorithm Template Classes
|
||||
|
||||
The file `reader.py` contains two functions, `read_csv_as_dicts()` and `read_csv_as_instances()`.
|
||||
Both of those functions are almost identical--there is just one tiny bit of code that's
|
||||
different. Maybe that code could be consolidated into a class definition of some sort.
|
||||
Add the following class to the `reader.py` file:
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
|
||||
import csv
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class CSVParser(ABC):
|
||||
|
||||
def parse(self, filename):
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = self.make_record(headers, row)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
@abstractmethod
|
||||
def make_record(self, headers, row):
|
||||
pass
|
||||
```
|
||||
|
||||
This code provides a shell (or template) of the CSV parsing functionality. To use it, you subclass it, add
|
||||
any additional attributes you might need, and redefine the `make_record()` method. For example:
|
||||
|
||||
```python
|
||||
class DictCSVParser(CSVParser):
|
||||
def __init__(self, types):
|
||||
self.types = types
|
||||
|
||||
def make_record(self, headers, row):
|
||||
return { name: func(val) for name, func, val in zip(headers, self.types, row) }
|
||||
|
||||
class InstanceCSVParser(CSVParser):
|
||||
def __init__(self, cls):
|
||||
self.cls = cls
|
||||
|
||||
def make_record(self, headers, row):
|
||||
return self.cls.from_row(row)
|
||||
```
|
||||
|
||||
Add the above classes to the `reader.py` file. Here's how you would use one of them:
|
||||
|
||||
```python
|
||||
>>> from reader import DictCSVParser
|
||||
>>> parser = DictCSVParser([str, int, float])
|
||||
>>> port = parser.parse('Data/portfolio.csv')
|
||||
>>>
|
||||
```
|
||||
|
||||
It works, but it's kind of annoying. To fix this, reimplement the `read_csv_as_dicts()` and
|
||||
`read_csv_as_instances()` functions to use these classes. Your refactored code should work
|
||||
exactly the same way that it did before. For example:
|
||||
|
||||
```python
|
||||
>>> import reader
|
||||
>>> import stock
|
||||
>>> port = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln3_7.md) | [Index](index.md) | [Exercise 3.6](ex3_6.md) | [Exercise 3.8](ex3_8.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
249
Exercises/ex3_8.md
Normal file
249
Exercises/ex3_8.md
Normal file
@ -0,0 +1,249 @@
|
||||
\[ [Index](index.md) | [Exercise 3.7](ex3_7.md) | [Exercise 4.1](ex4_1.md) \]
|
||||
|
||||
# Exercise 3.8
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about mixin classes and cooperative inheritance
|
||||
|
||||
*Files Modified:* `tableformat.py`
|
||||
|
||||
## (a) The Trouble with Column Formatting
|
||||
|
||||
If you go all the way back to link:ex3_1.txt[Exercise 3.1], you
|
||||
wrote a function `print_portfolio()` that produced a table like this:
|
||||
|
||||
```python
|
||||
>>> portfolio = read_portfolio('Data/portfolio.csv')
|
||||
>>> print_portfolio(portfolio)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
The `print_table()` function developed in the last several exercises
|
||||
almost replaces this functionality--almost. The one problem that it
|
||||
has is that it can't precisely format the content of each column. For
|
||||
example, notice how the values in the `price` column are precisely
|
||||
formatted with 2 decimal points. The `TableFormatter` class and
|
||||
related subclasses can't do that.
|
||||
|
||||
One way to fix it would be to modify the `print_table()` function to
|
||||
accept an additional formats argument. For example, maybe something
|
||||
like this:
|
||||
|
||||
```python
|
||||
>>> def print_table(records, fields, formats, formatter):
|
||||
formatter.headings(fields)
|
||||
for r in records:
|
||||
rowdata = [(fmt % getattr(r, fieldname))
|
||||
for fieldname,fmt in zip(fields,formats)]
|
||||
formatter.row(rowdata)
|
||||
|
||||
>>> import stock, reader
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> from tableformat import TextTableFormatter
|
||||
>>> formatter = TextTableFormatter()
|
||||
>>> print_table(portfolio,
|
||||
['name','shares','price'],
|
||||
['%s','%d','%0.2f'],
|
||||
formatter)
|
||||
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
Yes, you could modify `print_table()` like this, but is that the right
|
||||
place to do it? The whole idea of all of the `TableFormatter` classes
|
||||
is that they could be used in different kinds of applications. Column
|
||||
formatting is something that could be useful elsewhere, not just
|
||||
in the `print_table()` function.
|
||||
|
||||
Another possible approach might be to change the interface to the
|
||||
`TableFormatter` class in some way. For example, maybe adding a third
|
||||
method to apply formatting.
|
||||
|
||||
```python
|
||||
class TableFormatter:
|
||||
def headings(self, headers):
|
||||
...
|
||||
def format(self, rowdata):
|
||||
...
|
||||
def row(self, rowdata):
|
||||
...
|
||||
```
|
||||
|
||||
The problem here is that any time you change the interface on a class,
|
||||
you're going to have to refactor all of the existing code to work with
|
||||
it. Specifically, you'd have to modify all of the already written
|
||||
`TableFormatter` subclasses and all of the code written to use them.
|
||||
Let's not do that.
|
||||
|
||||
As an alternative, a user could use inheritance to customize a
|
||||
specific formatter in order to inject some formatting into it. For
|
||||
example, try this experiment:
|
||||
|
||||
```python
|
||||
>>> from tableformat import TextTableFormatter, print_table
|
||||
>>> class PortfolioFormatter(TextTableFormatter):
|
||||
def row(self, rowdata):
|
||||
formats = ['%s','%d','%0.2f']
|
||||
rowdata = [(fmt % d) for fmt, d in zip(formats, rowdata)]
|
||||
super().row(rowdata)
|
||||
|
||||
>>> formatter = PortfolioFormatter()
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
Yes, that works, but it's also a bit clumsy and weird. The user has
|
||||
to pick a specific formatter to customize. On top of that, they have
|
||||
to implement the actual column formatting code themselves. Surely
|
||||
there is a different way to do this.
|
||||
|
||||
## (b) Going Sideways
|
||||
|
||||
In the `tableformat.py` file, add the following class definition:
|
||||
|
||||
```python
|
||||
class ColumnFormatMixin:
|
||||
formats = []
|
||||
def row(self, rowdata):
|
||||
rowdata = [(fmt % d) for fmt, d in zip(self.formats, rowdata)]
|
||||
super().row(rowdata)
|
||||
```
|
||||
|
||||
This class contains a single method `row()` that applies formatting to
|
||||
the row contents. A class variable `formats` is used to hold the format
|
||||
codes. This class is used via multiple inheritance. For example:
|
||||
|
||||
```python
|
||||
>>> import stock, reader
|
||||
>>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> from tableformat import TextTableFormatter, ColumnFormatMixin, print_table
|
||||
>>> class PortfolioFormatter(ColumnFormatMixin, TextTableFormatter):
|
||||
formats = ['%s', '%d', '%0.2f']
|
||||
|
||||
>>> formatter = PortfolioFormatter()
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.20
|
||||
IBM 50 91.10
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.10
|
||||
IBM 100 70.44
|
||||
```
|
||||
|
||||
This whole approach works because the `ColumnFormatMixin` class is
|
||||
meant to be mixed together with another class that provides the
|
||||
required `row()` method.
|
||||
|
||||
Make another class that makes a formatter print the table headers in all-caps:
|
||||
|
||||
```python
|
||||
class UpperHeadersMixin:
|
||||
def headings(self, headers):
|
||||
super().headings([h.upper() for h in headers])
|
||||
```
|
||||
|
||||
Try it out and notice that the headers are now uppercase:
|
||||
|
||||
```python
|
||||
>>> from tableformat import TextTableFormatter, UpperHeadersMixin
|
||||
>>> class PortfolioFormatter(UpperHeadersMixin, TextTableFormatter):
|
||||
pass
|
||||
|
||||
>>> formatter = PortfolioFormatter()
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
NAME SHARES PRICE
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
This is really the whole idea on "mixins." The creator of a library
|
||||
can provide a basic set of classes such as `TextTableFormatter`,
|
||||
`CSVTableFormatter`, and so forth to start. Then, a collection of
|
||||
add-on classes can be provided to make those classes behave in
|
||||
different ways.
|
||||
|
||||
## (c) Making it Sane
|
||||
|
||||
Using mixins can be a useful tool for framework builders for reducing
|
||||
the amount of code that needs to be written. However, forcing users
|
||||
to remember how to properly compose classes and use multiple inheritance can
|
||||
fry their brains. In link:ex3_5.html[Exercise 3.5], you wrote a
|
||||
function `create_formatter()` that made it easier to create a custom
|
||||
formatter. Take that function and extend it to understand a few optional
|
||||
arguments related to the mixin classes. For example:
|
||||
|
||||
```python
|
||||
>>> from tableformat import create_formatter
|
||||
>>> formatter = create_formatter('csv', column_formats=['"%s"','%d','%0.2f'])
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name,shares,price
|
||||
"AA",100,32.20
|
||||
"IBM",50,91.10
|
||||
"CAT",150,83.44
|
||||
"MSFT",200,51.23
|
||||
"GE",95,40.37
|
||||
"MSFT",50,65.10
|
||||
"IBM",100,70.44
|
||||
|
||||
>>> formatter = create_formatter('text', upper_headers=True)
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
NAME SHARES PRICE
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
Under the covers the `create_formatter()` function will properly compose
|
||||
the classes and return a proper `TableFormatter` instance.
|
||||
|
||||
\[ [Solution](soln3_8.md) | [Index](index.md) | [Exercise 3.7](ex3_7.md) | [Exercise 4.1](ex4_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
167
Exercises/ex4_1.md
Normal file
167
Exercises/ex4_1.md
Normal file
@ -0,0 +1,167 @@
|
||||
\[ [Index](index.md) | [Exercise 3.8](ex3_8.md) | [Exercise 4.2](ex4_2.md) \]
|
||||
|
||||
# Exercise 4.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about how objects are represented.
|
||||
- Learn how attribute assignment and lookup works.
|
||||
- Better understand the role of a class definition
|
||||
|
||||
*Files Created:* None
|
||||
|
||||
*Files Modified:* None
|
||||
|
||||
Start this exercise, by going back to a simple version of the `Stock` class you created.
|
||||
At the interactive prompt, define a
|
||||
new class called `SimpleStock` that looks like this:
|
||||
|
||||
```python
|
||||
>>> class SimpleStock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Once you have defined this class, create a few instances.
|
||||
|
||||
```python
|
||||
>>> goog = SimpleStock('GOOG',100,490.10)
|
||||
>>> ibm = SimpleStock('IBM',50, 91.23)
|
||||
>>>
|
||||
```
|
||||
|
||||
## (a) Representation of Instances
|
||||
|
||||
At the interactive shell, inspect the underlying dictionaries of the two instances you created:
|
||||
|
||||
```python
|
||||
>>> goog.__dict__
|
||||
... look at the output ...
|
||||
>>> ibm.__dict__
|
||||
... look at the output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Modification of Instance Data
|
||||
|
||||
Try setting a new attribute on one of the above instances:
|
||||
|
||||
```python
|
||||
>>> goog.date = "6/11/2007"
|
||||
>>> goog.__dict__
|
||||
... look at output ...
|
||||
>>> ibm.__dict__
|
||||
... look at output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
In the above output, you'll notice that the `goog` instance has
|
||||
a attribute `date` whereas the `ibm` instance does not.
|
||||
It is important to note that Python really doesn't place any
|
||||
restrictions on attributes. For example, the attributes of an
|
||||
instance are not limited to those set up in the `__init__()`
|
||||
method.
|
||||
|
||||
Instead of setting an attribute, try placing a new value directly into
|
||||
the `__dict__` object:
|
||||
|
||||
```python
|
||||
>>> goog.__dict__['time'] = '9:45am'
|
||||
>>> goog.time
|
||||
'9:45am'
|
||||
>>>
|
||||
```
|
||||
|
||||
Here, you really notice the fact that an instance is a layer on top of a dictionary.
|
||||
|
||||
|
||||
## (c) The role of classes
|
||||
|
||||
The definitions that make up a class definition are shared by all
|
||||
instances of that class. Notice, that all instances have a link back
|
||||
to their associated class:
|
||||
|
||||
```python
|
||||
>>> goog.__class__
|
||||
... look at output ...
|
||||
>>> ibm.__class__
|
||||
... look at output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Try calling a method on the instances:
|
||||
|
||||
```python
|
||||
>>> goog.cost()
|
||||
49010.0
|
||||
>>> ibm.cost()
|
||||
4561.5
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice that the name 'cost' is not defined in either `goog.__dict__` or `ibm.__dict__`. Instead, it is being supplied by the
|
||||
class dictionary. Try this:
|
||||
|
||||
```python
|
||||
>>> SimpleStock.__dict__['cost']
|
||||
... look at output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Try calling the `cost()` method directly through the dictionary:
|
||||
|
||||
```python
|
||||
>>> SimpleStock.__dict__['cost'](goog)
|
||||
49010.00
|
||||
>>> SimpleStock.__dict__['cost'](ibm)
|
||||
4561.5
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how you are calling the function defined in the class definition and how the `self` argument
|
||||
gets the instance.
|
||||
|
||||
If you add a new value to the class, it becomes a class variable that's visible to all instances. Try it:
|
||||
|
||||
```python
|
||||
>>> SimpleStock.spam = 42
|
||||
>>> ibm.spam
|
||||
42
|
||||
>>> goog.spam
|
||||
42
|
||||
>>>
|
||||
```
|
||||
|
||||
Observe that `spam` is not part of the instance dictionary.
|
||||
|
||||
```python
|
||||
>>> ibm.__dict__
|
||||
... look at the output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Instead, it's part of the class dictionary:
|
||||
|
||||
```python
|
||||
>>> SimpleStock.__dict__['spam']
|
||||
42
|
||||
>>>
|
||||
```
|
||||
|
||||
Essentially this is all a class really is--it's a collection of values shared by instances.
|
||||
|
||||
|
||||
\[ [Solution](soln4_1.md) | [Index](index.md) | [Exercise 3.8](ex3_8.md) | [Exercise 4.2](ex4_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
288
Exercises/ex4_2.md
Normal file
288
Exercises/ex4_2.md
Normal file
@ -0,0 +1,288 @@
|
||||
\[ [Index](index.md) | [Exercise 4.1](ex4_1.md) | [Exercise 4.3](ex4_3.md) \]
|
||||
|
||||
# Exercise 4.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about the behavior of inheritance
|
||||
- Understand the behavior of super().
|
||||
- More cooperative inheritance.
|
||||
|
||||
*Files Created:* `validate.py`
|
||||
|
||||
|
||||
## (a) The directions of inheritance
|
||||
|
||||
Python has two different "directions" of inheritance. The first is
|
||||
found in the concept of "single inheritance" where a series
|
||||
of classes inherit from a single parent. For example, try this example:
|
||||
|
||||
```python
|
||||
>>> class A:
|
||||
def spam(self):
|
||||
print('A.spam')
|
||||
|
||||
>>> class B(A):
|
||||
def spam(self):
|
||||
print('B.spam')
|
||||
super().spam()
|
||||
|
||||
>>> class C(B):
|
||||
def spam(self):
|
||||
print('C.spam')
|
||||
super().spam()
|
||||
|
||||
|
||||
>>> C.__mro__
|
||||
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
|
||||
>>> c = C()
|
||||
>>> c.spam()
|
||||
C.spam
|
||||
B.spam
|
||||
A.spam
|
||||
>>>
|
||||
```
|
||||
|
||||
Observe that the `__mro__` attribute of class `C` encodes all of its ancestors in
|
||||
order. When you invoke the `spam()` method, it walks the MRO class-by-class up
|
||||
the hierarchy.
|
||||
|
||||
With multiple inheritance, you get a different kind of inheritance that
|
||||
allows different classes to be composed together. Try this example:
|
||||
|
||||
```
|
||||
>>> class Base:
|
||||
def spam(self):
|
||||
print('Base.spam')
|
||||
|
||||
>>> class X(Base):
|
||||
def spam(self):
|
||||
print('X.spam')
|
||||
super().spam()
|
||||
|
||||
>>> class Y(Base):
|
||||
def spam(self):
|
||||
print('Y.spam')
|
||||
super().spam()
|
||||
|
||||
>>> class Z(Base):
|
||||
def spam(self):
|
||||
print('Z.spam')
|
||||
super().spam()
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice that all of the classes above inherit from a common parent `Base`.
|
||||
However, the classes `X`, `Y`, and `Z` are not directly related
|
||||
to each other (there is no inheritance chain linking those classes together).
|
||||
|
||||
However, watch what happens in multiple inheritance:
|
||||
|
||||
```python
|
||||
>>> class M(X,Y,Z):
|
||||
pass
|
||||
|
||||
>>> M.__mro__
|
||||
(<class '__main__.M'>, <class '__main__.X'>, <class '__main__.Y'>, <class '__main__.Z'>, <class '__main__.Base'>, <class 'object'>)
|
||||
>>> m = M()
|
||||
>>> m.spam()
|
||||
X.spam
|
||||
Y.spam
|
||||
Z.spam
|
||||
Base.spam
|
||||
>>>
|
||||
```
|
||||
|
||||
Here, you see all of the classes stack together in the order supplied by the subclass.
|
||||
Suppose the subclass rearranges the class order:
|
||||
|
||||
```python
|
||||
>>> class N(Z,Y,X):
|
||||
pass
|
||||
|
||||
>>> N.__mro__
|
||||
(<class '__main__.N'>, <class '__main__.Z'>, <class '__main__.Y'>, <class '__main__.X'>, <class '__main__.Base'>, <class 'object'>)
|
||||
>>> n = N()
|
||||
>>> n.spam()
|
||||
Z.spam
|
||||
Y.spam
|
||||
X.spam
|
||||
Base.spam
|
||||
>>>
|
||||
```
|
||||
|
||||
Here, you see the order of the parents flip around. Carefully pay attention to what `super()`
|
||||
is doing in both cases. It doesn't delegate to the immediate parent of each class--instead,
|
||||
it moves to the next class on the MRO. Not only that, the exact order is controlled
|
||||
by the child. This is pretty weird.
|
||||
|
||||
Also notice that the common parent `Base` serves to terminate the chain of `super()` operations.
|
||||
Specifically, the `Base.spam()` method does not call any further methods. It also appears at
|
||||
the end of the MRO since it is the parent to all of the classes being composed together.
|
||||
|
||||
## (b) Build a Value Checker
|
||||
|
||||
In link:ex3_4.html[Exercise 3.4], you added some properties to the `Stock` class that
|
||||
checked attributes for different types and values (e.g., shares had to be a positive
|
||||
integer). Let's play with that idea a bit. Start by creating a file `validate.py` and
|
||||
defining the following base class:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
class Validator:
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
return value
|
||||
```
|
||||
|
||||
Now, let's make some classes for type checking:
|
||||
|
||||
```python
|
||||
class Typed(Validator):
|
||||
expected_type = object
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if not isinstance(value, cls.expected_type):
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
return super().check(value)
|
||||
|
||||
class Integer(Typed):
|
||||
expected_type = int
|
||||
|
||||
class Float(Typed):
|
||||
expected_type = float
|
||||
|
||||
class String(Typed):
|
||||
expected_type = str
|
||||
```
|
||||
|
||||
Here's how you use these classes (Note: the use of `@classmethod` allows us to
|
||||
avoid the extra step of creating instances which we don't really need):
|
||||
|
||||
```python
|
||||
>>> Integer.check(10)
|
||||
10
|
||||
>>> Integer.check('10')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in check
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
TypeError: Expected <class 'int'>
|
||||
>>> String.check('10')
|
||||
'10'
|
||||
>>>
|
||||
```
|
||||
|
||||
You could use the validators in a function. For example:
|
||||
|
||||
```python
|
||||
>>> def add(x, y):
|
||||
Integer.check(x)
|
||||
Integer.check(y)
|
||||
return x + y
|
||||
|
||||
>>> add(2, 2)
|
||||
4
|
||||
>>> add('2', '3')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "<stdin>", line 2, in add
|
||||
File "validate.py", line 11, in check
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
TypeError: Expected <class 'int'>
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, make some more classes for different kinds of domain checking:
|
||||
|
||||
```python
|
||||
class Positive(Validator):
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if value < 0:
|
||||
raise ValueError('Expected >= 0')
|
||||
return super().check(value)
|
||||
|
||||
class NonEmpty(Validator):
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if len(value) == 0:
|
||||
raise ValueError('Must be non-empty')
|
||||
return super().check(value)
|
||||
```
|
||||
|
||||
Where is all of this going? Let's start composing classes together with multiple inheritance like toy blocks:
|
||||
|
||||
```python
|
||||
class PositiveInteger(Integer, Positive):
|
||||
pass
|
||||
|
||||
class PositiveFloat(Float, Positive):
|
||||
pass
|
||||
|
||||
class NonEmptyString(String, NonEmpty):
|
||||
pass
|
||||
```
|
||||
|
||||
Essentially, you're taking existing validators and composing them
|
||||
together into new ones. Madness! However, let's use them to validate
|
||||
some things now:
|
||||
|
||||
```python
|
||||
>>> PositiveInteger.check(10)
|
||||
10
|
||||
>>> PositiveInteger.check('10')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
TypeError: Expected <class 'int'>
|
||||
>>> PositiveInteger.check(-10)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
raise ValueError('Expected >= 0')
|
||||
ValueError: Must be >= 0
|
||||
|
||||
|
||||
>>> NonEmptyString.check('hello')
|
||||
'hello'
|
||||
>>> NonEmptyString.check('')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
raise ValueError('Must be non-empty')
|
||||
ValueError: Must be non-empty
|
||||
>>>
|
||||
```
|
||||
|
||||
At this point, your head is probably fully exploded. However, the problem of composing
|
||||
different bits of code together is one that arises in real-world programs. Cooperative
|
||||
multiple inheritance is one of the tools that can be used to organize it.
|
||||
|
||||
## (c) Using your validators
|
||||
|
||||
Your validators can be used to add value checking to functions and classes. For
|
||||
example, perhaps the validators could be used in the properties of `Stock`:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
...
|
||||
@property
|
||||
def shares(self):
|
||||
return self._shares
|
||||
|
||||
@shares.setter
|
||||
def shares(self, value):
|
||||
self._shares = PositiveInteger.check(value)
|
||||
...
|
||||
```
|
||||
|
||||
Copy the `Stock` class from `stock.py` change it to use the validators in the property
|
||||
code for `shares` and `price`.
|
||||
|
||||
\[ [Solution](soln4_2.md) | [Index](index.md) | [Exercise 4.1](ex4_1.md) | [Exercise 4.3](ex4_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
235
Exercises/ex4_3.md
Normal file
235
Exercises/ex4_3.md
Normal file
@ -0,0 +1,235 @@
|
||||
\[ [Index](index.md) | [Exercise 4.2](ex4_2.md) | [Exercise 4.4](ex4_4.md) \]
|
||||
|
||||
# Exercise 4.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about descriptors
|
||||
|
||||
*Files Created:* `descrip.py`
|
||||
|
||||
*Files Modified:* `validate.py`
|
||||
|
||||
## (a) Descriptors in action
|
||||
|
||||
Earlier, you created a class `Stock` that made use of
|
||||
slots, properties, and other features. All of these features
|
||||
are implemented using the descriptor protocol. See it in
|
||||
action by trying this simple experiment.
|
||||
|
||||
First, create a stock object, and try looking up a few attributes:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.10)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, notice that these attributes are in the class dictionary.
|
||||
|
||||
```python
|
||||
>>> Stock.__dict__.keys()
|
||||
['sell', '__module__', '__weakref__', 'price', '_price', 'shares', '_shares',
|
||||
'__slots__', 'cost', '__repr__', '__doc__', '__init__']
|
||||
>>>
|
||||
```
|
||||
|
||||
Try these steps which illustrate how descriptors get and set values on an instance:
|
||||
|
||||
```python
|
||||
>>> q = Stock.__dict__['shares']
|
||||
>>> q.__get__(s)
|
||||
100
|
||||
>>> q.__set__(s,75)
|
||||
>>> s.shares
|
||||
75
|
||||
>>> q.__set__(s, '75')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "stock.py", line 23, in shares
|
||||
raise TypeError('Expected an integer')
|
||||
TypeError: Expected an integer
|
||||
>>>
|
||||
```
|
||||
|
||||
The execution of `__get__()` and `__set__()` occurs automatically whenever you access instances.
|
||||
|
||||
|
||||
## (b) Make your own descriptor
|
||||
|
||||
Define the descriptor class from the notes:
|
||||
|
||||
```python
|
||||
# descrip.py
|
||||
|
||||
class Descriptor:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
def __get__(self, instance, cls):
|
||||
print('%s:__get__' % self.name)
|
||||
def __set__(self, instance, value):
|
||||
print('%s:__set__ %s' % (self.name, value))
|
||||
def __delete__(self, instance):
|
||||
print('%s:__delete__' % self.name)
|
||||
```
|
||||
|
||||
Now, try defining a simple class that uses this descriptor:
|
||||
|
||||
```python
|
||||
>>> class Foo:
|
||||
a = Descriptor('a')
|
||||
b = Descriptor('b')
|
||||
c = Descriptor('c')
|
||||
|
||||
>>> f = Foo()
|
||||
>>> f
|
||||
<__main__.Foo object at 0x38e130> <class __main__.Foo>
|
||||
>>> f.a
|
||||
a:__get__
|
||||
>>> f.b
|
||||
b:__get__
|
||||
>>> f.a = 23
|
||||
a:__set__ 23
|
||||
>>> del f.a
|
||||
a:__delete__
|
||||
>>>
|
||||
```
|
||||
|
||||
Ponder the fact that you have captured the dot-operator for a
|
||||
specific attribute.
|
||||
|
||||
## (c) From Validators to Descriptors
|
||||
|
||||
In the previous exercise, you wrote a series of classes that could perform checking.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> PositiveInteger.check(10)
|
||||
10
|
||||
>>> PositiveInteger.check('10')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
raise TypeError('Expected %s' % cls.expected_type)
|
||||
TypeError: expected <class 'int'>
|
||||
>>> PositiveInteger.check(-10)
|
||||
```
|
||||
|
||||
You can extend this to descriptors by making a simple change to the `Validator` base
|
||||
class. Change it to the following:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
|
||||
class Validator:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
return value
|
||||
|
||||
def __set__(self, instance, value):
|
||||
instance.__dict__[self.name] = self.check(value)
|
||||
```
|
||||
|
||||
Note: The lack of the `__get__()` method in the descriptor means that
|
||||
Python will use its default implementation of attribute lookup. This
|
||||
requires that the supplied name matches the name used in the instance
|
||||
dictionary.
|
||||
|
||||
No other changes should be necessary. Now, try modifying the `Stock` class to
|
||||
use the validators as descriptors like this:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
name = String('name')
|
||||
shares = PositiveInteger('shares')
|
||||
price = PositiveFloat('price')
|
||||
|
||||
def __init__(self,name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
You'll find that your class works the same way as before, involves much
|
||||
less code, and gives you all of the desired checking:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.10)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.shares = 75
|
||||
>>> s.shares = '75'
|
||||
... TypeError ...
|
||||
>>> s.shares = -50
|
||||
... ValueError ...
|
||||
>>>
|
||||
```
|
||||
|
||||
This is pretty cool. Descriptors have allowed you to greatly simplify the implementation
|
||||
of the `Stock` class. This is the real power of descriptors--you get low level control
|
||||
over the dot and can use it to do amazing things.
|
||||
|
||||
## (d) Fixing the Names
|
||||
|
||||
One annoying thing about descriptors is the redundant name specification. For example:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
...
|
||||
shares = PositiveInteger('shares')
|
||||
...
|
||||
```
|
||||
|
||||
We can fix that. Change the top-level `Validator` class to include a `__set_name__()` method
|
||||
like this:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
|
||||
class Validator:
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
|
||||
def __set_name__(self, cls, name):
|
||||
self.name = name
|
||||
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
return value
|
||||
|
||||
def __set__(self, instance, value):
|
||||
instance.__dict__[self.name] = self.check(value)
|
||||
```
|
||||
|
||||
Now, try rewriting your `Stock` class so that it looks like this:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
def __init__(self,name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
Ah, much nicer. Be aware that this ability to set the name is a Python 3.6
|
||||
feature however. It won't work on older versions.
|
||||
|
||||
\[ [Solution](soln4_3.md) | [Index](index.md) | [Exercise 4.2](ex4_2.md) | [Exercise 4.4](ex4_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
157
Exercises/ex4_4.md
Normal file
157
Exercises/ex4_4.md
Normal file
@ -0,0 +1,157 @@
|
||||
\[ [Index](index.md) | [Exercise 4.3](ex4_3.md) | [Exercise 5.1](ex5_1.md) \]
|
||||
|
||||
# Exercise 4.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about customizing attribute access
|
||||
- Delegation vs. inheritance
|
||||
|
||||
## (a) Slots vs. setattr
|
||||
|
||||
In previous exercises, `__slots__` was used to list the instance
|
||||
attributes on a class. The primary purpose of slots is to optimize
|
||||
the use of memory. A secondary effect is that it strictly limits the
|
||||
allowed attributes to those listed. A downside of slots is that it
|
||||
often interacts strangely with other parts of Python (for example,
|
||||
classes using slots can't be used with multiple inheritance). For
|
||||
that reason, you really shouldn't use slots except in special cases.
|
||||
|
||||
If you really wanted to limit the set of allowed attributes, an
|
||||
alternate way to do it would be to define a `__setattr__()` method.
|
||||
Try this experiment:
|
||||
|
||||
```python
|
||||
>>> class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
def __setattr__(self, name, value):
|
||||
if name not in { 'name', 'shares', 'price' }:
|
||||
raise AttributeError('No attribute %s' % name)
|
||||
super().__setattr__(name, value)
|
||||
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares = 75
|
||||
>>> s.share = 50
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "<stdin>", line 8, in __setattr__
|
||||
AttributeError: No attribute share
|
||||
>>>
|
||||
```
|
||||
|
||||
In this example, there are no slots, but the `__setattr__()` method still restricts
|
||||
attributes to those in a predefined set. You'd probably need to
|
||||
think about how this approach might interact with inheritance (e.g., if subclasses wanted
|
||||
to add new attributes, they'd probably need to redefine `__setattr__()` to make it work).
|
||||
|
||||
## (b) Proxies
|
||||
|
||||
A proxy class is a class that wraps around an existing class and provides a similar interface.
|
||||
Define the following class which makes a read-only layer around an existing object:
|
||||
|
||||
```python
|
||||
>>> class Readonly:
|
||||
def __init__(self, obj):
|
||||
self.__dict__['_obj'] = obj
|
||||
def __setattr__(self, name, value):
|
||||
raise AttributeError("Can't set attribute")
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._obj, name)
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
To use the class, you simply wrap it around an existing instance:
|
||||
|
||||
```python
|
||||
>>> from stock import Stock
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> p = Readonly(s)
|
||||
>>> p.name
|
||||
'GOOG'
|
||||
>>> p.shares
|
||||
100
|
||||
>>> p.cost
|
||||
49010.0
|
||||
>>> p.shares = 50
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "<stdin>", line 8, in __setattr__
|
||||
AttributeError: Can't set attribute
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Delegation as an alternative to inheritance
|
||||
|
||||
Delegation is sometimes used as an alternative to inheritance. The idea is almost the
|
||||
same as the proxy class you defined in part (b). Try defining the following class:
|
||||
|
||||
```python
|
||||
>>> class Spam:
|
||||
def a(self):
|
||||
print('Spam.a')
|
||||
def b(self):
|
||||
print('Spam.b')
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, make a class that wraps around it and redefines some of the methods:
|
||||
|
||||
```python
|
||||
>>> class MySpam:
|
||||
def __init__(self):
|
||||
self._spam = Spam()
|
||||
def a(self):
|
||||
print('MySpam.a')
|
||||
self._spam.a()
|
||||
def c(self):
|
||||
print('MySpam.c')
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._spam, name)
|
||||
|
||||
>>> s = MySpam()
|
||||
>>> s.a()
|
||||
MySpam.a
|
||||
Spam.a
|
||||
>>> s.b()
|
||||
Spam.b
|
||||
>>> s.c()
|
||||
MySpam.c
|
||||
>>>
|
||||
```
|
||||
|
||||
Carefully notice that the resulting class looks very similar to
|
||||
inheritance. For example the `a()` method is doing something similar
|
||||
to the `super()` call. The method `b()` is picked up via the
|
||||
`__getattr__()` method which delegates to the internally held `Spam`
|
||||
instance.
|
||||
|
||||
**Discussion**
|
||||
|
||||
The `__getattr__()` method is commonly defined on classes that act as
|
||||
wrappers around other objects. However, you have to be aware that the
|
||||
process of wrapping another object in this manner often introduces
|
||||
other complexities. For example, the wrapper object might break
|
||||
type-checking if any other part of the application is using the
|
||||
`isinstance()` function.
|
||||
|
||||
Delegating methods through `__getattr__()` also doesn't work with special
|
||||
methods such as `__getitem__()`, `__enter__()`, and so forth. If a class
|
||||
makes extensive use of such methods, you'll have to provide similar functions
|
||||
in your wrapper class.
|
||||
|
||||
|
||||
\[ [Solution](soln4_4.md) | [Index](index.md) | [Exercise 4.3](ex4_3.md) | [Exercise 5.1](ex5_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
178
Exercises/ex5_1.md
Normal file
178
Exercises/ex5_1.md
Normal file
@ -0,0 +1,178 @@
|
||||
\[ [Index](index.md) | [Exercise 4.4](ex4_4.md) | [Exercise 5.2](ex5_2.md) \]
|
||||
|
||||
# Exercise 5.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Explore a few definitional aspects of functions/methods
|
||||
- Making functions more flexible
|
||||
- Type hints
|
||||
|
||||
In [Exercise 2.6](ex2_6.md) you wrote a `reader.py` module that
|
||||
had a function for reading a CSV into a list of dictionaries. For example:
|
||||
|
||||
```python
|
||||
>>> import reader
|
||||
>>> port = reader.read_csv_as_dicts('Data/portfolio.csv', [str,int,float])
|
||||
>>>
|
||||
```
|
||||
|
||||
We later expanded to that code to work with instances in
|
||||
[Exercise 3.3](ex3_3.md):
|
||||
|
||||
```python
|
||||
>>> import reader
|
||||
>>> from stock import Stock
|
||||
>>> port = reader.read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
>>>
|
||||
```
|
||||
|
||||
Eventually the code was refactored into a collection of classes
|
||||
involving inheritance in [Exercise 3.7](ex3_7.md). However,
|
||||
the code has become rather complex and convoluted.
|
||||
|
||||
## (a) Back to Basics
|
||||
|
||||
Start by reverting the changes related to class definitions. Rewrite
|
||||
the `reader.py` file so that it contains the two basic functions that
|
||||
you had before you messed it up with classes:
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
|
||||
def read_csv_as_dicts(filename, types):
|
||||
'''
|
||||
Read CSV data into a list of dictionaries with optional type conversion
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as file:
|
||||
rows = csv.reader(file)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = { name: func(val)
|
||||
for name, func, val in zip(headers, types, row) }
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def read_csv_as_instances(filename, cls):
|
||||
'''
|
||||
Read CSV data into a list of instances
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as file:
|
||||
rows = csv.reader(file)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = cls.from_row(row)
|
||||
records.append(record)
|
||||
return records
|
||||
```
|
||||
|
||||
Make sure the code still works as it did before:
|
||||
|
||||
```python
|
||||
>>> import reader
|
||||
>>> port = reader.read_csv_as_dicts('Data/portfolio.csv', [str, int, float])
|
||||
>>> port
|
||||
[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1},
|
||||
{'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23},
|
||||
{'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1},
|
||||
{'name': 'IBM', 'shares': 100, 'price': 70.44}]
|
||||
>>> import stock
|
||||
>>> port = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock)
|
||||
>>> port
|
||||
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44),
|
||||
Stock('MSFT', 200, 51.23), Stock('GE', 95, 40.37), Stock('MSFT', 50, 65.1),
|
||||
Stock('IBM', 100, 70.44)]
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Thinking about Flexibility
|
||||
|
||||
Right now, the two functions in `reader.py` are hard-wired to work
|
||||
with filenames that are passed directly to `open()`. Refactor the
|
||||
code so that it works with any iterable object that produces lines.
|
||||
To do this, create two new functions `csv_as_dicts(lines, types)` and
|
||||
`csv_as_instances(lines, cls)` that convert any iterable sequence of
|
||||
lines. For example:
|
||||
|
||||
```python
|
||||
>>> file = open('Data/portfolio.csv')
|
||||
>>> port = reader.csv_as_dicts(file, [str, int, float])
|
||||
>>> port
|
||||
[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1},
|
||||
{'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23},
|
||||
{'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1},
|
||||
{'name': 'IBM', 'shares': 100, 'price': 70.44}]
|
||||
>>>
|
||||
```
|
||||
|
||||
The whole point of doing this is to make it possible to work with different
|
||||
kinds of input sources. For example:
|
||||
|
||||
```python
|
||||
>>> import gzip
|
||||
>>> import stock
|
||||
>>> file = gzip.open('Data/portfolio.csv.gz')
|
||||
>>> port = reader.csv_as_instances(file, stock.Stock)
|
||||
>>> port
|
||||
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44),
|
||||
Stock('MSFT', 200, 51.23), Stock('GE', 95, 40.37), Stock('MSFT', 50, 65.1),
|
||||
Stock('IBM', 100, 70.44)]
|
||||
>>>
|
||||
```
|
||||
|
||||
To maintain backwards compatibility with older code, write functions
|
||||
`read_csv_as_dicts()` and `read_csv_as_instances()` that take a
|
||||
filename as before. These functions should call `open()` on the
|
||||
supplied filename and use the new `csv_as_dicts()` or
|
||||
`csv_as_instances()` functions on the resulting file.
|
||||
|
||||
## (c) Design Challenge: CSV Headers
|
||||
|
||||
The code assumes that the first line of CSV data always contains
|
||||
column headers. However, this isn't always the case. For example, the
|
||||
file `Data/portfolio_noheader.csv` contains data, but no column
|
||||
headers.
|
||||
|
||||
How would you refactor the code to accommodate missing column headers, having
|
||||
them supplied manually by the caller instead?
|
||||
|
||||
## (d) API Challenge: Type hints
|
||||
|
||||
Functions can have optional type-hints attached to arguments and return values.
|
||||
For example:
|
||||
|
||||
```python
|
||||
def add(x:int, y:int) -> int:
|
||||
return x + y
|
||||
```
|
||||
|
||||
The `typing` module has additional classes for expressing more complex kinds of
|
||||
types including containers. For example:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
|
||||
def sum_squares(nums: List[int]) -> int:
|
||||
total = 0
|
||||
for n in nums:
|
||||
total += n*n
|
||||
return total
|
||||
```
|
||||
|
||||
Your challenge: Modify the code in `reader.py` so that all functions
|
||||
have type hints. Try to make the type-hints as accurate as possible.
|
||||
To do this, you may need to consult the documentation for the
|
||||
[typing module](https://docs.python.org/3/library/typing.html).
|
||||
|
||||
\[ [Solution](soln5_1.md) | [Index](index.md) | [Exercise 4.4](ex4_4.md) | [Exercise 5.2](ex5_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
126
Exercises/ex5_2.md
Normal file
126
Exercises/ex5_2.md
Normal file
@ -0,0 +1,126 @@
|
||||
\[ [Index](index.md) | [Exercise 5.1](ex5_1.md) | [Exercise 5.3](ex5_3.md) \]
|
||||
|
||||
# Exercise 5.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Returning values from functions
|
||||
|
||||
In this exercise, we briefly look at problems related to returning values from functions.
|
||||
At first glance, it seems like this should be straightforward, but there are some
|
||||
subtle problems that arise.
|
||||
|
||||
## (a) Returning Multiple Values
|
||||
|
||||
Suppose you were writing code to parse configuration files consisting of lines like this:
|
||||
|
||||
name=value
|
||||
|
||||
Write a function `parse_line(line)` that takes such a line and returns both the associated name and
|
||||
value. The common convention for returning multiple values is to return them in a tuple. For example:
|
||||
|
||||
```python
|
||||
>>> parse_line('email=guido@python.org')
|
||||
('email', 'guido@python.org')
|
||||
>>> name, val = parse_line('email=guido@python.org')
|
||||
>>> name
|
||||
'email'
|
||||
>>> val
|
||||
'guido@python.org'
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Returning Optional Values
|
||||
|
||||
Sometimes a function might return an optional value--possibly as a mechanism for indicating
|
||||
success or failure. The most common convention is to use `None` as a representation for
|
||||
a missing value. Modify the `parse_line()` function above so that it either returns a tuple
|
||||
on success or `None` on bad data. For example:
|
||||
|
||||
```python
|
||||
>>> parse_line('email=guido@python.org')
|
||||
('email', 'guido@python.org')
|
||||
>>> parse_line('spam') # Returns None
|
||||
>>>
|
||||
```
|
||||
|
||||
Design discussion: Would it be better for the `parse_line()` function to raise an exception
|
||||
on malformed data?
|
||||
|
||||
## (c) Futures
|
||||
|
||||
Sometimes Python code executes concurrently via threads or processes. To illustrate, try
|
||||
this example:
|
||||
|
||||
```python
|
||||
>>> import time
|
||||
>>> def worker(x, y):
|
||||
print('About to work')
|
||||
time.sleep(20)
|
||||
print('Done')
|
||||
return x + y
|
||||
|
||||
>>> worker(2, 3) # Normal function call
|
||||
About to work
|
||||
Done
|
||||
5
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, launch `worker()` into a separate thread:
|
||||
|
||||
```python
|
||||
>>> import threading
|
||||
>>> t = threading.Thread(target=worker, args=(2, 3))
|
||||
>>> t.start()
|
||||
About to work
|
||||
>>>
|
||||
Done
|
||||
```
|
||||
|
||||
Carefully notice that the result of the calculation appears nowhere. Not only that, you don't even
|
||||
know when it's going to be completed. There is a certain coordination problem here. The
|
||||
convention for handling this case is to wrap the result of a function in a `Future`. A
|
||||
`Future` represents a future result. Here's how it works:
|
||||
|
||||
```python
|
||||
>>> from concurrent.futures import Future
|
||||
>>> # Wrapper around the function to use a future
|
||||
>>> def do_work(x, y, fut):
|
||||
fut.set_result(worker(x,y))
|
||||
|
||||
>>> fut = Future()
|
||||
>>> t = threading.Thread(target=do_work, args=(2, 3, fut))
|
||||
>>> t.start()
|
||||
About to work
|
||||
>>> result = fut.result()
|
||||
Done
|
||||
>>> result
|
||||
5
|
||||
>>>
|
||||
```
|
||||
|
||||
You'll see this kind of pattern a lot of if working with thread pools, processes, and other
|
||||
constructs. For example:
|
||||
|
||||
```python
|
||||
>>> from concurrent.futures import ThreadPoolExecutor
|
||||
>>> pool = ThreadPoolExecutor()
|
||||
>>> fut = pool.submit(worker, 2, 3)
|
||||
About to work
|
||||
>>> fut
|
||||
<Future at 0x102157080 state=running>
|
||||
>>> fut.result()
|
||||
Done
|
||||
5
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln5_2.md) | [Index](index.md) | [Exercise 5.1](ex5_1.md) | [Exercise 5.3](ex5_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
101
Exercises/ex5_3.md
Normal file
101
Exercises/ex5_3.md
Normal file
@ -0,0 +1,101 @@
|
||||
\[ [Index](index.md) | [Exercise 5.2](ex5_2.md) | [Exercise 5.4](ex5_4.md) \]
|
||||
|
||||
# Exercise 5.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Higher order functions
|
||||
|
||||
*Files Modified:* `reader.py`
|
||||
|
||||
## (a) Using higher-order functions
|
||||
|
||||
At the moment, the `reader.py` program consists of two core functions, `csv_as_dicts()` and
|
||||
`csv_as_instances()`. The code in these two functions is almost identical. For example:
|
||||
|
||||
```python
|
||||
def csv_as_dicts(lines, types, *, headers=None):
|
||||
'''
|
||||
Convert lines of CSV data into a list of dictionaries
|
||||
'''
|
||||
records = []
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = { name: func(val)
|
||||
for name, func, val in zip(headers, types, row) }
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def csv_as_instances(lines, cls, *, headers=None):
|
||||
'''
|
||||
Convert lines of CSV data into a list of instances
|
||||
'''
|
||||
records = []
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = cls.from_row(row)
|
||||
records.append(record)
|
||||
return records
|
||||
```
|
||||
|
||||
Unify the core of these functions into a single function `convert_csv()` that accepts a user-defined
|
||||
conversion function as an argument. For example:
|
||||
|
||||
```python
|
||||
>>> def make_dict(headers, row):
|
||||
return dict(zip(headers, row))
|
||||
|
||||
>>> lines = open('Data/portfolio.csv')
|
||||
>>> convert_csv(lines, make_dict)
|
||||
[{'name': 'AA', 'shares': '100', 'price': '32.20'}, {'name': 'IBM', 'shares': '50', 'price': '91.10'},
|
||||
{'name': 'CAT', 'shares': '150', 'price': '83.44'}, {'name': 'MSFT', 'shares': '200', 'price': '51.23'},
|
||||
{'name': 'GE', 'shares': '95', 'price': '40.37'}, {'name': 'MSFT', 'shares': '50', 'price': '65.10'},
|
||||
{'name': 'IBM', 'shares': '100', 'price': '70.44'}]
|
||||
>>>
|
||||
```
|
||||
|
||||
Rewrite the `csv_as_dicts()` and `csv_as_instances()` functions in terms of the new `convert_csv()`
|
||||
function.
|
||||
|
||||
## (b) Mapping
|
||||
|
||||
One of the most common operations in functional programming is the `map()` operation that maps a function
|
||||
to the values in a sequence. Python has a built-in `map()` function that does this. For
|
||||
example:
|
||||
|
||||
```python
|
||||
>>> nums = [1,2,3,4]
|
||||
>>> squares = map(lambda x: x*x, nums)
|
||||
>>> for n in squares:
|
||||
print(n)
|
||||
|
||||
1
|
||||
4
|
||||
9
|
||||
16
|
||||
>>>
|
||||
```
|
||||
|
||||
`map()` produces an iterator so if you want a list, you'll need to create it explicitly:
|
||||
|
||||
```python
|
||||
>>> squares = list(map(lambda x: x*x, nums))
|
||||
>>> squares
|
||||
[1, 4, 9, 16]
|
||||
>>>
|
||||
```
|
||||
|
||||
Try to use `map()` in your `convert_csv()` function.
|
||||
|
||||
\[ [Solution](soln5_3.md) | [Index](index.md) | [Exercise 5.2](ex5_2.md) | [Exercise 5.4](ex5_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
153
Exercises/ex5_4.md
Normal file
153
Exercises/ex5_4.md
Normal file
@ -0,0 +1,153 @@
|
||||
\[ [Index](index.md) | [Exercise 5.3](ex5_3.md) | [Exercise 5.5](ex5_5.md) \]
|
||||
|
||||
# Exercise 5.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about closures
|
||||
|
||||
In this section, we look briefly at a few of the more unusual aspects of
|
||||
closures.
|
||||
|
||||
## (a) Closures as a data structure
|
||||
|
||||
One potential use of closures is as a tool for data encapsulation. Try this
|
||||
example:
|
||||
|
||||
```python
|
||||
def counter(value):
|
||||
def incr():
|
||||
nonlocal value
|
||||
value += 1
|
||||
return value
|
||||
|
||||
def decr():
|
||||
nonlocal value
|
||||
value -= 1
|
||||
return value
|
||||
|
||||
return incr, decr
|
||||
```
|
||||
|
||||
This code defines two inner functions that manipulate a value. Try it out:
|
||||
|
||||
```python
|
||||
>>> up, down = counter(0)
|
||||
>>> up()
|
||||
1
|
||||
>>> up()
|
||||
2
|
||||
>>> up()
|
||||
3
|
||||
>>> down()
|
||||
2
|
||||
>>> down()
|
||||
1
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how there is no class definition involved here. Moreover,
|
||||
there is no global variable either. Yet, the `up()` and `down()`
|
||||
functions are manipulating some "behind the scenes" value. It's
|
||||
fairly magical.
|
||||
|
||||
## (b) Closures as a code generator
|
||||
|
||||
In [Exercise 4.3](ex4_3.md), you developed a collection of
|
||||
descriptor classes that allowed type-checking of object attributes.
|
||||
For example:
|
||||
|
||||
```python
|
||||
|
||||
class Stock:
|
||||
name = String()
|
||||
shares = Integer()
|
||||
price = Float()
|
||||
```
|
||||
|
||||
This kind of thing can also be implemented using closures. Define a file
|
||||
``typedproperty.py`` and put the following code in it:
|
||||
|
||||
```python
|
||||
# typedproperty.py
|
||||
|
||||
def typedproperty(name, expected_type):
|
||||
private_name = '_' + name
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return getattr(self, private_name)
|
||||
|
||||
@value.setter
|
||||
def value(self, val):
|
||||
if not isinstance(val, expected_type):
|
||||
raise TypeError(f'Expected {expected_type}')
|
||||
setattr(self, private_name, val)
|
||||
|
||||
return value
|
||||
```
|
||||
|
||||
This look pretty wild, but the function is effectively making code. You'd use it in
|
||||
a class definition like this:
|
||||
|
||||
```python
|
||||
from typedproperty import typedproperty
|
||||
|
||||
class Stock:
|
||||
name = typedproperty('name', str)
|
||||
shares = typedproperty('shares', int)
|
||||
price = typedproperty('price', float)
|
||||
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
Verify that this class performs type-checking in the same way as the
|
||||
descriptor code.
|
||||
|
||||
Add function `String()`, `Integer()`, and `Float()` to the `typedproperty.py` file
|
||||
so that you can write the following code:
|
||||
|
||||
```python
|
||||
from typedproperty import String, Integer, Float
|
||||
|
||||
class Stock:
|
||||
name = String('name')
|
||||
shares = Integer('shares')
|
||||
price = Float('price')
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
## (c) Challenge: Eliminating names
|
||||
|
||||
Modify the `typedproperty.py` code so that attribute names are no-longer required:
|
||||
|
||||
```python
|
||||
from typedproperty import String, Integer, Float
|
||||
|
||||
class Stock:
|
||||
name = String()
|
||||
shares = Integer()
|
||||
price = Float()
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
Hint: To do this, recall the `__set_name__()` method of descriptor objects that
|
||||
gets called when descriptors are placed in a class definition.
|
||||
|
||||
\[ [Solution](soln5_4.md) | [Index](index.md) | [Exercise 5.3](ex5_3.md) | [Exercise 5.5](ex5_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
97
Exercises/ex5_5.md
Normal file
97
Exercises/ex5_5.md
Normal file
@ -0,0 +1,97 @@
|
||||
\[ [Index](index.md) | [Exercise 5.4](ex5_4.md) | [Exercise 5.6](ex5_6.md) \]
|
||||
|
||||
# Exercise 5.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about exception handling and logging
|
||||
|
||||
*Files Modified:* `reader.py`
|
||||
|
||||
In the `reader.py` file, there is a central function `convert_csv()` that does
|
||||
most of the work. This function crashes if you run it on data with missing or
|
||||
bad data. For example:
|
||||
|
||||
```python
|
||||
>>> port = read_csv_as_dicts('Data/missing.csv', types=[str, int, float])
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "reader.py", line 24, in read_csv_as_dicts
|
||||
return csv_as_dicts(file, types, headers=headers)
|
||||
File "reader.py", line 13, in csv_as_dicts
|
||||
lambda headers, row: { name: func(val) for name, func, val in zip(headers, types, row) })
|
||||
File "reader.py", line 9, in convert_csv
|
||||
return list(map(lambda row: converter(headers, row), rows))
|
||||
File "reader.py", line 9, in <lambda>
|
||||
return list(map(lambda row: converter(headers, row), rows))
|
||||
File "reader.py", line 13, in <lambda>
|
||||
lambda headers, row: { name: func(val) for name, func, val in zip(headers, types, row) })
|
||||
File "reader.py", line 13, in <dictcomp>
|
||||
lambda headers, row: { name: func(val) for name, func, val in zip(headers, types, row) })
|
||||
ValueError: invalid literal for int() with base 10: ''
|
||||
>>>
|
||||
```
|
||||
|
||||
## (a) Catching Exceptions
|
||||
|
||||
Instead of crashing on bad data, modify the code to issue a warning message
|
||||
instead. The final result should be a list of the rows that were successfully
|
||||
converted. For example:
|
||||
|
||||
```python
|
||||
>>> port = read_csv_as_dicts('Data/missing.csv', types=[str, int, float])
|
||||
Row 4: Bad row: ['C', '', '53.08']
|
||||
Row 7: Bad row: ['DIS', '50', 'N/A']
|
||||
Row 8: Bad row: ['GE', '', '37.23']
|
||||
Row 13: Bad row: ['INTC', '', '21.84']
|
||||
Row 17: Bad row: ['MCD', '', '51.11']
|
||||
Row 19: Bad row: ['MO', '', '70.09']
|
||||
Row 22: Bad row: ['PFE', '', '26.40']
|
||||
Row 26: Bad row: ['VZ', '', '42.92']
|
||||
>>> len(port)
|
||||
20
|
||||
>>>
|
||||
```
|
||||
|
||||
Note: Making this change may be a bit tricky because of your previous use of the `map()`
|
||||
built-in function. You may have to abandon that approach since there's no easy way to catch
|
||||
and handle exceptions in `map()`.
|
||||
|
||||
## (b) Logging
|
||||
|
||||
Modify the code so that warning messages are issued using the `logging` module. In
|
||||
addition, give optional debugging information indicating the reason for failure.
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> import reader
|
||||
>>> import logging
|
||||
>>> logging.basicConfig(level=logging.DEBUG)
|
||||
>>> port = reader.read_csv_as_dicts('Data/missing.csv', types=[str, int, float])
|
||||
WARNING:reader:Row 4: Bad row: ['C', '', '53.08']
|
||||
DEBUG:reader:Row 4: Reason: invalid literal for int() with base 10: ''
|
||||
WARNING:reader:Row 7: Bad row: ['DIS', '50', 'N/A']
|
||||
DEBUG:reader:Row 7: Reason: could not convert string to float: 'N/A'
|
||||
WARNING:reader:Row 8: Bad row: ['GE', '', '37.23']
|
||||
DEBUG:reader:Row 8: Reason: invalid literal for int() with base 10: ''
|
||||
WARNING:reader:Row 13: Bad row: ['INTC', '', '21.84']
|
||||
DEBUG:reader:Row 13: Reason: invalid literal for int() with base 10: ''
|
||||
WARNING:reader:Row 17: Bad row: ['MCD', '', '51.11']
|
||||
DEBUG:reader:Row 17: Reason: invalid literal for int() with base 10: ''
|
||||
WARNING:reader:Row 19: Bad row: ['MO', '', '70.09']
|
||||
DEBUG:reader:Row 19: Reason: invalid literal for int() with base 10: ''
|
||||
WARNING:reader:Row 22: Bad row: ['PFE', '', '26.40']
|
||||
DEBUG:reader:Row 22: Reason: invalid literal for int() with base 10: ''
|
||||
WARNING:reader:Row 26: Bad row: ['VZ', '', '42.92']
|
||||
DEBUG:reader:Row 26: Reason: invalid literal for int() with base 10: ''
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln5_5.md) | [Index](index.md) | [Exercise 5.4](ex5_4.md) | [Exercise 5.6](ex5_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
104
Exercises/ex5_6.md
Normal file
104
Exercises/ex5_6.md
Normal file
@ -0,0 +1,104 @@
|
||||
\[ [Index](index.md) | [Exercise 5.5](ex5_5.md) | [Exercise 6.1](ex6_1.md) \]
|
||||
|
||||
# Exercise 5.6
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to use Python's unittest module
|
||||
|
||||
*Files Created:* `teststock.py`
|
||||
|
||||
In this exercise, you will explore the basic mechanics of using
|
||||
Python's `unittest` modules.
|
||||
|
||||
## (a) Preliminaries
|
||||
|
||||
In previous exercises, you created a file `stock.py` that contained
|
||||
a `Stock` class. In a separate file, `teststock.py`, define the following
|
||||
testing code:
|
||||
|
||||
```python
|
||||
# teststock.py
|
||||
|
||||
import unittest
|
||||
import stock
|
||||
|
||||
class TestStock(unittest.TestCase):
|
||||
def test_create(self):
|
||||
s = Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
```
|
||||
|
||||
Make sure you can run the file:
|
||||
|
||||
```
|
||||
bash % python3 teststock.py
|
||||
.
|
||||
------------------------------------------------------------------```
|
||||
Ran 1 tests in 0.001s
|
||||
|
||||
OK
|
||||
bash %
|
||||
```
|
||||
|
||||
## (b) Unit testing
|
||||
|
||||
Using the code in `teststock.py` as a guide, extend the `TestStock` class
|
||||
with tests for the following:
|
||||
|
||||
- Test that you can create a `Stock` using keyword arguments such as `Stock(name='GOOG',shares=100,price=490.1)`.
|
||||
- Test that the `cost` property returns a correct value
|
||||
- Test that the `sell()` method correctly updates the shares.
|
||||
- Test that the `from_row()` class method creates a new instance from good data.
|
||||
- Test that the `__repr__()` method creates a proper representation string.
|
||||
- Test the comparison operator method `__eq__()`
|
||||
|
||||
## (c) Unit tests with expected errors
|
||||
|
||||
Suppose you wanted to write a unit test that checks for an exception.
|
||||
Here is how you can do it:
|
||||
|
||||
```python
|
||||
class TestStock(unittest.TestCase):
|
||||
...
|
||||
def test_bad_shares(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
with self.assertRaises(TypeError):
|
||||
s.shares = '50'
|
||||
...
|
||||
```
|
||||
|
||||
Using this test as a guide, write unit tests for the following failure modes:
|
||||
|
||||
- Test that setting `shares` to a string raises a `TypeError`
|
||||
- Test that setting `shares` to a negative number raises a `ValueError`
|
||||
- Test that setting `price` to a string raises a `TypeError`
|
||||
- Test that setting `price` to a negative number raises a `ValueError`
|
||||
- Test that setting a non-existent attribute `share` raises an `AttributeError`
|
||||
|
||||
In total, you should have around a dozen unit tests when you're done.
|
||||
|
||||
**Important Note**
|
||||
|
||||
For later use in the course, you will want to have a fully working
|
||||
`stock.py` and `teststock.py` file. Save your work in progress if you
|
||||
have to, but you are strongly encouraged to copy the code from
|
||||
`Solutions/5_6` if things are still broken at this point.
|
||||
|
||||
We're going to use the `teststock.py` file as a tool for improving the `Stock` code
|
||||
later. You'll want it on hand to make sure that the new code behaves the same way
|
||||
as the old code.
|
||||
|
||||
\[ [Solution](soln5_6.md) | [Index](index.md) | [Exercise 5.5](ex5_5.md) | [Exercise 6.1](ex6_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
195
Exercises/ex6_1.md
Normal file
195
Exercises/ex6_1.md
Normal file
@ -0,0 +1,195 @@
|
||||
\[ [Index](index.md) | [Exercise 5.6](ex5_6.md) | [Exercise 6.2](ex6_2.md) \]
|
||||
|
||||
# Exercise 6.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about function argument passing conventions
|
||||
|
||||
*Files Created:* `structure.py`, `stock.py`
|
||||
|
||||
|
||||
**IMPORTANT NOTE**
|
||||
|
||||
This exercise is going to start a long road of rewriting the `stock.py` file in a more
|
||||
sane way. Before doing anything, copy your work in `stock.py` to a new file
|
||||
`orig_stock.py`.
|
||||
|
||||
We're going to recreate the `Stock` class from scratch using some new techniques.
|
||||
Make sure you have your unit tests from link:ex5_4.html[Exercise 5.4] handy. You'll want those.
|
||||
|
||||
If you define a function, you probably already know that it can be
|
||||
called using a mix of positional or keyword arguments. For example:
|
||||
|
||||
```python
|
||||
>>> def foo(x, y, z):
|
||||
return x + y + z
|
||||
|
||||
>>> foo(1, 2, 3)
|
||||
6
|
||||
>>> foo(1, z=3, y=2)
|
||||
6
|
||||
>>>
|
||||
```
|
||||
|
||||
You may also know that you can pass sequences and dictionaries as
|
||||
function arguments using the * and ** syntax. For example:
|
||||
|
||||
```python
|
||||
>>> args = (1, 2, 3)
|
||||
>>> foo(*args)
|
||||
6
|
||||
>>> kwargs = {'y':2, 'z':3 }
|
||||
>>> foo(1,**kwargs)
|
||||
6
|
||||
>>>
|
||||
```
|
||||
|
||||
In addition to that, you can write functions that accept any number of
|
||||
positional or keyword arguments using the * and ** syntax. For
|
||||
example:
|
||||
|
||||
```python
|
||||
>>> def foo(*args):
|
||||
print(args)
|
||||
|
||||
>>> foo(1,2)
|
||||
(1, 2)
|
||||
>>> foo(1,2,3,4,5)
|
||||
(1, 2, 3, 4, 5)
|
||||
>>> foo()
|
||||
()
|
||||
>>>
|
||||
>>> def bar(**kwargs):
|
||||
print(kwargs)
|
||||
|
||||
>>> bar(x=1,y=2)
|
||||
{'y': 2, 'x': 1}
|
||||
>>> bar(x=1,y=2,z=3)
|
||||
{'y': 2, 'x': 1, 'z': 3}
|
||||
>>> bar()
|
||||
{}
|
||||
>>>
|
||||
```
|
||||
|
||||
Variable argument functions are sometimes useful as a technique for
|
||||
reducing or simplifying the amount of code you need to type. In this
|
||||
exercise, we'll explore that idea for simple data structures.
|
||||
|
||||
## (a) Simplified Data Structures
|
||||
|
||||
In earlier exercises, you defined a class representing a stock like
|
||||
this:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
def __init__(self,name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
Focus on the `__init__()` method---isn't that a lot of
|
||||
code to type each time you want to populate a structure? What if you
|
||||
had to define dozens of such structures in your program?
|
||||
|
||||
In a file `structure.py`, define a base class
|
||||
`Structure` that allows the user to define simple
|
||||
data structures as follows:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
_fields = ('name','shares','price')
|
||||
|
||||
class Date(Structure):
|
||||
_fields = ('year', 'month', 'day')
|
||||
```
|
||||
|
||||
The `Structure` class should define an `__init__()`
|
||||
method that takes any number of arguments and which looks for the
|
||||
presence of a `_fields` class variable. Have the method
|
||||
populate the instance from the attribute names in `_fields`
|
||||
and values passed to `__init__()`.
|
||||
|
||||
Here is some sample code to test your implementation:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG',100,490.1)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.price
|
||||
490.1
|
||||
>>> s = Stock('AA',50)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Expected 3 arguments
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Making a Useful Representation
|
||||
|
||||
Modify the `Structure` class so that it produces a nice
|
||||
representation when `repr()` is used. For example:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s
|
||||
Stock('GOOG',100,490.1)
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Restricting Attribute Names
|
||||
|
||||
Give the `Structure` class a `__setattr__()` method that restricts
|
||||
the allowed set of attributes to those listed in the `_fields` variable.
|
||||
However, it should still allow any "private" attribute (e.g., name starting
|
||||
with `_` to be set).
|
||||
|
||||
For example:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG',100,490.1)
|
||||
>>> s.shares = 50
|
||||
>>> s.share = 50
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "structure.py", line 13, in __setattr__
|
||||
raise AttributeError('No attribute %s' % name)
|
||||
AttributeError: No attribute share
|
||||
>>> s._shares = 100 # Private attribute. OK
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Starting Over
|
||||
|
||||
Create a new file `stock.py` (or delete all of your previous code). Start over by defining `Stock` as follows:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure
|
||||
|
||||
class Stock(Structure):
|
||||
_fields = ('name', 'shares', 'price')
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
Once you've done this, run your `teststock.py` unit tests. You should get a lot of failures, but at least a
|
||||
handful of the tests should pass.
|
||||
|
||||
\[ [Solution](soln6_1.md) | [Index](index.md) | [Exercise 5.6](ex5_6.md) | [Exercise 6.2](ex6_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
197
Exercises/ex6_2.md
Normal file
197
Exercises/ex6_2.md
Normal file
@ -0,0 +1,197 @@
|
||||
\[ [Index](index.md) | [Exercise 6.1](ex6_1.md) | [Exercise 6.3](ex6_3.md) \]
|
||||
|
||||
# Exercise 6.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about scoping rules
|
||||
- Learn some scoping tricks
|
||||
|
||||
*Files modified:* `structure.py`, `stock.py`
|
||||
|
||||
In the last exercise, you create a class `Structure` that made it easy to define
|
||||
data structures. For example:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
_fields = ('name','shares','price')
|
||||
```
|
||||
|
||||
This works fine except that a lot of things are pretty weird about the `__init__()`
|
||||
function. For example, if you ask for help using `help(Stock)`, you don't get
|
||||
any kind of useful signature. Also, keyword argument passing doesn't work. For
|
||||
example:
|
||||
|
||||
```python
|
||||
>>> help(Stock)
|
||||
... look at output ...
|
||||
|
||||
>>> s = Stock(name='GOOG', shares=100, price=490.1)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: __init__() got an unexpected keyword argument 'price'
|
||||
>>>
|
||||
```
|
||||
|
||||
In this exercise, we're going to look at a different approach to the problem.
|
||||
|
||||
## (a) Show me your locals
|
||||
|
||||
First, try an experiment by defining the following class:
|
||||
|
||||
```python
|
||||
>>> class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
print(locals())
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, try running this:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
{'self': <__main__.Stock object at 0x100699b00>, 'price': 490.1, 'name': 'GOOG', 'shares': 100}
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how the locals dictionary contains all of the arguments passed
|
||||
to `__init__()`. That's interesting. Now, define the following function
|
||||
and class definitions:
|
||||
|
||||
```python
|
||||
>>> def _init(locs):
|
||||
self = locs.pop('self')
|
||||
for name, val in locs.items():
|
||||
setattr(self, name, val)
|
||||
|
||||
>>> class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
_init(locals())
|
||||
```
|
||||
|
||||
In this code, the `_init()` function is used to automatically
|
||||
initialize an object from a dictionary of passed local variables.
|
||||
You'll find that `help(Stock)` and keyword arguments work perfectly.
|
||||
|
||||
```python
|
||||
>>> s = Stock(name='GOOG', price=490.1, shares=50)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
50
|
||||
>>> s.price
|
||||
490.1
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Frame Hacking
|
||||
|
||||
One complaint about the last part is that the `__init__()` function
|
||||
now looks pretty weird with that call to `locals()` inserted into it.
|
||||
You can get around that though if you're willing to do a bit of stack
|
||||
frame hacking. Try this variant of the `_init()` function:
|
||||
|
||||
```python
|
||||
>>> import sys
|
||||
>>> def _init():
|
||||
locs = sys._getframe(1).f_locals # Get callers local variables
|
||||
self = locs.pop('self')
|
||||
for name, val in locs.items():
|
||||
setattr(self, name, val)
|
||||
>>>
|
||||
```
|
||||
|
||||
In this code, the local variables are extracted from the stack frame of the caller.
|
||||
Here is a modified class definition:
|
||||
|
||||
```python
|
||||
>>> class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
_init()
|
||||
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>>
|
||||
```
|
||||
|
||||
At this point, you're probably feeling rather disturbed. Yes, you just wrote a function that reached
|
||||
into the stack frame of another function and examined its local variables.
|
||||
|
||||
## (c) Putting it Together
|
||||
|
||||
Taking the ideas in the first two parts, delete the `__init__()` method that was originally part of the
|
||||
`Structure` class. Next, add an `_init()` method like this:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
import sys
|
||||
|
||||
class Structure:
|
||||
...
|
||||
@staticmethod
|
||||
def _init():
|
||||
locs = sys._getframe(1).f_locals
|
||||
self = locs.pop('self')
|
||||
for name, val in locs.items():
|
||||
setattr(self, name, val)
|
||||
...
|
||||
```
|
||||
|
||||
Note: The reason this is defined as a `@staticmethod` is that the `self` argument
|
||||
is obtained from the locals--there's no need to additionally have it passed as
|
||||
an argument to the method itself (admittedly this is a bit subtle).
|
||||
|
||||
Now, modify your `Stock` class so that it looks like the following:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
from structure import Structure
|
||||
|
||||
class Stock(Structure):
|
||||
_fields = ('name','shares','price')
|
||||
def __init__(self, name, shares, price):
|
||||
self._init()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= shares
|
||||
```
|
||||
|
||||
Verify that the class works properly, supports keyword arguments, and has a
|
||||
proper help signature.
|
||||
|
||||
```python
|
||||
>>> s = Stock(name='GOOG', price=490.1, shares=50)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
50
|
||||
>>> s.price
|
||||
490.1
|
||||
>>> help(Stock)
|
||||
... look at the output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Run your unit tests in `teststock.py` again. You should see at least one more test pass. Yay!
|
||||
|
||||
At this point, it's going to look like we just took a giant step backwards. Not
|
||||
only do the classes need the `__init__()` method, they also need the `_fields`
|
||||
variable for some of the other methods to work (`__repr__()` and `__setattr__()`). Plus,
|
||||
the use of `self._init()` looks pretty hacky. We'll work on this, but be patient.
|
||||
|
||||
\[ [Solution](soln6_2.md) | [Index](index.md) | [Exercise 6.1](ex6_1.md) | [Exercise 6.3](ex6_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
152
Exercises/ex6_3.md
Normal file
152
Exercises/ex6_3.md
Normal file
@ -0,0 +1,152 @@
|
||||
\[ [Index](index.md) | [Exercise 6.2](ex6_2.md) | [Exercise 6.4](ex6_4.md) \]
|
||||
|
||||
# Exercise 6.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to inspect the internals of functions
|
||||
|
||||
*Files Modified:* `structure.py`
|
||||
|
||||
## (a) Inspecting functions
|
||||
|
||||
Define a simple function:
|
||||
|
||||
```python
|
||||
>>> def add(x,y):
|
||||
'Adds two things'
|
||||
return x+y
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Do a `dir()` on the function to look at its attributes.
|
||||
|
||||
```python
|
||||
>>> dir(add)
|
||||
... look at the result ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Get some basic information such as the function name, defining module name, and documentation string.
|
||||
|
||||
```python
|
||||
>>> add.__name__
|
||||
'add'
|
||||
>>> add.__module__
|
||||
'__main__'
|
||||
>>> add.__doc__
|
||||
'Adds two things'
|
||||
>>>
|
||||
```
|
||||
|
||||
The `__code__` attribute of a function has low-level information about
|
||||
the function implementation. See if you can look at this and
|
||||
determine the number of required arguments and names of local
|
||||
variables.
|
||||
|
||||
## (b) Using the inspect module
|
||||
|
||||
Use the inspect module to get calling information about the function:
|
||||
|
||||
```python
|
||||
>>> import inspect
|
||||
>>> sig = inspect.signature(add)
|
||||
>>> sig
|
||||
<Signature (x, y)>
|
||||
>>> sig.parameters
|
||||
mappingproxy(OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y">)]))
|
||||
>>> tuple(sig.parameters)
|
||||
('x', 'y')
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) Putting it Together
|
||||
|
||||
In link:ex6_1.html[Exercise 6.1], you created a class `Structure`
|
||||
that defined a generalized `__init__()`, `__setattr__()`, and `__repr__()`
|
||||
method. That class required a user to define a `_fields` class
|
||||
variable like this:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
_fields = ('name','shares','price')
|
||||
```
|
||||
|
||||
The problem with this class is that the `__init__()` function didn't
|
||||
have a useful argument signature for the purposes of help and
|
||||
keyword argument passing. In link:ex6_2.html[Exercise 6.2], you
|
||||
did a sneaky trick involving a special `self._init()` function. For example:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
_fields = ('name', 'shares', 'price')
|
||||
def __init__(self, name, shares, price):
|
||||
self._init()
|
||||
...
|
||||
```
|
||||
|
||||
This gave a useful signature, but now the class is just weird because
|
||||
the user has to provide both the `_fields` variable and the `__init__()` method.
|
||||
|
||||
Your task is to eliminate the `_fields` variable using some function
|
||||
inspection techniques. First, notice that you can get the argument
|
||||
signature from `Stock` as follows:
|
||||
|
||||
```python
|
||||
>>> import inspect
|
||||
>>> sig = inspect.signature(Stock)
|
||||
>>> tuple(sig.parameters)
|
||||
('name', 'shares', 'price')
|
||||
>>>
|
||||
```
|
||||
|
||||
Perhaps you could set the `_fields` variable from the argument signature
|
||||
of `__init__()`. Add a class method `set_fields(cls)` to `Structure` that
|
||||
inspects the `__init__()` function, and sets the `_fields`
|
||||
variable appropriately. You should use your new function like this:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
def __init__(self, name, shares, price):
|
||||
self._init()
|
||||
|
||||
...
|
||||
|
||||
Stock.set_fields()
|
||||
```
|
||||
|
||||
The resulting class should work the same way as before:
|
||||
|
||||
```python
|
||||
>>> s = Stock(name='GOOG', shares=100, price=490.1)
|
||||
>>> s
|
||||
Stock('GOOG',100,490.1)
|
||||
>>> s.shares = 50
|
||||
>>> s.share = 50
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "structure.py", line 12, in __setattr__
|
||||
raise AttributeError('No attribute %s' % name)
|
||||
AttributeError: No attribute share
|
||||
>>>
|
||||
```
|
||||
|
||||
Verify the slightly modified `Stock` class with your unit tests again. There will still
|
||||
be failures, but nothing should change from the previous exercise.
|
||||
|
||||
At this point, it's all still a bit "hacky", but you're making
|
||||
progress. You have a Stock structure class with a useful `__init__()`
|
||||
function, there is a useful representation string, and the
|
||||
`__setattr__()` method restricts the set of attribute names. The
|
||||
extra step of having to invoke `set_fields()` is a bit odd, but we'll
|
||||
get back to that.
|
||||
|
||||
\[ [Solution](soln6_3.md) | [Index](index.md) | [Exercise 6.2](ex6_2.md) | [Exercise 6.4](ex6_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
145
Exercises/ex6_4.md
Normal file
145
Exercises/ex6_4.md
Normal file
@ -0,0 +1,145 @@
|
||||
\[ [Index](index.md) | [Exercise 6.3](ex6_3.md) | [Exercise 6.5](ex6_5.md) \]
|
||||
|
||||
# Exercise 6.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn to create code with `exec()`
|
||||
|
||||
## (a) Experiment with exec()
|
||||
|
||||
Define a fragment of Python code in a string and try running it:
|
||||
|
||||
```python
|
||||
>>> code = '''
|
||||
for i in range(n):
|
||||
print(i, end=' ')
|
||||
'''
|
||||
>>> n = 10
|
||||
>>> exec(code)
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
>>>
|
||||
```
|
||||
|
||||
That's interesting, but executing random code fragments is not
|
||||
especially useful. A more interesting use of `exec()` is in making
|
||||
code such as functions, methods, or classes. Try this example in
|
||||
which we make an `__init__()` function for a class.
|
||||
|
||||
```python
|
||||
>>> class Stock:
|
||||
_fields = ('name', 'shares', 'price')
|
||||
|
||||
>>> argstr = ','.join(Stock._fields)
|
||||
>>> code = f'def __init__(self, {argstr}):\n'
|
||||
>>> for name in Stock._fields:
|
||||
code += f' self.{name} = {name}\n'
|
||||
>>> print(code)
|
||||
def __init__(self, name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
>>> locs = { }
|
||||
>>> exec(code, locs)
|
||||
>>> Stock.__init__ = locs['__init__']
|
||||
|
||||
>>> # Now try the class
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s.price
|
||||
490.1
|
||||
>>>
|
||||
```
|
||||
|
||||
In this example, an `__init__()` function is made directly from the `_fields` variable.
|
||||
There are no weird hacks involving a special `_init()` method or stack frames.
|
||||
|
||||
## (b) Creating an `__init__()` function
|
||||
|
||||
In link:ex6_3.txt[Exercise 6.3], you wrote code that inspected the
|
||||
signature of the `__init__()` method to set the attribute names
|
||||
in a `_fields` class variable. For example:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
def __init__(self, name, shares, price):
|
||||
self._init()
|
||||
|
||||
Stock.set_fields()
|
||||
```
|
||||
|
||||
Instead of inspecting the `__init__()` method, write a class method
|
||||
`create_init(cls)` that creates an `__init__()` method from the value of
|
||||
`_fields`. Use the `exec()` function to do this as shown above.
|
||||
Here's how a user will use it:
|
||||
|
||||
```python
|
||||
class Stock(Structure):
|
||||
_fields = ('name', 'shares', 'price')
|
||||
|
||||
Stock.create_init()
|
||||
```
|
||||
|
||||
The resulting class should work exactly the name way as before:
|
||||
|
||||
```python
|
||||
>>> s = Stock(name='GOOG', shares=100, price=490.1)
|
||||
>>> s
|
||||
Stock('GOOG',100,490.1)
|
||||
>>> s.shares = 50
|
||||
>>> s.share = 50
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "structure.py", line 12, in __setattr__
|
||||
raise AttributeError('No attribute %s' % name)
|
||||
AttributeError: No attribute share
|
||||
>>>
|
||||
```
|
||||
|
||||
Modify the `Stock` class in progress to use the `create_init()` function as shown.
|
||||
Verify with your unit tests as before.
|
||||
|
||||
While you're at it, get rid of the `_init()` and `set_fields()`
|
||||
methods on the `Structure` class--that approach was kind of weird.
|
||||
|
||||
## (c) Named Tuples
|
||||
|
||||
In link:ex2_1.html[Exercise 2.1], you experimented with `namedtuple` objects
|
||||
in the `collections` module. Just to refresh your memory, here is how
|
||||
they worked:
|
||||
|
||||
```python
|
||||
>>> from collections import namedtuple
|
||||
>>> Stock = namedtuple('Stock', ['name', 'shares', 'price'])
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.shares
|
||||
100
|
||||
>>> s[1]
|
||||
100
|
||||
>>>
|
||||
```
|
||||
|
||||
Under the covers, the `namedtuple()` function is creating code as a string
|
||||
and executing it using `exec()`. Look at the code and marvel:
|
||||
|
||||
```python
|
||||
>>> import inspect
|
||||
>>> print(inspect.getsource(namedtuple))
|
||||
... look at the output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
\[ [Solution](soln6_4.md) | [Index](index.md) | [Exercise 6.3](ex6_3.md) | [Exercise 6.5](ex6_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
147
Exercises/ex6_5.md
Normal file
147
Exercises/ex6_5.md
Normal file
@ -0,0 +1,147 @@
|
||||
\[ [Index](index.md) | [Exercise 6.4](ex6_4.md) | [Exercise 7.1](ex7_1.md) \]
|
||||
|
||||
# Exercise 6.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to define a proper callable object
|
||||
|
||||
Files Modified : `validate.py`
|
||||
|
||||
Back in [Exercise 4.3](ex4_3.md), you created a series of `Validator` classes
|
||||
for performing different kinds of type and value checks. For example:
|
||||
|
||||
```python
|
||||
>>> from validate import Integer
|
||||
>>> Integer.check(1)
|
||||
>>> Integer.check('hello')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "validate.py", line 21, in check
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
TypeError: Expected <class 'int'>
|
||||
>>>
|
||||
```
|
||||
|
||||
You could use the validators in functions like this:
|
||||
|
||||
```python
|
||||
>>> def add(x, y):
|
||||
Integer.check(x)
|
||||
Integer.check(y)
|
||||
return x + y
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
In this exercise, we're going to take it just one step further.
|
||||
|
||||
## (a) Creating a Callable Object
|
||||
|
||||
In the file `validate.py`, start by creating a class like this:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
...
|
||||
|
||||
class ValidatedFunction:
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
print('Calling', self.func)
|
||||
result = self.func(*args, **kwargs)
|
||||
return result
|
||||
```
|
||||
|
||||
Test the class by applying it to a function:
|
||||
|
||||
```python
|
||||
>>> def add(x, y):
|
||||
return x + y
|
||||
|
||||
>>> add = ValidatedFunction(add)
|
||||
>>> add(2, 3)
|
||||
Calling <function add at 0x1014df598>
|
||||
5
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Enforcement
|
||||
|
||||
Modify the `ValidatedFunction` class so that it enforces value checks
|
||||
attached via function annotations. For example:
|
||||
|
||||
```python
|
||||
>>> def add(x: Integer, y:Integer):
|
||||
return x + y
|
||||
>>> add = ValidatedFunction(add)
|
||||
>>> add(2,3)
|
||||
5
|
||||
>>> add('two','three')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "validate.py", line 67, in __call__
|
||||
self.func.__annotations__[name].check(val)
|
||||
File "validate.py", line 21, in check
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
TypeError: expected <class 'int'>
|
||||
>>>>
|
||||
```
|
||||
|
||||
Hint: To do this, play around with signature binding. Use the `bind()`
|
||||
method of `Signature` objects to bind function arguments to argument
|
||||
names. Then cross reference this information with the
|
||||
`__annotations__` attribute to get the different validator classes.
|
||||
|
||||
Keep in mind, you're making an object that looks like a function, but
|
||||
it's really not. There is magic going on behind the scenes.
|
||||
|
||||
## (c) Use as a Method (Challenge)
|
||||
|
||||
A custom callable often presents problems if used as a custom method.
|
||||
For example, try this:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares:Integer):
|
||||
self.shares -= nshares
|
||||
sell = ValidatedFunction(sell) # Fails
|
||||
```
|
||||
|
||||
You'll find that the wrapped `sell()` fails miserably:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.sell(10)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "validate.py", line 64, in __call__
|
||||
bound = self.signature.bind(*args, **kwargs)
|
||||
File "/usr/local/lib/python3.6/inspect.py", line 2933, in bind
|
||||
return args[0]._bind(args[1:], kwargs)
|
||||
File "/usr/local/lib/python3.6/inspect.py", line 2848, in _bind
|
||||
raise TypeError(msg) from None
|
||||
TypeError: missing a required argument: 'nshares'
|
||||
>>>
|
||||
```
|
||||
|
||||
Bonus: Figure out why it fails--but don't spend too much time fooling around with it.
|
||||
|
||||
\[ [Solution](soln6_5.md) | [Index](index.md) | [Exercise 6.4](ex6_4.md) | [Exercise 7.1](ex7_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
137
Exercises/ex7_1.md
Normal file
137
Exercises/ex7_1.md
Normal file
@ -0,0 +1,137 @@
|
||||
\[ [Index](index.md) | [Exercise 6.5](ex6_5.md) | [Exercise 7.2](ex7_2.md) \]
|
||||
|
||||
# Exercise 7.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to define a simple decorator functions.
|
||||
|
||||
*Files Created:* `logcall.py`
|
||||
|
||||
*Files Modifie:* `validate.py`
|
||||
|
||||
## (a) Your First Decorator
|
||||
|
||||
To start with decorators, write a _very_ simple decorator
|
||||
function that simply prints out a message each time a function is
|
||||
called. Create a file `logcall.py` and define the following
|
||||
function:
|
||||
|
||||
```python
|
||||
# logcall.py
|
||||
|
||||
def logged(func):
|
||||
print('Adding logging to', func.__name__)
|
||||
def wrapper(*args, **kwargs):
|
||||
print('Calling', func.__name__)
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
```
|
||||
|
||||
Now, make a separate file `sample.py` and apply it to a
|
||||
few function definitions:
|
||||
|
||||
```python
|
||||
# sample.py
|
||||
|
||||
from logcall import logged
|
||||
|
||||
@logged
|
||||
def add(x,y):
|
||||
return x+y
|
||||
|
||||
@logged
|
||||
def sub(x,y):
|
||||
return x-y
|
||||
```
|
||||
|
||||
Test your code as follows:
|
||||
|
||||
```python
|
||||
>>> import sample
|
||||
Adding logging to add
|
||||
Adding logging to sub
|
||||
>>> sample.add(3,4)
|
||||
Calling add
|
||||
7
|
||||
>>> sample.sub(2,3)
|
||||
Calling sub
|
||||
-1
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) A Real Decorator
|
||||
|
||||
In [Exercise 6.6](ex6_6.md), you created a callable class `ValidatedFunction` that
|
||||
enforced type annotations. Rewrite this class as a decorator function called `validated`.
|
||||
It should allow you to write code like this:
|
||||
|
||||
```python
|
||||
from validate import Integer, validated
|
||||
|
||||
@validated
|
||||
def add(x: Integer, y:Integer) -> Integer:
|
||||
return x + y
|
||||
|
||||
@validated
|
||||
def pow(x: Integer, y:Integer) -> Integer:
|
||||
return x ** y
|
||||
```
|
||||
|
||||
Here's how the decorated functions should work:
|
||||
|
||||
```python
|
||||
>>> add(2, 3)
|
||||
5
|
||||
>>> add('2', '3')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "validate.py", line 75, in wrapper
|
||||
raise TypeError('Bad Arguments\n' + '\n'.join(errors))
|
||||
TypeError: Bad Arguments
|
||||
x: Expected <class 'int'>
|
||||
y: Expected <class 'int'>
|
||||
|
||||
>>> pow(2, 3)
|
||||
8
|
||||
>>> pow(2, -1)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "validate.py", line 83, in wrapper
|
||||
raise TypeError(f'Bad return: {e}') from None
|
||||
TypeError: Bad return: Expected <class 'int'>
|
||||
>>>
|
||||
```
|
||||
|
||||
Your decorator should try to patch up the exceptions so that they
|
||||
show more useful information as shown. Also, the `@validated`
|
||||
decorator should work in classes (you don't need to do anything special).
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
@validated
|
||||
def sell(self, nshares:PositiveInteger):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
Note: This part doesn't involve a lot of code, but there are a lot of low-level
|
||||
fiddly bits. The solution will look almost the same as for Exercise 6.6. Don't
|
||||
be shy about looking at solution code though.
|
||||
|
||||
\[ [Solution](soln7_1.md) | [Index](index.md) | [Exercise 6.5](ex6_5.md) | [Exercise 7.2](ex7_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
178
Exercises/ex7_2.md
Normal file
178
Exercises/ex7_2.md
Normal file
@ -0,0 +1,178 @@
|
||||
\[ [Index](index.md) | [Exercise 7.1](ex7_1.md) | [Exercise 7.3](ex7_3.md) \]
|
||||
|
||||
# Exercise 7.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Decorator chaining
|
||||
- Defining decorators that accept arguments.
|
||||
|
||||
*Files Modified:* `logcall.py`, `validate.py`
|
||||
|
||||
## (a) Copying Metadata
|
||||
|
||||
When a function gets wrapped by a decorator, you often lose
|
||||
information about the name of the function, documentation strings, and
|
||||
other details. Verify this:
|
||||
|
||||
```python
|
||||
>>> @logged
|
||||
def add(x,y):
|
||||
'Adds two things'
|
||||
return x+y
|
||||
|
||||
>>> add
|
||||
<function wrapper at 0x4439b0>
|
||||
>>> help(add)
|
||||
... look at the output ...
|
||||
>>>
|
||||
```
|
||||
|
||||
Fix the definition of the `logged` decorator so that it copies
|
||||
function metadata properly. To do this, use the `@wraps(func)`
|
||||
decorator as shown in the notes.
|
||||
|
||||
After you're done, make sure the decorator preserves the function name
|
||||
and doc string.
|
||||
|
||||
```python
|
||||
>>> @logged
|
||||
def add(x,y):
|
||||
'Adds two things'
|
||||
return x+y
|
||||
|
||||
>>> add
|
||||
<function add at 0x4439b0>
|
||||
>>> add.__doc__
|
||||
'Adds two things'
|
||||
>>>
|
||||
```
|
||||
|
||||
Fix the `@validated` decorator you wrote earlier so that it also preserves
|
||||
metadata using `@wraps(func)`.
|
||||
|
||||
## (b) Your first decorator with arguments
|
||||
|
||||
The `@logged` decorator you defined earlier always just
|
||||
prints a simple message with the function name.
|
||||
Suppose that you wanted the user to be able to specify a
|
||||
custom message of some sort.
|
||||
|
||||
Define a new decorator `@logformat(fmt)` that accepts
|
||||
a format string as an argument and uses `fmt.format(func=func)` to
|
||||
format a supplied function into a log message:
|
||||
|
||||
```python
|
||||
# sample.py
|
||||
...
|
||||
from logcall import logformat
|
||||
|
||||
@logformat('{func.__code__.co_filename}:{func.__name__}')
|
||||
def mul(x,y):
|
||||
return x*y
|
||||
```
|
||||
|
||||
To do this, you need to define a decorator that takes an argument.
|
||||
This is what it should look like when you test it:
|
||||
|
||||
```python
|
||||
>>> import sample
|
||||
Adding logging to add
|
||||
Adding logging to sub
|
||||
Adding logging to mul
|
||||
>>> sample.add(2,3)
|
||||
Calling add
|
||||
5
|
||||
>>> sample.mul(2,3)
|
||||
sample.py:mul
|
||||
6
|
||||
>>>
|
||||
```
|
||||
|
||||
To further simplify the code, show how you can define the original `@logged` decorator
|
||||
using the the `@logformat` decorator.
|
||||
|
||||
## (c) Multiple decorators and methods
|
||||
|
||||
Things can get a bit dicey when decorators are applied to methods in a
|
||||
class. Try applying your `@logged` decorator to the methods in the
|
||||
following class.
|
||||
|
||||
```python
|
||||
class Spam:
|
||||
@logged
|
||||
def instance_method(self):
|
||||
pass
|
||||
|
||||
@logged
|
||||
@classmethod
|
||||
def class_method(cls):
|
||||
pass
|
||||
|
||||
@logged
|
||||
@staticmethod
|
||||
def static_method():
|
||||
pass
|
||||
|
||||
@logged
|
||||
@property
|
||||
def property_method(self):
|
||||
pass
|
||||
```
|
||||
|
||||
Does it even work at all? (hint: no). Is there any way to fix the code so
|
||||
that it works? For example, can you make it so the following example
|
||||
works?
|
||||
|
||||
```python
|
||||
>>> s = Spam()
|
||||
>>> s.instance_method()
|
||||
instance_method
|
||||
>>> Spam.class_method()
|
||||
class_method
|
||||
>>> Spam.static_method()
|
||||
static_method
|
||||
>>> s.property_method
|
||||
property_method
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Validation (Redux)
|
||||
|
||||
In the last exercise, you wrote a `@validated` decorator that enforced
|
||||
type annotations. For example:
|
||||
|
||||
```python
|
||||
@validated
|
||||
def add(x: Integer, y:Integer) -> Integer:
|
||||
return x + y
|
||||
```
|
||||
|
||||
Make a new decorator `@enforce()` that enforces types specified
|
||||
via keyword arguments to the decorator instead. For example:
|
||||
|
||||
```python
|
||||
@enforce(x=Integer, y=Integer, return_=Integer)
|
||||
def add(x, y):
|
||||
return x + y
|
||||
```
|
||||
|
||||
The resulting behavior of the decorated function should be identical.
|
||||
Note: Make the `return_` keyword specify the return type. `return` is
|
||||
a Python reserved word so you have to pick a slightly different name.
|
||||
|
||||
|
||||
**Discussion**
|
||||
|
||||
Writing robust decorators is often a lot harder than it looks.
|
||||
Recommended reading:
|
||||
|
||||
|
||||
\[ [Solution](soln7_2.md) | [Index](index.md) | [Exercise 7.1](ex7_1.md) | [Exercise 7.3](ex7_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
261
Exercises/ex7_3.md
Normal file
261
Exercises/ex7_3.md
Normal file
@ -0,0 +1,261 @@
|
||||
\[ [Index](index.md) | [Exercise 7.2](ex7_2.md) | [Exercise 7.4](ex7_4.md) \]
|
||||
|
||||
# Exercise 7.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about class decorators
|
||||
- Descriptors revisited
|
||||
|
||||
*Files Modified:* `validate.py`, `structure.py`
|
||||
|
||||
This exercise is going to pull together a bunch of topics we've
|
||||
developed over the last few days. Hang on to your hat.
|
||||
|
||||
## (a) Descriptors Revisited
|
||||
|
||||
In link:ex4_3.html[Exercise 4.3] you defined some descriptors that
|
||||
allowed a user to define classes with type-checked attributes like
|
||||
this:
|
||||
|
||||
```python
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
|
||||
class Stock:
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
...
|
||||
```
|
||||
|
||||
Modify your `Stock` class so that it includes the above descriptors
|
||||
and now looks like this (see link:ex6_4.html[Exercise 6.4]):
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
|
||||
class Stock(Structure):
|
||||
_fields = ('name', 'shares', 'price')
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
|
||||
Stock.create_init()
|
||||
```
|
||||
|
||||
Run the unit tests in `teststock.py`. You should see a significant
|
||||
number of tests passing with the addition of type checking.
|
||||
Excellent.
|
||||
|
||||
## (b) Using Class Decorators to Fill in Details
|
||||
|
||||
An annoying aspect of the above code is there are extra details such as
|
||||
`_fields` variable and the final step of `Stock.create_init()`. A lot
|
||||
of this could be packaged into a class decorator instead.
|
||||
|
||||
In the file `structure.py`, make a class decorator `@validate_attributes`
|
||||
that examines the class body for instances of Validators and fills in
|
||||
the `_fields` variable. For example:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
|
||||
from validate import Validator
|
||||
|
||||
def validate_attributes(cls):
|
||||
validators = []
|
||||
for name, val in vars(cls).items():
|
||||
if isinstance(val, Validator):
|
||||
validators.append(val)
|
||||
cls._fields = [val.name for val in validators]
|
||||
return cls
|
||||
```
|
||||
|
||||
This code relies on the fact that class dictionaries are ordered
|
||||
starting in Python 3.6. Thus, it will encounter the different
|
||||
`Validator` descriptors in the order that they're listed. Using this
|
||||
order, you can then fill in the `_fields` variable. This allows
|
||||
you to write code like this:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure, validate_attributes
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
|
||||
@validate_attributes
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
|
||||
Stock.create_init()
|
||||
```
|
||||
|
||||
Once you've got this working, modify the `@validate_attributes`
|
||||
decorator to additionally perform the final step of calling
|
||||
`Stock.create_init()`. This will reduce the class to the
|
||||
following:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure, validate_attributes
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
|
||||
@validate_attributes
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
## (c) Applying Decorators via Inheritance
|
||||
|
||||
Having to specify the class decorator itself is kind of annoying. Modify the
|
||||
`Structure` class with the following `__init_subclass__()` method:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
|
||||
class Structure:
|
||||
...
|
||||
@classmethod
|
||||
def __init_subclass__(cls):
|
||||
validate_attributes(cls)
|
||||
```
|
||||
|
||||
Once you've made this change, you should be able to drop the decorator entirely and
|
||||
solely rely on inheritance. It's inheritance plus some hidden magic!
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
Now, the code is really starting to go places. In fact, it almost
|
||||
looks normal. Let's keep pushing it.
|
||||
|
||||
## (d) Row Conversion
|
||||
|
||||
One missing feature from the `Structure` class is a `from_row()` method that
|
||||
allows it to work with earlier CSV reading code. Let's fix that. Give the
|
||||
`Structure` class a `_types` class variable and the following class method:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
|
||||
class Structure:
|
||||
_types = ()
|
||||
...
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
rowdata = [ func(val) for func, val in zip(cls._types, row) ]
|
||||
return cls(*rowdata)
|
||||
...
|
||||
```
|
||||
|
||||
Modify the `@validate_attributes` decorator so that it examines the
|
||||
various validators for an `expected_type` attribute and uses it to
|
||||
fill in the `_types` variable above.
|
||||
|
||||
Once you've done this, you should be able to do things like this:
|
||||
|
||||
```python
|
||||
>>> s = Stock.from_row(['GOOG', '100', '490.1'])
|
||||
>>> s
|
||||
Stock('GOOG', 100, 490.1)
|
||||
>>> import reader
|
||||
>>> port = reader.read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
>>>
|
||||
```
|
||||
|
||||
## (e) Method Argument Checking
|
||||
|
||||
Remember that `@validated` decorator you wrote in the last part?
|
||||
Let's modify the `@validate_attributes` decorator so that any method
|
||||
in the class with annotations gets wrapped by `@validated`
|
||||
automatically. This allows you to put enforced annotations on methods
|
||||
such as the `sell()` method:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares: PositiveInteger):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
You'll find that `sell()` now enforces the argument.
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.sell(25)
|
||||
>>> s.sell(-25)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Bad Arguments
|
||||
nshares: must be >= 0
|
||||
>>>
|
||||
```
|
||||
|
||||
Yes, this starting to get very interesting now. The combination of a class decorator and
|
||||
inheritance is a powerful force.
|
||||
|
||||
\[ [Solution](soln7_3.md) | [Index](index.md) | [Exercise 7.2](ex7_2.md) | [Exercise 7.4](ex7_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
177
Exercises/ex7_4.md
Normal file
177
Exercises/ex7_4.md
Normal file
@ -0,0 +1,177 @@
|
||||
\[ [Index](index.md) | [Exercise 7.3](ex7_3.md) | [Exercise 7.5](ex7_5.md) \]
|
||||
|
||||
# Exercise 7.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about the low-level steps involved in creating a class
|
||||
|
||||
*Files Modified:* `validate.py`, `structure.py`
|
||||
|
||||
In this exercise, we look at the mechanics of how classes are actually
|
||||
created.
|
||||
|
||||
## (a) Class creation
|
||||
|
||||
Recall, from earlier exercises, we defined a simple class
|
||||
`Stock` that looked like this:
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
def __init__(self,name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
def cost(self):
|
||||
return self.shares*self.price
|
||||
def sell(self,nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
What we're going to do here is create the class manually. Start out
|
||||
by just defining the methods as normal Python functions.
|
||||
|
||||
```python
|
||||
>>> def __init__(self,name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
>>> def cost(self):
|
||||
return self.shares*self.price
|
||||
|
||||
>>> def sell(self,nshares):
|
||||
self.shares -= nshares
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Next, make a methods dictionary:
|
||||
|
||||
```python
|
||||
>>> methods = {
|
||||
'__init__' : __init__,
|
||||
'cost' : cost,
|
||||
'sell' : sell }
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
Finally, create the `Stock` class object:
|
||||
|
||||
```python
|
||||
>>> Stock = type('Stock',(object,),methods)
|
||||
>>> s = Stock('GOOG',100,490.10)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s.cost()
|
||||
49010.0
|
||||
>>> s.sell(25)
|
||||
>>> s.shares
|
||||
75
|
||||
>>>
|
||||
```
|
||||
|
||||
Congratulations, you just created a class. A class is really nothing
|
||||
more than a name, a tuple of base classes, and a dictionary holding
|
||||
all of the class contents. `type()` is a constructor that
|
||||
creates a class for you if you supply these three parts.
|
||||
|
||||
## (b) Typed structures
|
||||
|
||||
In the `structure.py` file, define the following function:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
|
||||
...
|
||||
def typed_structure(clsname, **validators):
|
||||
cls = type(clsname, (Structure,), validators)
|
||||
return cls
|
||||
```
|
||||
|
||||
This function is somewhat similar to the `namedtuple()` function in that it creates a class. Try it out:
|
||||
|
||||
```python
|
||||
>>> from validate import String, PositiveInteger, PositiveFloat
|
||||
>>> from structure import typed_structure
|
||||
>>> Stock = typed_structure('Stock', name=String(), shares=PositiveInteger(), price=PositiveFloat())
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.name
|
||||
'GOOG'
|
||||
>>> s
|
||||
Stock('GOOG', 100, 490.1)
|
||||
>>>
|
||||
```
|
||||
|
||||
You might find the seams of your head starting to pull apart about now.
|
||||
|
||||
## (c) Making a lot of classes
|
||||
|
||||
|
||||
There are other situations where direct usage of the `type()` constructor might be advantageous.
|
||||
Consider this bit of code:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
...
|
||||
|
||||
class Typed(Validator):
|
||||
expected_type = object
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if not isinstance(value, cls.expected_type):
|
||||
raise TypeError(f'expected {cls.expected_type}')
|
||||
super().check(value)
|
||||
|
||||
class Integer(Typed):
|
||||
expected_type = int
|
||||
|
||||
class Float(Typed):
|
||||
expected_type = float
|
||||
|
||||
class String(Typed):
|
||||
expected_type = str
|
||||
...
|
||||
```
|
||||
|
||||
Wow is the last part of that annoying and repetitive. Change it
|
||||
to use a table of desired type classes like this:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
...
|
||||
|
||||
_typed_classes = [
|
||||
('Integer', int),
|
||||
('Float', float),
|
||||
('String', str) ]
|
||||
|
||||
globals().update((name, type(name, (Typed,), {'expected_type':ty}))
|
||||
for name, ty in _typed_classes)
|
||||
```
|
||||
|
||||
Now, if you want to have more type classes, you just add them to the
|
||||
table:
|
||||
|
||||
```python
|
||||
_typed_classes = [
|
||||
('Integer', int),
|
||||
('Float', float),
|
||||
('Complex', complex),
|
||||
('Decimal', decimal.Decimal),
|
||||
('List', list),
|
||||
('Bool', bool),
|
||||
('String', str) ]
|
||||
```
|
||||
|
||||
Admit it, that's kind of cool and saves a lot of typing (at the keyboard).
|
||||
|
||||
\[ [Solution](soln7_4.md) | [Index](index.md) | [Exercise 7.3](ex7_3.md) | [Exercise 7.5](ex7_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
81
Exercises/ex7_5.md
Normal file
81
Exercises/ex7_5.md
Normal file
@ -0,0 +1,81 @@
|
||||
\[ [Index](index.md) | [Exercise 7.4](ex7_4.md) | [Exercise 7.6](ex7_6.md) \]
|
||||
|
||||
# Exercise 7.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Create your first metaclass
|
||||
|
||||
*Files Created:* `mymeta.py`
|
||||
|
||||
## (a) Create your first metaclass
|
||||
|
||||
Create a file called `mymeta.py`
|
||||
and put the following code in it (from the slides):
|
||||
|
||||
```python
|
||||
# mymeta.py
|
||||
|
||||
class mytype(type):
|
||||
@staticmethod
|
||||
def __new__(meta, name, bases, __dict__):
|
||||
print("Creating class :", name)
|
||||
print("Base classes :", bases)
|
||||
print("Attributes :", list(__dict__))
|
||||
return super().__new__(meta, name, bases, __dict__)
|
||||
|
||||
class myobject(metaclass=mytype):
|
||||
pass
|
||||
```
|
||||
|
||||
Once you've done this, define a class that inherits from
|
||||
`myobject` instead of object. For example:
|
||||
|
||||
```python
|
||||
class Stock(myobject):
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
Try running your code and creating instances of `Stock`. See
|
||||
what happens. You should see the print statements from your
|
||||
`mytype` running once when the `Stock` class is
|
||||
defined.
|
||||
|
||||
What happens if you inherit from `Stock`?
|
||||
|
||||
```python
|
||||
class MyStock(Stock):
|
||||
pass
|
||||
```
|
||||
|
||||
You should still see your metaclass at work. Metaclasses are "sticky" in that they
|
||||
get applied across an entire inheritance hierarchy.
|
||||
|
||||
**Discussion**
|
||||
|
||||
Why would you want to do something like this?
|
||||
The main power of a metaclass is that it gives a programmer the ability
|
||||
to capture details about classes just prior to their creation. For
|
||||
example, in the `__new__()` method, you are given all of the
|
||||
basic details including the name of the class, base classes, and
|
||||
methods data. If you inspect this data, you can perform various
|
||||
types of diagnostic checks. If you're more daring, you can modify the
|
||||
data and change what gets placed in the class definition when it is
|
||||
created. Needless to say, there are many opportunities for horrible
|
||||
diabolical evil.
|
||||
|
||||
\[ [Solution](soln7_5.md) | [Index](index.md) | [Exercise 7.4](ex7_4.md) | [Exercise 7.6](ex7_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
165
Exercises/ex7_6.md
Normal file
165
Exercises/ex7_6.md
Normal file
@ -0,0 +1,165 @@
|
||||
\[ [Index](index.md) | [Exercise 7.5](ex7_5.md) | [Exercise 8.1](ex8_1.md) \]
|
||||
|
||||
# Exercise 7.6
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Metaclasses in action
|
||||
- Explode your brain
|
||||
|
||||
*Files Modified:* `structure.py`, `validate.py`
|
||||
|
||||
## (a) The Final Frontier
|
||||
|
||||
In link:ex7_3.html[Exercise 7.3], we made it possible to define type-checked structures as follows:
|
||||
|
||||
```python
|
||||
from validate import String, PositiveInteger, PositiveFloat
|
||||
from structure import Structure
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares: PositiveInteger):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
There are a lot of things going on under the covers. However, one annoyance
|
||||
concerns all of those type-name imports at the top (e.g., `String`, `PositiveInteger`, etc.).
|
||||
That's just the kind of thing that might lead to a `from validate import *` statement.
|
||||
One interesting thing about a metaclass is that it can be used to control
|
||||
the process by which a class gets defined. This includes managing the
|
||||
environment of a class definition itself. Let's tackle those imports.
|
||||
|
||||
The first step in managing all of the validator names is to collect
|
||||
them. Go to the file `validate.py` and modify the `Validator` base
|
||||
class with this extra bit of code involving `__init_subclass__()` again:
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
|
||||
class Validator:
|
||||
...
|
||||
|
||||
# Collect all derived classes into a dict
|
||||
validators = { }
|
||||
@classmethod
|
||||
def __init_subclass__(cls):
|
||||
cls.validators[cls.__name__] = cls
|
||||
```
|
||||
|
||||
That's not much, but it's creating a little namespace of all of the `Validator`
|
||||
subclasses. Take a look at it:
|
||||
|
||||
```python
|
||||
>>> from validate import Validator
|
||||
>>> Validator.validators
|
||||
{'Float': <class 'validate.Float'>,
|
||||
'Integer': <class 'validate.Integer'>,
|
||||
'NonEmpty': <class 'validate.NonEmpty'>,
|
||||
'NonEmptyString': <class 'validate.NonEmptyString'>,
|
||||
'Positive': <class 'validate.Positive'>,
|
||||
'PositiveFloat': <class 'validate.PositiveFloat'>,
|
||||
'PositiveInteger': <class 'validate.PositiveInteger'>,
|
||||
'String': <class 'validate.String'>,
|
||||
'Typed': <class 'validate.Typed'>}
|
||||
>>>
|
||||
```
|
||||
|
||||
Now that you've done that, let's inject this namespace into namespace
|
||||
of classes defined from `Structure`. Define the following metaclass:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
...
|
||||
|
||||
from validate import Validator
|
||||
from collections import ChainMap
|
||||
|
||||
class StructureMeta(type):
|
||||
@classmethod
|
||||
def __prepare__(meta, clsname, bases):
|
||||
return ChainMap({}, Validator.validators)
|
||||
|
||||
@staticmethod
|
||||
def __new__(meta, name, bases, methods):
|
||||
methods = methods.maps[0]
|
||||
return super().__new__(meta, name, bases, methods)
|
||||
|
||||
class Structure(metaclass=StructureMeta):
|
||||
...
|
||||
```
|
||||
|
||||
In this code, the `__prepare__()` method is making a special `ChainMap` mapping that consists
|
||||
of an empty dictionary and a dictionary of all of the defined validators. The empty dictionary
|
||||
that's listed first is going to collect all of the definitions made inside the class body.
|
||||
The `Validator.validators` dictionary is going to make all of the type definitions available
|
||||
to for use as descriptors or argument type annotations.
|
||||
|
||||
The `__new__()` method discards extra the validator dictionary and
|
||||
passes the remaining definitions onto the type constructor. It's
|
||||
ingenious, but it lets you drop the annoying imports:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structure import Structure
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares: PositiveInteger):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
## (b) Stare in Amazement
|
||||
|
||||
Try running your `teststock.py` unit tests across this new file. Most of them should be
|
||||
passing now. For kicks, try your `Stock` class with some of the earlier code
|
||||
for tableformatting and reading data. It should all work.
|
||||
|
||||
```python
|
||||
>>> from stock import Stock
|
||||
>>> from reader import read_csv_as_instances
|
||||
>>> portfolio = read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
>>> portfolio
|
||||
[Stock('AA',100,32.2), Stock('IBM',50,91.1), Stock('CAT',150,83.44), Stock('MSFT',200,51.23), Stock('GE',95,40.37), Stock('MSFT',50,65.1), Stock('IBM',100,70.44)]
|
||||
>>> from tableformat import create_formatter, print_table
|
||||
>>> formatter = create_formatter('text')
|
||||
>>> print_table(portfolio, ['name','shares','price'], formatter)
|
||||
name shares price
|
||||
---------- ---------- ----------
|
||||
AA 100 32.2
|
||||
IBM 50 91.1
|
||||
CAT 150 83.44
|
||||
MSFT 200 51.23
|
||||
GE 95 40.37
|
||||
MSFT 50 65.1
|
||||
IBM 100 70.44
|
||||
>>>
|
||||
```
|
||||
|
||||
Again, marvel at the final `stock.py` file and observe how clean the
|
||||
code looks. Just try not think about everything that is happening
|
||||
under the hood with the `Structure` base class.
|
||||
|
||||
\[ [Solution](soln7_6.md) | [Index](index.md) | [Exercise 7.5](ex7_5.md) | [Exercise 8.1](ex8_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
258
Exercises/ex8_1.md
Normal file
258
Exercises/ex8_1.md
Normal file
@ -0,0 +1,258 @@
|
||||
\[ [Index](index.md) | [Exercise 7.6](ex7_6.md) | [Exercise 8.2](ex8_2.md) \]
|
||||
|
||||
# Exercise 8.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to customize iteration using generators
|
||||
|
||||
*Files Modified:* `structure.py`
|
||||
|
||||
*Files Created:* `follow.py`
|
||||
|
||||
## (a) A Simple Generator
|
||||
|
||||
If you ever find yourself wanting to customize iteration, you should
|
||||
always think generator functions. They're easy to write---simply make
|
||||
a function that carries out the desired iteration logic and uses `yield`
|
||||
to emit values.
|
||||
|
||||
For example, try this generator that allows you to iterate over a
|
||||
range of numbers with fractional steps (something not supported by
|
||||
the `range()` builtin):
|
||||
|
||||
```python
|
||||
>>> def frange(start,stop,step):
|
||||
while start < stop:
|
||||
yield start
|
||||
start += step
|
||||
|
||||
>>> for x in frange(0, 2, 0.25):
|
||||
print(x, end=' ')
|
||||
|
||||
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75
|
||||
>>>
|
||||
```
|
||||
|
||||
Iterating on a generator is a one-time operation. For example, here's
|
||||
what happen if you try to iterate twice:
|
||||
|
||||
```python
|
||||
>>> f = frange(0, 2, 0.25)
|
||||
>>> for x in f:
|
||||
print(x, end=' ')
|
||||
|
||||
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75
|
||||
>>> for x in f:
|
||||
print(x, end=' ')
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
If you want to iterate over the same sequence, you need to recreate the generator
|
||||
by calling `frange()` again. Alternative, you could package everything into a class:
|
||||
|
||||
```python
|
||||
>>> class FRange:
|
||||
def __init__(self, start, stop, step):
|
||||
self.start = start
|
||||
self.stop = stop
|
||||
self.step = step
|
||||
def __iter__(self):
|
||||
n = self.start
|
||||
while n < self.stop:
|
||||
yield n
|
||||
n += self.step
|
||||
|
||||
>>> f = FRange(0, 2, 0.25)
|
||||
>>> for x in f:
|
||||
print(x, end=' ')
|
||||
|
||||
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75
|
||||
>>> for x in f:
|
||||
print(x, end=' ')
|
||||
|
||||
0 0.25 0.5 0.75 1.0 1.25 1.5 1.75
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Adding Iteration to Objects
|
||||
|
||||
If you've created a custom class, you can make it support iteration by
|
||||
defining an `__iter__()` special method. `__iter__()` returns an
|
||||
iterator as a result. As shown in the previous example, an easy way
|
||||
to do it is to define `__iter__()` as a generator.
|
||||
|
||||
In earlier exercises, you defined a `Structure` base class.
|
||||
Add an `__iter__()` method to this class that produces the attribute values
|
||||
in order. For example:
|
||||
|
||||
```python
|
||||
class Structure(metaclass=StructureMeta):
|
||||
...
|
||||
def __iter__(self):
|
||||
for name in self._fields:
|
||||
yield getattr(self, name)
|
||||
...
|
||||
```
|
||||
|
||||
Once you've done this, you should be able to iterate over the instance
|
||||
attributes like this:
|
||||
|
||||
```python
|
||||
>>> from stock import Stock
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> for val in s:
|
||||
print(val)
|
||||
GOOG
|
||||
100
|
||||
490.1
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) The Surprising Power of Iteration
|
||||
|
||||
Python uses iteration in ways you might not expect. Once you've added `__iter__()`
|
||||
to the `Structure` class, you'll find that it is easy to do all sorts of new
|
||||
operations. For example, conversions to sequences and unpacking:
|
||||
|
||||
```python
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> list(s)
|
||||
['GOOG', 100, 490.1]
|
||||
>>> tuple(s)
|
||||
('GOOG', 100, 490.1)
|
||||
>>> name, shares, price = s
|
||||
>>> name
|
||||
'GOOG'
|
||||
>>> shares
|
||||
100
|
||||
>>> price
|
||||
490.1
|
||||
>>>
|
||||
```
|
||||
|
||||
While we're at it, we can now add a comparison operator to our `Structure`
|
||||
class:
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
class Structure(metaclass=StructureMeta):
|
||||
...
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, type(self)) and tuple(self) == tuple(other)
|
||||
...
|
||||
```
|
||||
|
||||
You should now be able to compare objects:
|
||||
|
||||
```python
|
||||
>>> a = Stock('GOOG', 100, 490.1)
|
||||
>>> b = Stock('GOOG', 100, 490.1)
|
||||
>>> a == b
|
||||
True
|
||||
>>>
|
||||
```
|
||||
|
||||
Try running your `teststock.py` unit tests again. Everything should be passing now.
|
||||
Excellent.
|
||||
|
||||
## (d) Monitoring a streaming data source
|
||||
|
||||
Generators can also be a useful way to simply produce a stream of
|
||||
data. In this part, we'll explore this idea by writing a generator to
|
||||
watch a log file. To start, follow the next instructions carefully.
|
||||
|
||||
The program `Data/stocksim.py` is a program that
|
||||
simulates stock market data. As output, the program constantly writes
|
||||
real-time data to a file `stocklog.csv`. In a
|
||||
command window (not IDLE) go into the `Data/` directory and run this program:
|
||||
|
||||
```
|
||||
% python3 stocksim.py
|
||||
```
|
||||
|
||||
If you are on Windows, just locate the `stocksim.py` program and
|
||||
double-click on it to run it. Now, forget about this program (just
|
||||
let it run). Again, just let this program run in the background---it
|
||||
will run for several hours (you shouldn't need to worry about it).
|
||||
|
||||
Once the above program is running, let's write a little program to
|
||||
open the file, seek to the end, and watch for new output. Create a
|
||||
file `follow.py` and put this code in it:
|
||||
|
||||
```python
|
||||
# follow.py
|
||||
import os
|
||||
import time
|
||||
f = open('Data/stocklog.csv')
|
||||
f.seek(0, os.SEEK_END) # Move file pointer 0 bytes from end of file
|
||||
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line == '':
|
||||
time.sleep(0.1) # Sleep briefly and retry
|
||||
continue
|
||||
fields = line.split(',')
|
||||
name = fields[0].strip('"')
|
||||
price = float(fields[1])
|
||||
change = float(fields[4])
|
||||
if change < 0:
|
||||
print('%10s %10.2f %10.2f' % (name, price, change))
|
||||
```
|
||||
|
||||
If you run the program, you'll see a real-time stock ticker. Under the covers,
|
||||
this code is kind of like the Unix `tail -f` command that's used to watch a log file.
|
||||
|
||||
**Note:** The use of the `readline()` method in this example is
|
||||
somewhat unusual in that it is not the usual way of reading lines from
|
||||
a file (normally you would just use a `for`-loop). However, in
|
||||
this case, we are using it to repeatedly probe the end of the file to
|
||||
see if more data has been added (`readline()` will either
|
||||
return new data or an empty string).
|
||||
|
||||
If you look at the code carefully, the first part of the code is
|
||||
producing lines of data whereas the statements at the end of the
|
||||
`while` loop are consuming the data. A major feature of generator
|
||||
functions is that you can move all of the data production code into a
|
||||
reusable function.
|
||||
|
||||
Modify the code so that the file-reading is performed by
|
||||
a generator function `follow(filename)`. Make it so the following code
|
||||
works:
|
||||
|
||||
```python
|
||||
>>> for line in follow('Data/stocklog.csv'):
|
||||
print(line, end='')
|
||||
|
||||
... Should see lines of output produced here ...
|
||||
```
|
||||
|
||||
Modify the stock ticker code so that it looks like this:
|
||||
|
||||
```python
|
||||
for line in follow('Data/stocklog.csv'):
|
||||
fields = line.split(',')
|
||||
name = fields[0].strip('"')
|
||||
price = float(fields[1])
|
||||
change = float(fields[4])
|
||||
if change < 0:
|
||||
print('%10s %10.2f %10.2f' % (name, price, change))
|
||||
```
|
||||
|
||||
**Discussion**
|
||||
|
||||
Something very powerful just happened here. You moved an interesting iteration pattern
|
||||
(reading lines at the end of a file) into its own little function. The `follow()` function
|
||||
is now this completely general purpose utility that you can use in any program. For
|
||||
example, you could use it to watch server logs, debugging logs, and other similar data sources.
|
||||
That's kind of cool.
|
||||
|
||||
\[ [Solution](soln8_1.md) | [Index](index.md) | [Exercise 7.6](ex7_6.md) | [Exercise 8.2](ex8_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
137
Exercises/ex8_2.md
Normal file
137
Exercises/ex8_2.md
Normal file
@ -0,0 +1,137 @@
|
||||
\[ [Index](index.md) | [Exercise 8.1](ex8_1.md) | [Exercise 8.3](ex8_3.md) \]
|
||||
|
||||
# Exercise 8.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Using generators to set up processing pipelines
|
||||
|
||||
*Files Created:* `ticker.py`
|
||||
|
||||
**Note**
|
||||
|
||||
For this exercise the `stocksim.py` program should still be
|
||||
running in the background. You're going to use the `follow()`
|
||||
function you wrote in the previous exercise.
|
||||
|
||||
## (a) Setting up a processing pipeline
|
||||
|
||||
A major power of generators is that they allow you to create programs
|
||||
that set up processing pipelines--much like pipes on Unix systems.
|
||||
Experiment with this concept by performing these steps:
|
||||
|
||||
```python
|
||||
>>> from follow import follow
|
||||
>>> import csv
|
||||
>>> lines = follow('Data/stocklog.csv')
|
||||
>>> rows = csv.reader(lines)
|
||||
>>> for row in rows:
|
||||
print(row)
|
||||
|
||||
['BA', '98.35', '6/11/2007', '09:41.07', '0.16', '98.25', '98.35', '98.31', '158148']
|
||||
['AA', '39.63', '6/11/2007', '09:41.07', '-0.03', '39.67', '39.63', '39.31', '270224']
|
||||
['XOM', '82.45', '6/11/2007', '09:41.07', '-0.23', '82.68', '82.64', '82.41', '748062']
|
||||
['PG', '62.95', '6/11/2007', '09:41.08', '-0.12', '62.80', '62.97', '62.61', '454327']
|
||||
...
|
||||
```
|
||||
|
||||
Well, that's interesting. What you're seeing here is that the output of the
|
||||
`follow()` function has been piped into the `csv.reader()` function and we're
|
||||
now getting a sequence of split rows.
|
||||
|
||||
## (b) Making more pipeline components
|
||||
|
||||
In a file `ticker.py`, define the following class (using your structure code from before) and set up
|
||||
a pipeline:
|
||||
|
||||
```python
|
||||
# ticker.py
|
||||
|
||||
from structure import Structure
|
||||
|
||||
class Ticker(Structure):
|
||||
name = String()
|
||||
price = Float()
|
||||
date = String()
|
||||
time = String()
|
||||
change = Float()
|
||||
open = Float()
|
||||
high = Float()
|
||||
low = Float()
|
||||
volume = Integer()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from follow import follow
|
||||
import csv
|
||||
lines = follow('Data/stocklog.csv')
|
||||
rows = csv.reader(lines)
|
||||
records = (Ticker.from_row(row) for row in rows)
|
||||
for record in records:
|
||||
print(record)
|
||||
```
|
||||
|
||||
When you run this, you should see some output like this:
|
||||
|
||||
Ticker('IBM',103.53,'6/11/2007','09:53.59',0.46,102.87,103.53,102.77,541633)
|
||||
Ticker('MSFT',30.21,'6/11/2007','09:54.01',0.16,30.05,30.21,29.95,7562516)
|
||||
Ticker('AA',40.01,'6/11/2007','09:54.01',0.35,39.67,40.15,39.31,576619)
|
||||
Ticker('T',40.1,'6/11/2007','09:54.08',-0.16,40.2,40.19,39.87,1312959)
|
||||
|
||||
## (c) Keep going
|
||||
|
||||
Oh, you can do better than that. Let's plug this into your table generation code. Change
|
||||
the program to the following:
|
||||
|
||||
```python
|
||||
# ticker.py
|
||||
...
|
||||
|
||||
if __name__ == '__main__':
|
||||
from follow import follow
|
||||
import csv
|
||||
from tableformat import create_formatter, print_table
|
||||
|
||||
formatter = create_formatter('text')
|
||||
|
||||
lines = follow('Data/stocklog.csv')
|
||||
rows = csv.reader(lines)
|
||||
records = (Ticker.from_row(row) for row in rows)
|
||||
negative = (rec for rec in records if rec.change < 0)
|
||||
print_table(negative, ['name','price','change'], formatter)
|
||||
```
|
||||
|
||||
This should produce some output that looks like this:
|
||||
|
||||
name price change
|
||||
---------- ---------- ----------
|
||||
C 53.12 -0.21
|
||||
UTX 70.04 -0.19
|
||||
AXP 62.86 -0.18
|
||||
MMM 85.72 -0.22
|
||||
MCD 51.38 -0.03
|
||||
WMT 49.85 -0.23
|
||||
KO 51.6 -0.07
|
||||
AIG 71.39 -0.14
|
||||
PG 63.05 -0.02
|
||||
HD 37.76 -0.19
|
||||
|
||||
Now, THAT is crazy! And pretty awesome.
|
||||
|
||||
**Discussion**
|
||||
|
||||
Some lessons learned: You can create various generator functions and
|
||||
chain them together to perform processing involving data-flow
|
||||
pipelines.
|
||||
|
||||
A good mental model for generator functions might be Lego blocks.
|
||||
You can make a collection of small iterator patterns and start
|
||||
stacking them together in various ways. It can be an extremely powerful way to program.
|
||||
|
||||
\[ [Solution](soln8_2.md) | [Index](index.md) | [Exercise 8.1](ex8_1.md) | [Exercise 8.3](ex8_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
141
Exercises/ex8_3.md
Normal file
141
Exercises/ex8_3.md
Normal file
@ -0,0 +1,141 @@
|
||||
\[ [Index](index.md) | [Exercise 8.2](ex8_2.md) | [Exercise 8.4](ex8_4.md) \]
|
||||
|
||||
# Exercise 8.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Using coroutines to set up processing pipelines
|
||||
|
||||
*Files Created:* `cofollow.py`, `coticker.py`
|
||||
|
||||
**Note**
|
||||
|
||||
For this exercise the `stocksim.py` program should still be
|
||||
running in the background.
|
||||
|
||||
In link:ex8_2.html[Exercise 8.2] you wrote some code that used
|
||||
generators to set up a processing pipeline. A key aspect of that
|
||||
program was the idea of data flowing between generator functions. A
|
||||
very similar kind of dataflow can be set up using coroutines. The
|
||||
only difference is that with a coroutine, you send data into different
|
||||
processing elements as opposed to pulling data out with a for-loop.
|
||||
|
||||
## (a) A coroutine example
|
||||
|
||||
Getting started with coroutines can be a little tricky. Here is an
|
||||
example program that performs the same task as
|
||||
link:ex8_2.html[Exercise 8.2], but with coroutines. Take this program
|
||||
and copy it into a file called `cofollow.py`.
|
||||
|
||||
```python
|
||||
# cofollow.py
|
||||
import os
|
||||
import time
|
||||
|
||||
# Data source
|
||||
def follow(filename,target):
|
||||
with open(filename,'r') as f:
|
||||
f.seek(0,os.SEEK_END)
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line != '':
|
||||
target.send(line)
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
|
||||
# Decorator for coroutine functions
|
||||
from functools import wraps
|
||||
|
||||
def consumer(func):
|
||||
@wraps(func)
|
||||
def start(*args,**kwargs):
|
||||
f = func(*args,**kwargs)
|
||||
f.send(None)
|
||||
return f
|
||||
return start
|
||||
|
||||
# Sample coroutine
|
||||
@consumer
|
||||
def printer():
|
||||
while True:
|
||||
item = yield # Receive an item sent to me
|
||||
print(item)
|
||||
|
||||
# Example use
|
||||
if __name__ == '__main__':
|
||||
follow('Data/stocklog.csv',printer())
|
||||
```
|
||||
|
||||
Run this program and make sure produces output.. Make sure you understand how the different pieces are hooked together.
|
||||
|
||||
## (b) Build some pipeline components
|
||||
|
||||
In a file `coticker.py`, build a series of pipeline components that carry out the same tasks as
|
||||
the `ticker.py` program in link:ex8_2.html[Exercise 8.2]. Here is the implementation of the
|
||||
various pieces.
|
||||
|
||||
```python
|
||||
# coticker.py
|
||||
from structure import Structure
|
||||
|
||||
class Ticker(Structure):
|
||||
name = String()
|
||||
price =Float()
|
||||
date = String()
|
||||
time = String()
|
||||
change = Float()
|
||||
open = Float()
|
||||
high = Float()
|
||||
low = Float()
|
||||
volume = Integer()
|
||||
|
||||
from cofollow import consumer, follow
|
||||
from tableformat import create_formatter
|
||||
import csv
|
||||
|
||||
# This one is tricky. See solution for notes about it
|
||||
@consumer
|
||||
def to_csv(target):
|
||||
def producer():
|
||||
while True:
|
||||
yield line
|
||||
|
||||
reader = csv.reader(producer())
|
||||
while True:
|
||||
line = yield
|
||||
target.send(next(reader))
|
||||
|
||||
@consumer
|
||||
def create_ticker(target):
|
||||
while True:
|
||||
row = yield
|
||||
target.send(Ticker.from_row(row))
|
||||
|
||||
@consumer
|
||||
def negchange(target):
|
||||
while True:
|
||||
record = yield
|
||||
if record.change < 0:
|
||||
target.send(record)
|
||||
|
||||
@consumer
|
||||
def ticker(fmt, fields):
|
||||
formatter = create_formatter(fmt)
|
||||
formatter.headings(fields)
|
||||
while True:
|
||||
rec = yield
|
||||
row = [getattr(rec, name) for name in fields]
|
||||
formatter.row(row)
|
||||
```
|
||||
|
||||
Your challenge: Write the main program that hooks all of these components together to
|
||||
generate the same stock ticker as in the previous exercise.
|
||||
|
||||
\[ [Solution](soln8_3.md) | [Index](index.md) | [Exercise 8.2](ex8_2.md) | [Exercise 8.4](ex8_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
153
Exercises/ex8_4.md
Normal file
153
Exercises/ex8_4.md
Normal file
@ -0,0 +1,153 @@
|
||||
\[ [Index](index.md) | [Exercise 8.3](ex8_3.md) | [Exercise 8.5](ex8_5.md) \]
|
||||
|
||||
# Exercise 8.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Managing what happens at the `yield` statements
|
||||
|
||||
*Files Modified:* `follow.py`, `cofollow.py`
|
||||
|
||||
## (a) Closing a Generator
|
||||
|
||||
A common question concerning generators is their lifetime and garbage
|
||||
collection. For example, the `follow()` generator runs forever in
|
||||
an infinite `while` loop. What happens if the iteration loop that's
|
||||
driving it stops? Also, is there anyway to prematurely terminate the
|
||||
generator?
|
||||
|
||||
Modify the `follow()` function so that all of the code is enclosed in
|
||||
a `try-except` block like this:
|
||||
|
||||
```python
|
||||
def follow(filename):
|
||||
try:
|
||||
with open(filename,'r') as f:
|
||||
f.seek(0,os.SEEK_END)
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line == '':
|
||||
time.sleep(0.1) # Sleep briefly to avoid busy wait
|
||||
continue
|
||||
yield line
|
||||
except GeneratorExit:
|
||||
print('Following Done')
|
||||
```
|
||||
|
||||
Now, try a few experiments:
|
||||
|
||||
```python
|
||||
>>> from follow import follow
|
||||
>>> # Experiment: Garbage collection of a running generator
|
||||
>>> f = follow('Data/stocklog.csv')
|
||||
>>> next(f)
|
||||
'"MO",70.29,"6/11/2007","09:30.09",-0.01,70.25,70.30,70.29,365314\n'
|
||||
>>> del f
|
||||
Following Done
|
||||
>>> # Experiment: Closing a generator
|
||||
>>> f = follow('Data/stocklog.csv')
|
||||
>>> for line in f:
|
||||
print(line,end='')
|
||||
if 'IBM' in line:
|
||||
f.close()
|
||||
|
||||
"VZ",42.91,"6/11/2007","09:34.28",-0.16,42.95,42.91,42.78,210151
|
||||
"HPQ",45.76,"6/11/2007","09:34.29",0.06,45.80,45.76,45.59,257169
|
||||
"GM",31.45,"6/11/2007","09:34.31",0.45,31.00,31.50,31.45,582429
|
||||
...
|
||||
"IBM",102.86,"6/11/2007","09:34.44",-0.21,102.87,102.86,102.77,147550
|
||||
Following Done
|
||||
>>> for line in f:
|
||||
print(line, end='') # No output: generator is done
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
In these experiments you can see that a `GeneratorExit` exception is
|
||||
raised when a generator is garbage-collected or explicitly closed via
|
||||
its `close()` method.
|
||||
|
||||
One additional area of exploration is whether or not you can resume
|
||||
iteration on a generator if you break out of a for-loop. For example,
|
||||
try this:
|
||||
|
||||
```python
|
||||
>>> f = follow('Data/stocklog.csv')
|
||||
>>> for line in f:
|
||||
print(line,end='')
|
||||
if 'IBM' in line:
|
||||
break
|
||||
|
||||
"CAT",78.36,"6/11/2007","09:37.19",-0.16,78.32,78.36,77.99,237714
|
||||
"VZ",42.99,"6/11/2007","09:37.20",-0.08,42.95,42.99,42.78,268459
|
||||
...
|
||||
"IBM",102.91,"6/11/2007","09:37.31",-0.16,102.87,102.91,102.77,190859
|
||||
>>> # Resume iteration
|
||||
>>> for line in f:
|
||||
print(line,end='')
|
||||
if 'IBM' in line:
|
||||
break
|
||||
|
||||
"AA",39.58,"6/11/2007","09:39.28",-0.08,39.67,39.58,39.31,243159
|
||||
"HPQ",45.94,"6/11/2007","09:39.29",0.24,45.80,45.94,45.59,408919
|
||||
...
|
||||
"IBM",102.95,"6/11/2007","09:39.44",-0.12,102.87,102.95,102.77,225350
|
||||
>>> del f
|
||||
Following Done
|
||||
>>>
|
||||
```
|
||||
|
||||
In general, you can break out of running iteration and resume it later
|
||||
if you need to. You just need to make sure the generator object isn't
|
||||
forcefully closed or garbage collected somehow.
|
||||
|
||||
## (b) Raising Exceptions
|
||||
|
||||
In the file `cofollow.py`, you created a coroutine `printer()`. Modify the
|
||||
code to catch and report exceptions like this:
|
||||
|
||||
```python
|
||||
# cofollow.py
|
||||
...
|
||||
@consumer
|
||||
def printer():
|
||||
while True:
|
||||
try:
|
||||
item = yield
|
||||
print(item)
|
||||
except Exception as e:
|
||||
print('ERROR: %r' % e)
|
||||
```
|
||||
|
||||
Now, try an experiment:
|
||||
|
||||
```python
|
||||
>>> from cofollow import printer
|
||||
>>> p = printer()
|
||||
>>> p.send('hello')
|
||||
hello
|
||||
>>> p.send(42)
|
||||
42
|
||||
>>> p.throw(ValueError('It failed'))
|
||||
ERROR: ValueError('It failed',)
|
||||
>>> try:
|
||||
int('n/a')
|
||||
except ValueError as e:
|
||||
p.throw(e)
|
||||
|
||||
ERROR: ValueError("invalid literal for int() with base 10: 'n/a'",)
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how the running generator is not terminated by the exception. This
|
||||
is merely allowing the `yield` statement to signal an error instead of
|
||||
receiving a value.
|
||||
|
||||
\[ [Solution](soln8_4.md) | [Index](index.md) | [Exercise 8.3](ex8_3.md) | [Exercise 8.5](ex8_5.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
225
Exercises/ex8_5.md
Normal file
225
Exercises/ex8_5.md
Normal file
@ -0,0 +1,225 @@
|
||||
\[ [Index](index.md) | [Exercise 8.4](ex8_4.md) | [Exercise 8.6](ex8_6.md) \]
|
||||
|
||||
# Exercise 8.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about managed generators
|
||||
|
||||
*Files Created:* `multitask.py`, `server.py`
|
||||
|
||||
A generator or coroutine function can never execute without being
|
||||
driven by some other code. For example, a generator used for
|
||||
iteration doesn't do anything unless iteration is actually carried out
|
||||
using a for-loop. Similarly, a collection of coroutines won't run
|
||||
unless their `send()` method is invoked somehow.
|
||||
|
||||
In advanced applications of generators, it is possible to drive
|
||||
generators in various unusual ways. In this exercise, we look at a
|
||||
few examples.
|
||||
|
||||
## (a) Generators as tasks
|
||||
|
||||
If a file `multitask.py`, define the following code:
|
||||
|
||||
```python
|
||||
# multitask.py
|
||||
|
||||
from collections import deque
|
||||
|
||||
tasks = deque()
|
||||
def run():
|
||||
while tasks:
|
||||
task = tasks.popleft()
|
||||
try:
|
||||
task.send(None)
|
||||
tasks.append(task)
|
||||
except StopIteration:
|
||||
print('Task done')
|
||||
```
|
||||
|
||||
This code implements a tiny task scheduler that runs generator functions.
|
||||
Try it by running it on the following functions.
|
||||
|
||||
```python
|
||||
# multitask.py
|
||||
...
|
||||
|
||||
def countdown(n):
|
||||
while n > 0:
|
||||
print('T-minus', n)
|
||||
yield
|
||||
n -= 1
|
||||
|
||||
def countup(n):
|
||||
x = 0
|
||||
while x < n:
|
||||
print('Up we go', x)
|
||||
yield
|
||||
x += 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
tasks.append(countdown(10))
|
||||
tasks.append(countdown(5))
|
||||
tasks.append(countup(20))
|
||||
run()
|
||||
```
|
||||
|
||||
When you run this, you should see output from all of the generators
|
||||
interleaved together. For example:
|
||||
|
||||
```python
|
||||
T-minus 10
|
||||
T-minus 5
|
||||
Up we go 0
|
||||
T-minus 9
|
||||
T-minus 4
|
||||
Up we go 1
|
||||
T-minus 8
|
||||
T-minus 3
|
||||
Up we go 2
|
||||
T-minus 7
|
||||
T-minus 2
|
||||
Up we go 3
|
||||
T-minus 6
|
||||
T-minus 1
|
||||
Up we go 4
|
||||
T-minus 5
|
||||
Task done
|
||||
Up we go 5
|
||||
T-minus 4
|
||||
Up we go 6
|
||||
T-minus 3
|
||||
Up we go 7
|
||||
T-minus 2
|
||||
Up we go 8
|
||||
T-minus 1
|
||||
Up we go 9
|
||||
Task done
|
||||
Up we go 10
|
||||
Up we go 11
|
||||
Up we go 12
|
||||
Up we go 13
|
||||
Up we go 14
|
||||
Up we go 15
|
||||
Up we go 16
|
||||
Up we go 17
|
||||
Up we go 18
|
||||
Up we go 19
|
||||
Task done
|
||||
```
|
||||
|
||||
That's interesting, but not especially compelling. Move on to the next example.
|
||||
|
||||
## (b) Generators as Tasks Serving Network Connections
|
||||
|
||||
Create a file `server.py` and put the following code into it:
|
||||
|
||||
```python
|
||||
# server.py
|
||||
|
||||
from socket import *
|
||||
from select import select
|
||||
from collections import deque
|
||||
|
||||
tasks = deque()
|
||||
recv_wait = {} # sock -> task
|
||||
send_wait = {} # sock -> task
|
||||
|
||||
def run():
|
||||
while any([tasks, recv_wait, send_wait]):
|
||||
while not tasks:
|
||||
can_recv, can_send, _ = select(recv_wait, send_wait, [])
|
||||
for s in can_recv:
|
||||
tasks.append(recv_wait.pop(s))
|
||||
for s in can_send:
|
||||
tasks.append(send_wait.pop(s))
|
||||
task = tasks.popleft()
|
||||
try:
|
||||
reason, resource = task.send(None)
|
||||
if reason == 'recv':
|
||||
recv_wait[resource] = task
|
||||
elif reason == 'send':
|
||||
send_wait[resource] = task
|
||||
else:
|
||||
raise RuntimeError('Unknown reason %r' % reason)
|
||||
except StopIteration:
|
||||
print('Task done')
|
||||
```
|
||||
|
||||
This code is a slightly more complicated version of the task scheduler in
|
||||
part (a). It will require a bit of study, but the idea is that not only
|
||||
will each task yield, it will indicate a reason for doing so (receiving or
|
||||
sending). Depending on the reason, the task will move over to a waiting
|
||||
area. The scheduler then runs any available tasks or waits for I/O
|
||||
events to occur when nothing is left to do.
|
||||
|
||||
It's all a bit tricky perhaps, but add the following code which implements
|
||||
a simple echo server:
|
||||
|
||||
```python
|
||||
# server.py
|
||||
...
|
||||
|
||||
def tcp_server(address, handler):
|
||||
sock = socket(AF_INET, SOCK_STREAM)
|
||||
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||
sock.bind(address)
|
||||
sock.listen(5)
|
||||
while True:
|
||||
yield 'recv', sock
|
||||
client, addr = sock.accept()
|
||||
tasks.append(handler(client, addr))
|
||||
|
||||
def echo_handler(client, address):
|
||||
print('Connection from', address)
|
||||
while True:
|
||||
yield 'recv', client
|
||||
data = client.recv(1000)
|
||||
if not data:
|
||||
break
|
||||
yield 'send', client
|
||||
client.send(b'GOT:' + data)
|
||||
print('Connection closed')
|
||||
|
||||
if __name__ == '__main__':
|
||||
tasks.append(tcp_server(('',25000), echo_handler))
|
||||
run()
|
||||
```
|
||||
|
||||
Run this server in its own terminal window. In another terminal, connect to it using a command such as `telnet` or `nc`. For example:
|
||||
|
||||
```
|
||||
bash % nc localhost 25000
|
||||
Hello
|
||||
Got: Hello
|
||||
World
|
||||
Got: World
|
||||
```
|
||||
|
||||
If you don't have access to `nc` or `telnet` you can also use Python itself:
|
||||
|
||||
```
|
||||
bash % python3 -m telnetlib localhost 25000
|
||||
Hello
|
||||
Got: Hello
|
||||
World
|
||||
Got: World
|
||||
```
|
||||
|
||||
If it's working, you should see output being echoed back to you. Not only that,
|
||||
if you connect multiple clients, they'll all operate concurrently.
|
||||
|
||||
This tricky use of generators is not something that you would
|
||||
likely have to code directly. However, they are used in certain advanced
|
||||
packages such as `asyncio` that was added to the standard
|
||||
library in Python 3.4.
|
||||
|
||||
\[ [Solution](soln8_5.md) | [Index](index.md) | [Exercise 8.4](ex8_4.md) | [Exercise 8.6](ex8_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
200
Exercises/ex8_6.md
Normal file
200
Exercises/ex8_6.md
Normal file
@ -0,0 +1,200 @@
|
||||
\[ [Index](index.md) | [Exercise 8.5](ex8_5.md) | [Exercise 9.1](ex9_1.md) \]
|
||||
|
||||
# Exercise 8.6
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about delegating generators
|
||||
|
||||
*Files Modified:* `cofollow.py`, `server.py`
|
||||
|
||||
One potential issue in code that relies on generators is the problem
|
||||
of hiding details from the user and writing libraries. A lot of low-level
|
||||
mechanics are generally required to drive everything and it's often rather
|
||||
awkward to directly expose it to users.
|
||||
|
||||
Starting in Python 3.3, a new `yield from` statement can be used to
|
||||
delegate generators to another function. It is a useful way to
|
||||
clean-up code that relies on generators.
|
||||
|
||||
## (a) Example: Receiving messages
|
||||
|
||||
In link:ex8_3.html[Exercise 8.3], we looked at the definitions of coroutines.
|
||||
Coroutines were functions that you sent data to. For example:
|
||||
|
||||
```python
|
||||
>>> from cofollow import consumer
|
||||
>>> @consumer
|
||||
def printer():
|
||||
while True:
|
||||
item = yield
|
||||
print('Got:', item)
|
||||
|
||||
>>> p = printer()
|
||||
>>> p.send('Hello')
|
||||
Got: Hello
|
||||
>>> p.send('World')
|
||||
Got: World
|
||||
>>>
|
||||
```
|
||||
|
||||
At the time, it might have been interesting to use `yield` to receive a
|
||||
value. However, if you really look at the code, it looks pretty weird--a
|
||||
bare `yield` like that? What's going on there?
|
||||
|
||||
In the `cofollow.py` file, define the following function:
|
||||
|
||||
```python
|
||||
def receive(expected_type):
|
||||
msg = yield
|
||||
assert isinstance(msg, expected_type), 'Expected type %s' % (expected_type)
|
||||
return msg
|
||||
```
|
||||
|
||||
This function receives a message, but then verifies that it is of an expected
|
||||
type. Try it:
|
||||
|
||||
```python
|
||||
>>> from cofollow import consumer, receive
|
||||
>>> @consumer
|
||||
def print_ints():
|
||||
while True:
|
||||
val = yield from receive(int)
|
||||
print('Got:', val)
|
||||
|
||||
>>> p = print_ints()
|
||||
>>> p.send(42)
|
||||
Got: 42
|
||||
>>> p.send(13)
|
||||
Got: 13
|
||||
>>> p.send('13')
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
...
|
||||
AssertionError: Expected type <class 'int'>
|
||||
>>>
|
||||
```
|
||||
|
||||
From a readability point of view, the `yield from receive(int)` statement
|
||||
is a bit more descriptive--it indicates that the function will yield until
|
||||
it receives a message of a given type.
|
||||
|
||||
Now, modify all of the coroutines in `coticker.py` to use the new `receive()`
|
||||
function and make sure the code from link:ex8_3.html[Exercise 8.3] still
|
||||
works.
|
||||
|
||||
## (b) Wrapping a Socket
|
||||
|
||||
In the previous exercise, you wrote a simple network echo server using
|
||||
generators. The code for the server looked like this:
|
||||
|
||||
```python
|
||||
def tcp_server(address, handler):
|
||||
sock = socket(AF_INET, SOCK_STREAM)
|
||||
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||
sock.bind(address)
|
||||
sock.listen(5)
|
||||
while True:
|
||||
yield 'recv', sock
|
||||
client, addr = sock.accept()
|
||||
tasks.append(handler(client, addr))
|
||||
|
||||
def echo_handler(client, address):
|
||||
print('Connection from', address)
|
||||
while True:
|
||||
yield 'recv', client
|
||||
data = client.recv(1000)
|
||||
if not data:
|
||||
break
|
||||
yield 'send', client
|
||||
client.send(b'GOT:', data)
|
||||
print('Connection closed')
|
||||
```
|
||||
|
||||
Create a class `GenSocket` that cleans up the `yield` statements and
|
||||
allows the server to be rewritten more simply as follows:
|
||||
|
||||
```python
|
||||
def tcp_server(address, handler):
|
||||
sock = GenSocket(socket(AF_INET, SOCK_STREAM))
|
||||
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||
sock.bind(address)
|
||||
sock.listen(5)
|
||||
while True:
|
||||
client, addr = yield from sock.accept()
|
||||
tasks.append(handler(client, addr))
|
||||
|
||||
def echo_handler(client, address):
|
||||
print('Connection from', address)
|
||||
while True:
|
||||
data = yield from client.recv(1000)
|
||||
if not data:
|
||||
break
|
||||
yield from client.send(b'GOT:', data)
|
||||
print('Connection closed')
|
||||
```
|
||||
|
||||
## (c) Async/Await
|
||||
|
||||
Take the `GenSocket` class you just wrote and wrap all of the methods
|
||||
that use `yield` with the `@coroutine` decorator from the `types` module.
|
||||
|
||||
```python
|
||||
from types import coroutine
|
||||
...
|
||||
|
||||
class GenSocket:
|
||||
def __init__(self, sock):
|
||||
self.sock = sock
|
||||
|
||||
@coroutine
|
||||
def accept(self):
|
||||
yield 'recv', self.sock
|
||||
client, addr = self.sock.accept()
|
||||
return GenSocket(client), addr
|
||||
|
||||
@coroutine
|
||||
def recv(self, maxsize):
|
||||
yield 'recv', self.sock
|
||||
return self.sock.recv(maxsize)
|
||||
|
||||
@coroutine
|
||||
def send(self, data):
|
||||
yield 'send', self.sock
|
||||
return self.sock.send(data)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.sock, name)
|
||||
```
|
||||
|
||||
Now, rewrite your server code to use `async` functions and `await` statements like this:
|
||||
|
||||
```python
|
||||
async def tcp_server(address, handler):
|
||||
sock = GenSocket(socket(AF_INET, SOCK_STREAM))
|
||||
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||
sock.bind(address)
|
||||
sock.listen(5)
|
||||
while True:
|
||||
client, addr = await sock.accept()
|
||||
tasks.append(handler(client, addr))
|
||||
|
||||
async def echo_handler(client, address):
|
||||
print('Connection from', address)
|
||||
while True:
|
||||
data = await client.recv(1000)
|
||||
if not data:
|
||||
break
|
||||
await client.send(b'GOT:', data)
|
||||
print('Connection closed')
|
||||
```
|
||||
|
||||
|
||||
\[ [Solution](soln8_6.md) | [Index](index.md) | [Exercise 8.5](ex8_5.md) | [Exercise 9.1](ex9_1.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
211
Exercises/ex9_1.md
Normal file
211
Exercises/ex9_1.md
Normal file
@ -0,0 +1,211 @@
|
||||
\[ [Index](index.md) | [Exercise 8.6](ex8_6.md) | [Exercise 9.2](ex9_2.md) \]
|
||||
|
||||
# Exercise 9.1
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- A review of module basics
|
||||
|
||||
This exercise is about some of the more tricky details of library modules.
|
||||
Start this exercise by creating a very simple library module:
|
||||
|
||||
```python
|
||||
# simplemod.py
|
||||
|
||||
x = 42 # A global variable
|
||||
|
||||
# A simple function
|
||||
def foo():
|
||||
print('x is', x)
|
||||
|
||||
# A simple class
|
||||
class Spam:
|
||||
def yow(self):
|
||||
print('Yow!')
|
||||
|
||||
# A scripting statement
|
||||
print('Loaded simplemod')
|
||||
```
|
||||
|
||||
## (a) Module Loading and System Path
|
||||
|
||||
Try importing the module you just created:
|
||||
|
||||
```python
|
||||
>>> import simplemod
|
||||
Loaded simplemod
|
||||
>>> simplemod.foo()
|
||||
x is 42
|
||||
>>>
|
||||
```
|
||||
|
||||
If this failed with an `ImportError`, your path setting is
|
||||
flaky. Look at the value of `sys.path` and fix it.
|
||||
|
||||
```python
|
||||
>>> import sys
|
||||
>>> sys.path
|
||||
... look at the result ...
|
||||
>>>
|
||||
```
|
||||
|
||||
## (b) Repeated Module Loading
|
||||
|
||||
Make sure you understand that modules are only loaded
|
||||
once. Try a repeated import and notice how you do not see
|
||||
the output from the `print` function:
|
||||
|
||||
```python
|
||||
>>> import simplemod
|
||||
>>>
|
||||
```
|
||||
|
||||
Try changing the value of `x` and see that a repeated import
|
||||
has no effect.
|
||||
|
||||
```python
|
||||
>>> simplemod.x
|
||||
42
|
||||
>>> simplemod.x = 13
|
||||
>>> simplemod.x
|
||||
13
|
||||
>>> import simplemod
|
||||
>>> simplemod.x
|
||||
13
|
||||
>>>
|
||||
```
|
||||
|
||||
Use `importlib.reload()` if you want to force a module to reload.
|
||||
|
||||
```python
|
||||
>>> import importlib
|
||||
>>> importlib.reload(simplemod)
|
||||
Loaded simplemod
|
||||
<module 'simplemod' from 'simplemod.py'>
|
||||
>>> simplemod.x
|
||||
42
|
||||
>>>
|
||||
```
|
||||
|
||||
`sys.modules` is a dictionary of all loaded modules. Take
|
||||
a look at it, delete your module, and try a repeated import.
|
||||
|
||||
```python
|
||||
>>> sys.modules
|
||||
... look at output ...
|
||||
>>> sys.modules['simplemod']
|
||||
<module 'simplemod' from 'simplemod.py'>
|
||||
>>> del sys.modules['simplemod']
|
||||
>>> import simplemod
|
||||
Loaded simplemod
|
||||
>>>
|
||||
```
|
||||
|
||||
## (c) from module import
|
||||
|
||||
Restart Python and import a selected symbol from a module.
|
||||
|
||||
```python
|
||||
>>> ############### [ RESTART ] ###############
|
||||
>>> from simplemod import foo
|
||||
Loaded simplemod
|
||||
>>> foo()
|
||||
x is 42
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how this loaded the entire module (observe the output of
|
||||
the print function and how the `x` variable is used).
|
||||
|
||||
When you use `from`, the module object itself is not
|
||||
visible. For example:
|
||||
|
||||
```python
|
||||
>>> simplemod.foo()
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
NameError: name 'simplemod' is not defined
|
||||
>>>
|
||||
```
|
||||
|
||||
Make sure you understand that when you export things from a module,
|
||||
they are simply name references. For example, try this and explain:
|
||||
|
||||
```python
|
||||
>>> from simplemod import x,foo
|
||||
>>> x
|
||||
42
|
||||
>>> foo()
|
||||
x is 42
|
||||
>>> x = 13
|
||||
>>> foo()
|
||||
x is 42 # !! Please explain
|
||||
>>> x
|
||||
13
|
||||
>>>
|
||||
```
|
||||
|
||||
## (d) Broken reload()
|
||||
|
||||
Create an instance:
|
||||
|
||||
```python
|
||||
>>> import simplemod
|
||||
>>> s = simplemod.Spam()
|
||||
>>> s.yow()
|
||||
Yow!
|
||||
>>>
|
||||
```
|
||||
|
||||
Now, go to the `simplemod.py` file and change the implementation of `Spam.yow()` to the
|
||||
following:
|
||||
|
||||
```python
|
||||
# simplemod.py
|
||||
...
|
||||
|
||||
class Spam:
|
||||
def yow(self):
|
||||
print('More Yow!')
|
||||
```
|
||||
|
||||
Now, watch what happens on a reload. Do not restart Python for this part.
|
||||
|
||||
```python
|
||||
>>> importlib.reload(simplemod)
|
||||
Loaded simplemod
|
||||
<module 'simplemod' from 'simplemod.py'>
|
||||
>>> s.yow()
|
||||
'Yow!'
|
||||
>>> t = simplemod.Spam()
|
||||
>>> t.yow()
|
||||
'More Yow!'
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how you have two instances of `Spam`, but they're using different implementations
|
||||
of the `yow()` method. Yes, actually both versions of code are loaded at the same time.
|
||||
You'll find other oddities as well. For example:
|
||||
|
||||
```python
|
||||
>>> s
|
||||
<simplemod.Spam object at 0x1006940b8>
|
||||
>>> isinstance(s, simplemod.Spam)
|
||||
False
|
||||
>>> isinstance(t, simplemod.Spam)
|
||||
True
|
||||
>>>
|
||||
```
|
||||
|
||||
Bottom line: It's probably best not to rely on reloading for anything important.
|
||||
It might be fine if you're just trying to debug some things (as long as you're
|
||||
aware of its limitations and dangers).
|
||||
|
||||
\[ [Solution](soln9_1.md) | [Index](index.md) | [Exercise 8.6](ex8_6.md) | [Exercise 9.2](ex9_2.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
67
Exercises/ex9_2.md
Normal file
67
Exercises/ex9_2.md
Normal file
@ -0,0 +1,67 @@
|
||||
\[ [Index](index.md) | [Exercise 9.1](ex9_1.md) | [Exercise 9.3](ex9_3.md) \]
|
||||
|
||||
# Exercise 9.2
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn how to create a Python package
|
||||
|
||||
**Note**
|
||||
|
||||
This exercise mostly just involves copying files on the file system.
|
||||
There shouldn't be a lot of coding.
|
||||
|
||||
## (a) Making a Package
|
||||
|
||||
In previous exercises, you created the following files that were related to
|
||||
type-checked structures, reading data, and making tables:
|
||||
|
||||
- `structure.py`
|
||||
- `validate.py`
|
||||
- `reader.py`
|
||||
- `tableformat.py`
|
||||
|
||||
Your task is to take all of these files and move them into a package called `structly`.
|
||||
To do that, follow these steps:
|
||||
|
||||
- Make a directory called `structly`
|
||||
- Make an empty file `__init__.py` and put it in the `structly` directory
|
||||
- Move the files `structure.py`, `validate.py`, `reader.py`, and `tableformat.py` into the `structly` directory.
|
||||
- Fix any import statements between modules (specifically, the `structure` module depends on `validate`).
|
||||
|
||||
Once you've done that, modify the `stock.py` program so that it looks exactly like this
|
||||
and that it works:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structly.structure import Structure
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares: PositiveInteger):
|
||||
self.shares -= nshares
|
||||
|
||||
if __name__ == '__main__':
|
||||
from structly.reader import read_csv_as_instances
|
||||
from structly.tableformat import create_formatter, print_table
|
||||
portfolio = read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
formatter = create_formatter('text')
|
||||
print_table(portfolio, ['name','shares','price'], formatter)
|
||||
```
|
||||
|
||||
\[ [Solution](soln9_2.md) | [Index](index.md) | [Exercise 9.1](ex9_1.md) | [Exercise 9.3](ex9_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
208
Exercises/ex9_3.md
Normal file
208
Exercises/ex9_3.md
Normal file
@ -0,0 +1,208 @@
|
||||
\[ [Index](index.md) | [Exercise 9.2](ex9_2.md) | [Exercise 9.4](ex9_4.md) \]
|
||||
|
||||
# Exercise 9.3
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn about controlling symbols and combining submodules
|
||||
- Learn about module splitting
|
||||
|
||||
One potentially annoying aspect of packages is that they complicate
|
||||
import statements. For example, in the `stock.py` program, you now
|
||||
have import statements such as the following:
|
||||
|
||||
```python
|
||||
from structly.structure import Structure
|
||||
from structly.reader import read_csv_as_instances
|
||||
from structly.tableformat import create_formatter, print_table
|
||||
```
|
||||
|
||||
If the package is meant to be used as a unified whole, it might be
|
||||
more sane (and easier) to consolidate everything into a single top
|
||||
level package. Let's do that:
|
||||
|
||||
## (a) Controlling Exported Symbols
|
||||
|
||||
Modify all of the submodules in the `structly` package so that they explicitly
|
||||
define an `__all__` variable which exports selected symbols. Specifically:
|
||||
|
||||
- `structure.py` should export `Structure`
|
||||
- `reader.py` should export all of the various `read_csv_as_*()` functions
|
||||
- `tableformat.py` exports `create_formatter()` and `print_table()`
|
||||
|
||||
Now, in the `__init__.py` file, unify all of the submodules like this:
|
||||
|
||||
```python
|
||||
# structly/__init__.py
|
||||
|
||||
from .structure import *
|
||||
from .reader import *
|
||||
from .tableformat import *
|
||||
```
|
||||
|
||||
Once you have done this, you should be able to import everything from
|
||||
a single logical module:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structly import Structure
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares: PositiveInteger):
|
||||
self.shares -= nshares
|
||||
|
||||
if __name__ == '__main__':
|
||||
from structly import read_csv_as_instances, create_formatter, print_table
|
||||
portfolio = read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
formatter = create_formatter('text')
|
||||
print_table(portfolio, ['name','shares','price'], formatter)
|
||||
```
|
||||
|
||||
## (b) Exporting Everything
|
||||
|
||||
In the `structly/__init__.py`, define an `__all__` variable that contains all
|
||||
exported symbols. Once you've done this, you should be able to simplify the
|
||||
`stock.py` file further:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structly import *
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares: PositiveInteger):
|
||||
self.shares -= nshares
|
||||
|
||||
if __name__ == '__main__':
|
||||
portfolio = read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
formatter = create_formatter('text')
|
||||
print_table(portfolio, ['name','shares','price'], formatter)
|
||||
```
|
||||
|
||||
As an aside, use of the `from module import *` statement is generally frowned upon
|
||||
the Python community--especially if you're not sure what you're doing. That said,
|
||||
there are situations where it often makes sense. For example, if a package defines
|
||||
a large number of commonly used symbols or constants it might be useful to use it.
|
||||
|
||||
|
||||
## (c) Module Splitting
|
||||
|
||||
The file `structly/tableformat.py` contains code for creating tables in different
|
||||
formats. Specifically:
|
||||
|
||||
- A `TableFormatter` base class.
|
||||
- A `TextTableFormatter` class.
|
||||
- A `CSVTableFormatter` class.
|
||||
- A `HTMLTableFormatter` class.
|
||||
|
||||
Instead of having all of these classes in a single `.py`
|
||||
file, maybe it would make sense to move each concrete formatter to
|
||||
its own file. To do this, we're going to split the `tableformat.py`
|
||||
file into parts. Follow these instructions carefully:
|
||||
|
||||
First, remove the `structly/__pycache__` directory.
|
||||
|
||||
```
|
||||
% cd structly
|
||||
% rm -rf __pycache__
|
||||
```
|
||||
|
||||
Next, create the directory `structly/tableformat`. This directory
|
||||
must have exactly the same name as the module it is replacing
|
||||
(`tableformat.py`).
|
||||
|
||||
```
|
||||
bash % mkdir tableformat
|
||||
bash %
|
||||
```
|
||||
|
||||
Move the original `tableformat.py` file into the new
|
||||
`tableformat` directory and rename it to `formatter.py`.
|
||||
|
||||
```
|
||||
bash % mv tableformat.py tableformat/formatter.py
|
||||
bash %
|
||||
```
|
||||
|
||||
In the `tableformat` directory, split the
|
||||
`tableformat.py` code into the following files and directories:
|
||||
|
||||
- `formatter.py` - Contains the `TableFormatter` base class, mixins, and various functions.
|
||||
- `formats/text.py` - Contains the `TextTableFormatter` class.
|
||||
- `formats/csv.py` - Contains the `CSVTableFormatter` class.
|
||||
- `formats/html.py` - Contains the `HTMLTableFormatter` class.
|
||||
|
||||
Add an `__init__.py` file to the `tableformat/` and `tableformat/formats`
|
||||
directories. Have the `tableformat/__init__.py` export the same
|
||||
symbols that the original `tableformat.py` file exported.
|
||||
|
||||
After you have made all of these changes, you should have a package
|
||||
structure that looks like this:
|
||||
|
||||
```
|
||||
structly/
|
||||
__init__.py
|
||||
validate.py
|
||||
reader.py
|
||||
structure.py
|
||||
tableformat/
|
||||
__init__.py
|
||||
formatter.py
|
||||
formats/
|
||||
__init__.py
|
||||
text.py
|
||||
csv.py
|
||||
html.py
|
||||
```
|
||||
|
||||
To users, everything should work exactly as it did before. For example, your
|
||||
prior `stock.py` file should work:
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
from structly import *
|
||||
|
||||
class Stock(Structure):
|
||||
name = String()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
|
||||
if __name__ == '__main__':
|
||||
portfolio = read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
formatter = create_formatter('text')
|
||||
print_table(portfolio, ['name','shares','price'], formatter)
|
||||
```
|
||||
|
||||
\[ [Solution](soln9_3.md) | [Index](index.md) | [Exercise 9.2](ex9_2.md) | [Exercise 9.4](ex9_4.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
193
Exercises/ex9_4.md
Normal file
193
Exercises/ex9_4.md
Normal file
@ -0,0 +1,193 @@
|
||||
\[ [Index](index.md) | [Exercise 9.3](ex9_3.md) | []() \]
|
||||
|
||||
# Exercise 9.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Explore circular imports
|
||||
- Dynamic module imports
|
||||
|
||||
In the last exercise, you split the `tableformat.py` file up into submodules.
|
||||
The last part of the resulting `tableformat/formatter.py` file has turned into a mess of imports.
|
||||
|
||||
```python
|
||||
# tableformat.py
|
||||
...
|
||||
|
||||
class TableFormatter(ABC):
|
||||
@abstractmethod
|
||||
def headings(self, headers):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def row(self, rowdata):
|
||||
pass
|
||||
|
||||
from .formats.text import TextTableFormatter
|
||||
from .formats.csv import CSVTableFormatter
|
||||
from .formats.html import HTMLTableFormatter
|
||||
|
||||
...
|
||||
|
||||
def create_formatter(name, column_formats=None, upper_headers=False):
|
||||
if name == 'text':
|
||||
formatter_cls = TextTableFormatter
|
||||
elif name == 'csv':
|
||||
formatter_cls = CSVTableFormatter
|
||||
elif name == 'html':
|
||||
formatter_cls = HTMLTableFormatter
|
||||
else:
|
||||
raise RuntimeError('Unknown format %s' % name)
|
||||
|
||||
if column_formats:
|
||||
class formatter_cls(ColumnFormatMixin, formatter_cls):
|
||||
formats = column_formats
|
||||
|
||||
if upper_headers:
|
||||
class formatter_cls(UpperHeadersMixin, formatter_cls):
|
||||
pass
|
||||
|
||||
return formatter_cls()
|
||||
```
|
||||
|
||||
The imports in the middle of the file are required because the `create_formatter()`
|
||||
function needs them to find the appropriate classes. Really, the whole thing is a mess.
|
||||
|
||||
## (a) Circular Imports
|
||||
|
||||
Try moving the following import statements to the top of the `formatter.py` file:
|
||||
|
||||
```python
|
||||
# formatter.py
|
||||
|
||||
from .formats.text import TextTableFormatter
|
||||
from .formats.csv import CSVTableFormatter
|
||||
from .formats.html import HTMLTableFormatter
|
||||
|
||||
class TableFormatter(ABC):
|
||||
@abstractmethod
|
||||
def headings(self, headers):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def row(self, rowdata):
|
||||
pass
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
Observe that nothing works anymore. Try running the `stock.py` program and
|
||||
notice the error about `TableFormatter` not being defined. The order
|
||||
of import statements matters and you can't just move the imports anywhere
|
||||
you want.
|
||||
|
||||
Move the import statements back where they were. Sigh.
|
||||
|
||||
## (b) Subclass Registration
|
||||
|
||||
Try the following experiment and observe:
|
||||
|
||||
```python
|
||||
>>> from structly.tableformat.formats.text import TextTableFormatter
|
||||
>>> TextTableFormatter.__module__
|
||||
'structly.tableformat.formats.text'
|
||||
>>> TextTableFormatter.__module__.split('.')[-1]
|
||||
'text'
|
||||
>>>
|
||||
```
|
||||
|
||||
Modify the `TableFormatter` base class by adding a dictionary and an
|
||||
`__init_subclass__()` method:
|
||||
|
||||
```python
|
||||
class TableFormatter(ABC):
|
||||
_formats = { }
|
||||
|
||||
@classmethod
|
||||
def __init_subclass__(cls):
|
||||
name = cls.__module__.split('.')[-1]
|
||||
TableFormatter._formats[name] = cls
|
||||
|
||||
@abstractmethod
|
||||
def headings(self, headers):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def row(self, rowdata):
|
||||
pass
|
||||
```
|
||||
|
||||
This makes the parent class track all of its subclasses. Check it out:
|
||||
|
||||
```python
|
||||
>>> from structly.tableformat.formatter import TableFormatter
|
||||
>>> TableFormatter._formats
|
||||
{'text': <class 'structly.tableformat.formats.text.TextTableFormatter'>,
|
||||
'csv': <class 'structly.tableformat.formats.csv.CSVTableFormatter'>,
|
||||
'html': <class 'structly.tableformat.formats.html.HTMLTableFormatter'>}
|
||||
>>>
|
||||
```
|
||||
|
||||
Modify the `create_formatter()` function to look up the class in this dictionary
|
||||
instead:
|
||||
|
||||
```python
|
||||
def create_formatter(name, column_formats=None, upper_headers=False):
|
||||
formatter_cls = TableFormatter._formats.get(name)
|
||||
if not formatter_cls:
|
||||
raise RuntimeError('Unknown format %s' % name)
|
||||
|
||||
if column_formats:
|
||||
class formatter_cls(ColumnFormatMixin, formatter_cls):
|
||||
formats = column_formats
|
||||
|
||||
if upper_headers:
|
||||
class formatter_cls(UpperHeadersMixin, formatter_cls):
|
||||
pass
|
||||
|
||||
return formatter_cls()
|
||||
```
|
||||
|
||||
Run the `stock.py` program. Make sure it still works after you've made these changes.
|
||||
Just a note that all of the import statements are still there. You've mainly
|
||||
just cleaned up the code a bit and eliminated the hard-wired class names.
|
||||
|
||||
## (c) Dynamic Imports
|
||||
|
||||
You're now ready for the final frontier. Delete the following import statements
|
||||
altogether:
|
||||
|
||||
```python
|
||||
# formatter.py
|
||||
...
|
||||
|
||||
from .formats.text import TextTableFormatter # DELETE
|
||||
from .formats.csv import CSVTableFormatter # DELETE
|
||||
from .formats.html import HTMLTableFormatter # DELETE
|
||||
...
|
||||
```
|
||||
|
||||
Run your `stock.py` code again--it should fail with an error. It knows nothing about the
|
||||
text formatter. Fix it by adding this tiny fragment of code to `create_formatter()`:
|
||||
|
||||
```python
|
||||
def create_formatter(name, column_formats=None, upper_headers=False):
|
||||
if name not in TableFormatter._formats:
|
||||
__import__(f'{__package__}.formats.{name}')
|
||||
...
|
||||
```
|
||||
|
||||
This code attempts a dynamic import of a formatter module if nothing is known about the
|
||||
name. The import alone (if it works) will register the class with the `_formats`
|
||||
dictionary and everything will just work. Magic!
|
||||
|
||||
Try running the `stock.py` code and make sure it works afterwards.
|
||||
|
||||
\[ [Solution](soln9_4.md) | [Index](index.md) | [Exercise 9.3](ex9_3.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
95
Exercises/index.md
Normal file
95
Exercises/index.md
Normal file
@ -0,0 +1,95 @@
|
||||
# Advanced Python Mastery Exercises
|
||||
|
||||
Copyright (C) 2007-2023
|
||||
David M. Beazley
|
||||
https://www.dabeaz.com
|
||||
|
||||
This page contains links to all of the course exercises. For the best experience,
|
||||
the exercises should be worked in order as later exercises often build upon earlier
|
||||
exercises. The [`Solutions/`](../Solutions) directory contains fully worked
|
||||
out solutions to every exercise. You can use this if you get stuck or you
|
||||
need to reset your code to a working state.
|
||||
|
||||
Before beginning, you should fork/clone the GitHub repo so that you have a
|
||||
local copy on your machine. Exercises assume that all of your work will
|
||||
take place in the top-level `python-mastery` directory.
|
||||
|
||||
Associated course presentation slides can be found at [PythonMastery.pdf](../PythonMastery.pdf).
|
||||
|
||||
## 1. Python Review
|
||||
|
||||
- [Exercise 1.1](ex1_1.md)
|
||||
- [Exercise 1.2](ex1_2.md)
|
||||
- [Exercise 1.3](ex1_3.md)
|
||||
- [Exercise 1.4](ex1_4.md)
|
||||
- [Exercise 1.5](ex1_5.md)
|
||||
- [Exercise 1.6](ex1_6.md)
|
||||
|
||||
## 2. Data Handling
|
||||
|
||||
- [Exercise 2.1](ex2_1.md)
|
||||
- [Exercise 2.2](ex2_2.md)
|
||||
- [Exercise 2.3](ex2_3.md)
|
||||
- [Exercise 2.4](ex2_4.md)
|
||||
- [Exercise 2.5](ex2_5.md)
|
||||
- [Exercise 2.6](ex2_6.md)
|
||||
|
||||
## 3. Classes and Objects
|
||||
|
||||
- [Exercise 3.1](ex3_1.md)
|
||||
- [Exercise 3.2](ex3_2.md)
|
||||
- [Exercise 3.3](ex3_3.md)
|
||||
- [Exercise 3.4](ex3_4.md)
|
||||
- [Exercise 3.5](ex3_5.md)
|
||||
- [Exercise 3.6](ex3_6.md)
|
||||
- [Exercise 3.7](ex3_7.md)
|
||||
- [Exercise 3.8](ex3_8.md)
|
||||
|
||||
## 4. Inside Python Objects
|
||||
|
||||
- [Exercise 4.1](ex4_1.md)
|
||||
- [Exercise 4.2](ex4_2.md)
|
||||
- [Exercise 4.3](ex4_3.md)
|
||||
- [Exercise 4.4](ex4_4.md)
|
||||
|
||||
## 5. Functions, Errors, and Testing
|
||||
|
||||
- [Exercise 5.1](ex5_1.md)
|
||||
- [Exercise 5.2](ex5_2.md)
|
||||
- [Exercise 5.3](ex5_3.md)
|
||||
- [Exercise 5.4](ex5_4.md)
|
||||
- [Exercise 5.5](ex5_5.md)
|
||||
- [Exercise 5.6](ex5_6.md)
|
||||
|
||||
## 6. Working with Code
|
||||
|
||||
- [Exercise 6.1](ex6_1.md)
|
||||
- [Exercise 6.2](ex6_2.md)
|
||||
- [Exercise 6.3](ex6_3.md)
|
||||
- [Exercise 6.4](ex6_4.md)
|
||||
- [Exercise 6.5](ex6_5.md)
|
||||
|
||||
## 7. Metaprogramming
|
||||
|
||||
- [Exercise 7.1](ex7_1.md)
|
||||
- [Exercise 7.2](ex7_2.md)
|
||||
- [Exercise 7.3](ex7_3.md)
|
||||
- [Exercise 7.4](ex7_4.md)
|
||||
- [Exercise 7.5](ex7_5.md)
|
||||
- [Exercise 7.6](ex7_6.md)
|
||||
|
||||
## 8. Iterators, Generators, and Coroutines
|
||||
|
||||
- [Exercise 8.1](ex8_1.md)
|
||||
- [Exercise 8.2](ex8_2.md)
|
||||
- [Exercise 8.3](ex8_3.md)
|
||||
- [Exercise 8.4](ex8_4.md)
|
||||
- [Exercise 8.5](ex8_5.md)
|
||||
- [Exercise 8.6](ex8_6.md)
|
||||
|
||||
## 9. Modules and Packages
|
||||
|
||||
- [Exercise 9.1](ex9_1.md)
|
||||
- [Exercise 9.2](ex9_2.md)
|
||||
- [Exercise 9.3](ex9_3.md)
|
||||
- [Exercise 9.4](ex9_4.md)
|
||||
6
Exercises/soln1_1.md
Normal file
6
Exercises/soln1_1.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Exercise 1.1 - Solution
|
||||
|
||||
Nothing here. Just follow along with the exercise.
|
||||
|
||||
|
||||
[Back](ex1_1.md)
|
||||
6
Exercises/soln1_2.md
Normal file
6
Exercises/soln1_2.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Exercise 1.2 - Solution
|
||||
|
||||
Solution code is shown in the exercise.
|
||||
|
||||
|
||||
[Back](ex1_2.md)
|
||||
19
Exercises/soln1_3.md
Normal file
19
Exercises/soln1_3.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Exercise 1.3 - Solution
|
||||
|
||||
```python
|
||||
# pcost.py
|
||||
|
||||
total_cost = 0.0
|
||||
|
||||
with open('Data/portfolio.dat', 'r') as f:
|
||||
for line in f:
|
||||
fields = line.split()
|
||||
nshares = int(fields[1])
|
||||
price = float(fields[2])
|
||||
total_cost = total_cost + nshares * price
|
||||
|
||||
print(total_cost)
|
||||
```
|
||||
|
||||
|
||||
[Back](ex1_3.md)
|
||||
46
Exercises/soln1_4.md
Normal file
46
Exercises/soln1_4.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Exercise 1.4 - Solution
|
||||
|
||||
## (a) Defining a function
|
||||
|
||||
```python
|
||||
# pcost.py
|
||||
|
||||
def portfolio_cost(filename):
|
||||
total_cost = 0.0
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
fields = line.split()
|
||||
nshares = int(fields[1])
|
||||
price = float(fields[2])
|
||||
total_cost = total_cost + nshares * price
|
||||
return total_cost
|
||||
|
||||
print(portfolio_cost('Data/portfolio.dat'))
|
||||
```
|
||||
|
||||
## (b) Adding some error handling
|
||||
|
||||
```python
|
||||
# pcost.py
|
||||
|
||||
def portfolio_cost(filename):
|
||||
total_cost = 0.0
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
fields = line.split()
|
||||
try:
|
||||
nshares = int(fields[1])
|
||||
price = float(fields[2])
|
||||
total_cost = total_cost + nshares*price
|
||||
|
||||
# This catches errors in int() and float() conversions above
|
||||
except ValueError as e:
|
||||
print("Couldn't parse:", repr(line))
|
||||
print("Reason:", e)
|
||||
return total_cost
|
||||
|
||||
print(portfolio_cost('Data/portfolio3.dat'))
|
||||
```
|
||||
|
||||
|
||||
[Back](ex1_4.md)
|
||||
6
Exercises/soln1_5.md
Normal file
6
Exercises/soln1_5.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Exercise 1.5 - Solution
|
||||
|
||||
The solution is shown in the exercise description.
|
||||
|
||||
|
||||
[Back](ex1_5.md)
|
||||
30
Exercises/soln1_6.md
Normal file
30
Exercises/soln1_6.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Exercise 1.6 - Solution
|
||||
|
||||
## (b) Main Module
|
||||
|
||||
```python
|
||||
# pcost.py
|
||||
|
||||
def portfolio_cost(filename):
|
||||
total_cost = 0.0
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
fields = line.split()
|
||||
try:
|
||||
nshares = int(fields[1])
|
||||
price = float(fields[2])
|
||||
total_cost = total_cost + nshares * price
|
||||
|
||||
# This catches errors in int() and float() conversions above
|
||||
except ValueError as e:
|
||||
print("Couldn't parse:", line)
|
||||
print("Reason:", e)
|
||||
|
||||
return total_cost
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(portfolio_cost('Data/portfolio.dat'))
|
||||
```
|
||||
|
||||
|
||||
[Back](ex1_6.md)
|
||||
88
Exercises/soln2_1.md
Normal file
88
Exercises/soln2_1.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Exercise 2.1 - Solution
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
|
||||
import csv
|
||||
|
||||
def read_rides_as_tuples(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of tuples
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = (route, date, daytype, rides)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def read_rides_as_dicts(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of dicts
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = {
|
||||
'route': route,
|
||||
'date': date,
|
||||
'daytype': daytype,
|
||||
'rides' : rides
|
||||
}
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
class Row:
|
||||
# Uncomment to see effect of slots
|
||||
# __slots__ = ('route', 'date', 'daytype', 'rides')
|
||||
def __init__(self, route, date, daytype, rides):
|
||||
self.route = route
|
||||
self.date = date
|
||||
self.daytype = daytype
|
||||
self.rides = rides
|
||||
|
||||
# Uncomment to use a namedtuple instead
|
||||
#from collections import namedtuple
|
||||
#Row = namedtuple('Row',('route','date','daytype','rides'))
|
||||
|
||||
def read_rides_as_instances(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of instances
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = Row(route, date, daytype, rides)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
if __name__ == '__main__':
|
||||
import tracemalloc
|
||||
tracemalloc.start()
|
||||
read_rides = read_rides_as_tuples # Change to as_dicts, as_instances, etc.
|
||||
rides = read_rides("Data/ctabus.csv")
|
||||
|
||||
print('Memory Use: Current %d, Peak %d' % tracemalloc.get_traced_memory())
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex2_1.md)
|
||||
23
Exercises/soln2_2.md
Normal file
23
Exercises/soln2_2.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Exercise 2.2 - Solution
|
||||
|
||||
There is no official solution for this exercise--you need rely on your
|
||||
current Python knowledge. However, there are a few tips that can
|
||||
help.
|
||||
|
||||
- For problems where you need to determine uniqueness, use a set. Sets aren't allowed to
|
||||
have duplicates.
|
||||
|
||||
- If you need to tabulate data, consider using a dictionary that maps keys to a numeric
|
||||
count. For example, to count rides on each bus route, you could make a dictionary that
|
||||
maps the route name to the total ride count for that route. A `Counter` object from
|
||||
`collections` is perfect for this task.
|
||||
|
||||
- If you need to keep data in order or sort data, store it in a list.
|
||||
|
||||
- You can make Python data structures out of almost anything. For
|
||||
example, dictionaries of sets, nested dictionaries, etc. You might
|
||||
need to do this to answer questions 3 and 4.
|
||||
|
||||
|
||||
|
||||
[Back](ex2_2.md)
|
||||
7
Exercises/soln2_3.md
Normal file
7
Exercises/soln2_3.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Exercise 2.3 - Solution
|
||||
|
||||
Work through the exercise. Code is given in the description.
|
||||
|
||||
|
||||
|
||||
[Back](ex2_3.md)
|
||||
78
Exercises/soln2_4.md
Normal file
78
Exercises/soln2_4.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Exercise 2.4 - Solution
|
||||
|
||||
```python
|
||||
# mutint.py
|
||||
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
class MutInt:
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
def __repr__(self):
|
||||
return f'MutInt({self.value!r})'
|
||||
|
||||
def __format__(self, fmt):
|
||||
return format(self.value, fmt)
|
||||
|
||||
# Implement the "+" operator. Forward operands (MutInt + other)
|
||||
def __add__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return MutInt(self.value + other.value)
|
||||
elif isinstance(other, int):
|
||||
return MutInt(self.value + other)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
# Support for reversed operands (other + MutInt)
|
||||
__radd__ = __add__
|
||||
|
||||
# Support for in-place update (MutInt += other)
|
||||
def __iadd__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
self.value += other.value
|
||||
return self
|
||||
elif isinstance(other, int):
|
||||
self.value += other
|
||||
return self
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
# Support for equality testing
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return self.value == other.value
|
||||
elif isinstance(other, int):
|
||||
return self.value == other
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
# One relation is needed for @total_ordering decorator. It fills in others
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, MutInt):
|
||||
return self.value < other.value
|
||||
elif isinstance(other, int):
|
||||
return self.value < other
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
# Conversions to int() and float()
|
||||
def __int__(self):
|
||||
return int(self.value)
|
||||
|
||||
def __float__(self):
|
||||
return float(self.value)
|
||||
|
||||
# Support for indexing s[MutInt]
|
||||
__index__ = __int__
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex2_4.md)
|
||||
131
Exercises/soln2_5.md
Normal file
131
Exercises/soln2_5.md
Normal file
@ -0,0 +1,131 @@
|
||||
# Exercise 2.5 - Solution
|
||||
|
||||
Here is a version of `readrides.py` with changes for parts (c) and (d).
|
||||
|
||||
```python
|
||||
# readrides.py
|
||||
|
||||
import csv
|
||||
|
||||
def read_rides_as_tuples(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of tuples
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = (route, date, daytype, rides)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def read_rides_as_dicts(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of dicts
|
||||
'''
|
||||
records = RideData() # <---- CHANGED
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = {
|
||||
'route': route,
|
||||
'date': date,
|
||||
'daytype': daytype,
|
||||
'rides' : rides
|
||||
}
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
class Row:
|
||||
__slots__ = ('route', 'date', 'daytype', 'rides')
|
||||
def __init__(self, route, date, daytype, rides):
|
||||
self.route = route
|
||||
self.date = date
|
||||
self.daytype = daytype
|
||||
self.rides = rides
|
||||
|
||||
def read_rides_as_instances(filename):
|
||||
'''
|
||||
Read the bus ride data as a list of instances
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
route = row[0]
|
||||
date = row[1]
|
||||
daytype = row[2]
|
||||
rides = int(row[3])
|
||||
record = Row(route, date, daytype, rides)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
# Read as columns
|
||||
def read_rides_as_columns(filename):
|
||||
'''
|
||||
Read the bus ride data into 4 lists, representing columns
|
||||
'''
|
||||
routes = []
|
||||
dates = []
|
||||
daytypes = []
|
||||
numrides = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headings = next(rows) # Skip headers
|
||||
for row in rows:
|
||||
routes.append(row[0])
|
||||
dates.append(row[1])
|
||||
daytypes.append(row[2])
|
||||
numrides.append(int(row[3]))
|
||||
return dict(routes=routes, dates=dates, daytypes=daytypes, numrides=numrides)
|
||||
|
||||
# The great "fake"
|
||||
|
||||
import collections
|
||||
class RideData(collections.Sequence):
|
||||
def __init__(self):
|
||||
# Each value is a list with all of the values (a column)
|
||||
self.routes = []
|
||||
self.dates = []
|
||||
self.daytypes = []
|
||||
self.numrides = []
|
||||
|
||||
def __len__(self):
|
||||
# All lists assumed to have the same length
|
||||
return len(self.routes)
|
||||
|
||||
def append(self, d):
|
||||
self.routes.append(d['route'])
|
||||
self.dates.append(d['date'])
|
||||
self.daytypes.append(d['daytype'])
|
||||
self.numrides.append(d['rides'])
|
||||
|
||||
def __getitem__(self, index):
|
||||
return { 'route': self.routes[index],
|
||||
'date': self.dates[index],
|
||||
'daytype': self.daytypes[index],
|
||||
'rides': self.numrides[index] }
|
||||
|
||||
if __name__ == '__main__':
|
||||
import tracemalloc
|
||||
tracemalloc.start()
|
||||
read_rides = read_rides_as_dicts # Change to as_dicts, as_instances, etc.
|
||||
rides = read_rides("Data/ctabus.csv")
|
||||
|
||||
print('Memory Use: Current %d, Peak %d' % tracemalloc.get_traced_memory())
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex2_5.md)
|
||||
25
Exercises/soln2_6.md
Normal file
25
Exercises/soln2_6.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Exercise 2.6 - Solution
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
|
||||
def read_csv_as_dicts(filename, types):
|
||||
'''
|
||||
Read a CSV file with column type conversion
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = { name: func(val) for name, func, val in zip(headers, types, row) }
|
||||
records.append(record)
|
||||
return records
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex2_6.md)
|
||||
48
Exercises/soln3_1.md
Normal file
48
Exercises/soln3_1.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Exercise 3.1 - Solution
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
|
||||
def read_portfolio(filename):
|
||||
'''
|
||||
Read a CSV file of stock data into a list of Stocks
|
||||
'''
|
||||
import csv
|
||||
portfolio = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = Stock(row[0], int(row[1]), float(row[2]))
|
||||
portfolio.append(record)
|
||||
return portfolio
|
||||
|
||||
def print_portfolio(portfolio):
|
||||
'''
|
||||
Make a nicely formatted table showing stock data
|
||||
'''
|
||||
print('%10s %10s %10s' % ('name', 'shares', 'price'))
|
||||
print(('-'*10 + ' ')*3)
|
||||
for s in portfolio:
|
||||
print('%10s %10d %10.2f' % (s.name, s.shares, s.price))
|
||||
|
||||
if __name__ == '__main__':
|
||||
portfolio = read_portfolio('Data/portfolio.csv')
|
||||
print_portfolio(portfolio)
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex3_1.md)
|
||||
18
Exercises/soln3_2.md
Normal file
18
Exercises/soln3_2.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Exercise 3.2 - Solution
|
||||
|
||||
(c) Table Formatter
|
||||
|
||||
```python
|
||||
# tableformat.py
|
||||
|
||||
# Print a table
|
||||
def print_table(records, fields):
|
||||
print(' '.join('%10s' % fieldname for fieldname in fields))
|
||||
print(('-'*10 + ' ')*len(fields))
|
||||
for record in records:
|
||||
print(' '.join('%10s' % getattr(record, fieldname) for fieldname in fields))
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex3_2.md)
|
||||
84
Exercises/soln3_3.md
Normal file
84
Exercises/soln3_3.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Exercise 3.3 - Solution
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
types = (str, int, float)
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls.types, row)]
|
||||
return cls(*values)
|
||||
|
||||
def read_portfolio(filename):
|
||||
'''
|
||||
Read a CSV file of stock data into a list of Stocks
|
||||
'''
|
||||
import csv
|
||||
portfolio = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = Stock.from_row(row)
|
||||
portfolio.append(record)
|
||||
return portfolio
|
||||
|
||||
if __name__ == '__main__':
|
||||
import tableformat
|
||||
import reader
|
||||
portfolio = read_portfolio('Data/portfolio.csv')
|
||||
|
||||
# Generalized version
|
||||
# portfolio = reader.read_csv_as_instances('Data/portfolio.csv', Stock)
|
||||
tableformat.print_table(portfolio, ['name', 'shares', 'price'])
|
||||
```
|
||||
|
||||
The `reader.py` file should now look like this:
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
|
||||
def read_csv_as_dicts(filename, types):
|
||||
'''
|
||||
Read a CSV file into a list of dicts with column type conversion
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = { name: func(val) for name, func, val in zip(headers, types, row) }
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def read_csv_as_instances(filename, cls):
|
||||
'''
|
||||
Read a CSV file into a list of instances
|
||||
'''
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
records.append(cls.from_row(row))
|
||||
return records
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex3_3.md)
|
||||
199
Exercises/soln3_4.md
Normal file
199
Exercises/soln3_4.md
Normal file
@ -0,0 +1,199 @@
|
||||
# Exercise 3.4 - Solution
|
||||
|
||||
## (a) Private attributes
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
_types = (str, int, float)
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls._types, row)]
|
||||
return cls(*values)
|
||||
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
|
||||
## (b) Computed Attributes
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
_types = (str, int, float)
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls._types, row)]
|
||||
return cls(*values)
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
## (c) Enforcing Type-Checking Rules
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
_types = (str, int, float)
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls._types, row)]
|
||||
return cls(*values)
|
||||
|
||||
@property
|
||||
def shares(self):
|
||||
return self._shares
|
||||
@shares.setter
|
||||
def shares(self, value):
|
||||
if not isinstance(value, int):
|
||||
raise TypeError('Expected an integer')
|
||||
if value < 0:
|
||||
raise ValueError('shares must be >= 0')
|
||||
self._shares = value
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
return self._price
|
||||
@price.setter
|
||||
def price(self, value):
|
||||
if not isinstance(value, float):
|
||||
raise TypeError('Expected a float')
|
||||
if value < 0:
|
||||
raise ValueError('price must be >= 0')
|
||||
self._price = value
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
## (d) Adding `__slots__`
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
__slots__ = ('name','_shares','_price')
|
||||
_types = (str, int, float)
|
||||
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls._types, row)]
|
||||
return cls(*values)
|
||||
|
||||
@property
|
||||
def shares(self):
|
||||
return self._shares
|
||||
@shares.setter
|
||||
def shares(self, value):
|
||||
if not isinstance(value, int):
|
||||
raise TypeError('Expected integer')
|
||||
if value < 0:
|
||||
raise ValueError('shares must be >= 0')
|
||||
self._shares = value
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
return self._price
|
||||
@price.setter
|
||||
def price(self, value):
|
||||
if not isinstance(value, float):
|
||||
raise TypeError('Expected float')
|
||||
if value < 0:
|
||||
raise ValueError('price must be >= 0')
|
||||
self._price = value
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
## (e) Reconciling types
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
__slots__ = ('name','_shares','_price')
|
||||
_types = (str, int, float)
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row):
|
||||
values = [func(val) for func, val in zip(cls._types, row)]
|
||||
return cls(*values)
|
||||
|
||||
@property
|
||||
def shares(self):
|
||||
return self._shares
|
||||
@shares.setter
|
||||
def shares(self, value):
|
||||
if not isinstance(value, self._types[1]):
|
||||
raise TypeError(f'Expected {self._types[1].__name__}')
|
||||
if value < 0:
|
||||
raise ValueError('shares must be >= 0')
|
||||
self._shares = value
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
return self._price
|
||||
@price.setter
|
||||
def price(self, value):
|
||||
if not isinstance(value, self._types[2]):
|
||||
raise TypeError(f'Expected {self._types[2].__name__}')
|
||||
if value < 0:
|
||||
raise ValueError('price must be >= 0')
|
||||
self._price = value
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex3_4.md)
|
||||
61
Exercises/soln3_5.md
Normal file
61
Exercises/soln3_5.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Exercise 3.5 - Solution
|
||||
|
||||
```python
|
||||
# tableformat.py
|
||||
|
||||
def print_table(records, fields, formatter):
|
||||
formatter.headings(fields)
|
||||
for r in records:
|
||||
rowdata = [getattr(r, fieldname) for fieldname in fields]
|
||||
formatter.row(rowdata)
|
||||
|
||||
class TableFormatter:
|
||||
def headings(self, headers):
|
||||
raise NotImplementedError()
|
||||
|
||||
def row(self, rowdata):
|
||||
raise NotImplementedError()
|
||||
|
||||
class TextTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print(' '.join('%10s' % h for h in headers))
|
||||
print(('-'*10 + ' ')*len(headers))
|
||||
|
||||
def row(self, rowdata):
|
||||
print(' '.join('%10s' % d for d in rowdata))
|
||||
|
||||
class CSVTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print(','.join(headers))
|
||||
|
||||
def row(self, rowdata):
|
||||
print(','.join(str(d) for d in rowdata))
|
||||
|
||||
class HTMLTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print('<tr>', end=' ')
|
||||
for h in headers:
|
||||
print('<th>%s</th>' % h, end=' ')
|
||||
print('</tr>')
|
||||
|
||||
def row(self, rowdata):
|
||||
print('<tr>', end=' ')
|
||||
for d in rowdata:
|
||||
print('<td>%s</td>' % d, end=' ')
|
||||
print('</tr>')
|
||||
|
||||
def create_formatter(name):
|
||||
if name == 'text':
|
||||
formatter_cls = TextTableFormatter
|
||||
elif name == 'csv':
|
||||
formatter_cls = CSVTableFormatter
|
||||
elif name == 'html':
|
||||
formatter_cls = HTMLTableFormatter
|
||||
else:
|
||||
raise RuntimeError('Unknown format %s' % name)
|
||||
return formatter_cls()
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex3_5.md)
|
||||
34
Exercises/soln3_6.md
Normal file
34
Exercises/soln3_6.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Exercise 3.6 - Solution
|
||||
|
||||
## (a) Better output for printing objects
|
||||
|
||||
```python
|
||||
# stock.py
|
||||
|
||||
class Stock:
|
||||
...
|
||||
|
||||
def __repr__(self):
|
||||
# Note: The !r format code produces the repr() string
|
||||
return f'{type(self).__name__}({self.name!r}, {self.shares!r}, {self.price!r})'
|
||||
...
|
||||
```
|
||||
|
||||
## (b) Making Objects Comparable
|
||||
|
||||
```python
|
||||
class Stock:
|
||||
...
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Stock) and ((self.name, self.shares, self.price) ==
|
||||
(other.name, other.shares, other.price))
|
||||
...
|
||||
```
|
||||
|
||||
## (c) Context Managers
|
||||
|
||||
Code is given in the exercise.
|
||||
|
||||
|
||||
|
||||
[Back](ex3_6.md)
|
||||
85
Exercises/soln3_7.md
Normal file
85
Exercises/soln3_7.md
Normal file
@ -0,0 +1,85 @@
|
||||
|
||||
# Exercise 3.7 - Solution
|
||||
|
||||
## (a) Interfaces
|
||||
|
||||
```python
|
||||
# tableformat.py
|
||||
|
||||
def print_table(records, fields, formatter):
|
||||
if not isinstance(formatter, TableFormatter):
|
||||
raise TypeError('Expected a TableFormatter')
|
||||
|
||||
formatter.headings(fields)
|
||||
for r in records:
|
||||
rowdata = [getattr(r, fieldname) for fieldname in fields]
|
||||
formatter.row(rowdata)
|
||||
...
|
||||
```
|
||||
|
||||
## (b) Abstract Base Classes
|
||||
|
||||
```python
|
||||
# tableformat.py
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class TableFormatter(ABC):
|
||||
@abstractmethod
|
||||
def headings(self, headers):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def row(self, rowdata):
|
||||
pass
|
||||
```
|
||||
|
||||
## (c) Algorithm Template Classes
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class CSVParser(ABC):
|
||||
def parse(self, filename):
|
||||
records = []
|
||||
with open(filename) as f:
|
||||
rows = csv.reader(f)
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = self.make_record(headers, row)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
@abstractmethod
|
||||
def make_record(self, headers, row):
|
||||
pass
|
||||
|
||||
class DictCSVParser(CSVParser):
|
||||
def __init__(self, types):
|
||||
self.types = types
|
||||
|
||||
def make_record(self, headers, row):
|
||||
return { name: func(val) for name, func, val in zip(headers, self.types, row) }
|
||||
|
||||
class InstanceCSVParser(CSVParser):
|
||||
def __init__(self, cls):
|
||||
self.cls = cls
|
||||
|
||||
def make_record(self, headers, row):
|
||||
return self.cls.from_row(row)
|
||||
|
||||
def read_csv_as_dicts(filename, types):
|
||||
parser = DictCSVParser(types)
|
||||
return parser.parse(filename)
|
||||
|
||||
def read_csv_as_instances(filename, cls):
|
||||
parser = InstanceCSVParser(cls)
|
||||
return parser.parse(filename)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[Back](ex3_7.md)
|
||||
88
Exercises/soln3_8.md
Normal file
88
Exercises/soln3_8.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Exercise 3.8 - Solution
|
||||
|
||||
```python
|
||||
# tableformat.py
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
def print_table(records, fields, formatter):
|
||||
if not isinstance(formatter, TableFormatter):
|
||||
raise RuntimeError('Expected a TableFormatter')
|
||||
|
||||
formatter.headings(fields)
|
||||
for r in records:
|
||||
rowdata = [getattr(r, fieldname) for fieldname in fields]
|
||||
formatter.row(rowdata)
|
||||
|
||||
class TableFormatter(ABC):
|
||||
@abstractmethod
|
||||
def headings(self, headers):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def row(self, rowdata):
|
||||
pass
|
||||
|
||||
|
||||
class TextTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print(' '.join('%10s' % h for h in headers))
|
||||
print(('-'*10 + ' ')*len(headers))
|
||||
|
||||
def row(self, rowdata):
|
||||
print(' '.join('%10s' % d for d in rowdata))
|
||||
|
||||
class CSVTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print(','.join(headers))
|
||||
|
||||
def row(self, rowdata):
|
||||
print(','.join(str(d) for d in rowdata))
|
||||
|
||||
class HTMLTableFormatter(TableFormatter):
|
||||
def headings(self, headers):
|
||||
print('<tr>', end=' ')
|
||||
for h in headers:
|
||||
print('<th>%s</th>' % h, end=' ')
|
||||
print('</tr>')
|
||||
|
||||
def row(self, rowdata):
|
||||
print('<tr>', end=' ')
|
||||
for d in rowdata:
|
||||
print('<td>%s</td>' % d, end=' ')
|
||||
print('</tr>')
|
||||
|
||||
|
||||
class ColumnFormatMixin:
|
||||
formats = []
|
||||
def row(self, rowdata):
|
||||
rowdata = [ (fmt % item) for fmt, item in zip(self.formats, rowdata)]
|
||||
super().row(rowdata)
|
||||
|
||||
class UpperHeadersMixin:
|
||||
def headings(self, headers):
|
||||
super().headings([h.upper() for h in headers])
|
||||
|
||||
def create_formatter(name, column_formats=None, upper_headers=False):
|
||||
if name == 'text':
|
||||
formatter_cls = TextTableFormatter
|
||||
elif name == 'csv':
|
||||
formatter_cls = CSVTableFormatter
|
||||
elif name == 'html':
|
||||
formatter_cls = HTMLTableFormatter
|
||||
else:
|
||||
raise RuntimeError('Unknown format %s' % name)
|
||||
|
||||
if column_formats:
|
||||
class formatter_cls(ColumnFormatMixin, formatter_cls):
|
||||
formats = column_formats
|
||||
|
||||
if upper_headers:
|
||||
class formatter_cls(UpperHeadersMixin, formatter_cls):
|
||||
pass
|
||||
|
||||
return formatter_cls()
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex3_8.md)
|
||||
7
Exercises/soln4_1.md
Normal file
7
Exercises/soln4_1.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Exercise 4.1 - Solution
|
||||
|
||||
No solution. Just follow along.
|
||||
|
||||
|
||||
|
||||
[Back](ex4_1.md)
|
||||
93
Exercises/soln4_2.md
Normal file
93
Exercises/soln4_2.md
Normal file
@ -0,0 +1,93 @@
|
||||
# Exercise 4.2 - Solution
|
||||
|
||||
Here is the validator code in its entirety:
|
||||
|
||||
```python
|
||||
class Validator:
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
return value
|
||||
|
||||
class Typed(Validator):
|
||||
expected_type = object
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if not isinstance(value, cls.expected_type):
|
||||
raise TypeError(f'Expected {cls.expected_type}')
|
||||
return super().check(value)
|
||||
|
||||
class Integer(Typed):
|
||||
expected_type = int
|
||||
|
||||
class Float(Typed):
|
||||
expected_type = float
|
||||
|
||||
class String(Typed):
|
||||
expected_type = str
|
||||
|
||||
class Positive(Validator):
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if value < 0:
|
||||
raise ValueError('Must be >= 0')
|
||||
return super().check(value)
|
||||
|
||||
class NonEmpty(Validator):
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if len(value) == 0:
|
||||
raise ValueError('Must be non-empty')
|
||||
return super().check(value)
|
||||
|
||||
class PositiveInteger(Integer, Positive):
|
||||
pass
|
||||
|
||||
class PositiveFloat(Float, Positive):
|
||||
pass
|
||||
|
||||
class NonEmptyString(String, NonEmpty):
|
||||
pass
|
||||
```
|
||||
|
||||
## (c) Using the validators
|
||||
|
||||
```python
|
||||
# validate.py
|
||||
...
|
||||
|
||||
if __name__ == '__main__':
|
||||
class Stock:
|
||||
__slots__ = ('name', '_shares', '_price')
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
def __repr__(self):
|
||||
return f'Stock({self.name!r}, {self.shares!r}, {self.price!r})'
|
||||
|
||||
@property
|
||||
def shares(self):
|
||||
return self._shares
|
||||
@shares.setter
|
||||
def shares(self, value):
|
||||
self._shares = PositiveInteger.check(value)
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
return self._price
|
||||
@price.setter
|
||||
def price(self, value):
|
||||
self._price = PositiveFloat.check(value)
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex4_2.md)
|
||||
87
Exercises/soln4_3.md
Normal file
87
Exercises/soln4_3.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Exercise 4.3 - Solution
|
||||
|
||||
```python
|
||||
class Validator:
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
|
||||
def __set_name__(self, cls, name):
|
||||
self.name = name
|
||||
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
return value
|
||||
|
||||
def __set__(self, instance, value):
|
||||
instance.__dict__[self.name] = self.check(value)
|
||||
|
||||
class Typed(Validator):
|
||||
expected_type = object
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if not isinstance(value, cls.expected_type):
|
||||
raise TypeError(f'expected {cls.expected_type}')
|
||||
return super().check(value)
|
||||
|
||||
class Integer(Typed):
|
||||
expected_type = int
|
||||
|
||||
class Float(Typed):
|
||||
expected_type = float
|
||||
|
||||
class String(Typed):
|
||||
expected_type = str
|
||||
|
||||
class Positive(Validator):
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if value < 0:
|
||||
raise ValueError('must be >= 0')
|
||||
return super().check(value)
|
||||
|
||||
class NonEmpty(Validator):
|
||||
@classmethod
|
||||
def check(cls, value):
|
||||
if len(value) == 0:
|
||||
raise ValueError('must be non-empty')
|
||||
return super().check(value)
|
||||
|
||||
class PositiveInteger(Integer, Positive):
|
||||
pass
|
||||
|
||||
class PositiveFloat(Float, Positive):
|
||||
pass
|
||||
|
||||
class NonEmptyString(String, NonEmpty):
|
||||
pass
|
||||
|
||||
# Examples
|
||||
if __name__ == '__main__':
|
||||
def add(x, y):
|
||||
Integer.check(x)
|
||||
Integer.check(y)
|
||||
return x + y
|
||||
|
||||
class Stock:
|
||||
name = NonEmptyString()
|
||||
shares = PositiveInteger()
|
||||
price = PositiveFloat()
|
||||
def __init__(self,name,shares,price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
|
||||
def __repr__(self):
|
||||
return f'Stock({self.name!r}, {self.shares!r}, {self.price!r})'
|
||||
|
||||
@property
|
||||
def cost(self):
|
||||
return self.shares * self.price
|
||||
|
||||
def sell(self, nshares):
|
||||
self.shares -= nshares
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex4_3.md)
|
||||
7
Exercises/soln4_4.md
Normal file
7
Exercises/soln4_4.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Exercise 4.4 - Solution
|
||||
|
||||
Code is given in the exercise.
|
||||
|
||||
|
||||
|
||||
[Back](ex4_4.md)
|
||||
55
Exercises/soln5_1.md
Normal file
55
Exercises/soln5_1.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Exercise 5.1 - Solution
|
||||
|
||||
This is a partial solution that does not include type-hints.
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
|
||||
def csv_as_dicts(lines, types, *, headers=None):
|
||||
'''
|
||||
Convert lines of CSV data into a list of dictionaries
|
||||
'''
|
||||
records = []
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = { name: func(val)
|
||||
for name, func, val in zip(headers, types, row) }
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def csv_as_instances(lines, cls, *, headers=None):
|
||||
'''
|
||||
Convert lines of CSV data into a list of instances
|
||||
'''
|
||||
records = []
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = cls.from_row(row)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def read_csv_as_dicts(filename, types, *, headers=None):
|
||||
'''
|
||||
Read CSV data into a list of dictionaries with optional type conversion
|
||||
'''
|
||||
with open(filename) as file:
|
||||
return csv_as_dicts(file, types, headers=headers)
|
||||
|
||||
def read_csv_as_instances(filename, cls, *, headers=None):
|
||||
'''
|
||||
Read CSV data into a list of instances
|
||||
'''
|
||||
with open(filename) as file:
|
||||
return csv_as_instances(file, cls, headers=headers)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[Back](ex5_1.md)
|
||||
7
Exercises/soln5_2.md
Normal file
7
Exercises/soln5_2.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Exercise 5.2 - Solution
|
||||
|
||||
None. Just work through the exercise.
|
||||
|
||||
|
||||
|
||||
[Back](ex5_2.md)
|
||||
60
Exercises/soln5_3.md
Normal file
60
Exercises/soln5_3.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Exercise 5.3 - Solution
|
||||
|
||||
## (a) Higher order functions
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
|
||||
def convert_csv(lines, converter, *, headers=None):
|
||||
records = []
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
for row in rows:
|
||||
record = converter(headers, row)
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
def csv_as_dicts(lines, types, *, headers=None):
|
||||
return convert_csv(lines,
|
||||
lambda headers, row: { name: func(val) for name, func, val in zip(headers, types, row) })
|
||||
|
||||
def csv_as_instances(lines, cls, *, headers=None):
|
||||
return convert_csv(lines,
|
||||
lambda headers, row: cls.from_row(row))
|
||||
|
||||
def read_csv_as_dicts(filename, types, *, headers=None):
|
||||
'''
|
||||
Read CSV data into a list of dictionaries with optional type conversion
|
||||
'''
|
||||
with open(filename) as file:
|
||||
return csv_as_dicts(file, types, headers=headers)
|
||||
|
||||
def read_csv_as_instances(filename, cls, *, headers=None):
|
||||
'''
|
||||
Read CSV data into a list of instances
|
||||
'''
|
||||
with open(filename) as file:
|
||||
return csv_as_instances(file, cls, headers=headers)
|
||||
```
|
||||
|
||||
## (b) Using map()
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
|
||||
def convert_csv(lines, converter, *, headers=None):
|
||||
records = []
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
return map(lambda row: converter(headers, row), rows)
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex5_3.md)
|
||||
39
Exercises/soln5_4.md
Normal file
39
Exercises/soln5_4.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Exercise 5.4 - Solution
|
||||
|
||||
```python
|
||||
# typedproperty.py
|
||||
|
||||
def typedproperty(name, expected_type):
|
||||
private_name = '_' + name
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return getattr(self, private_name)
|
||||
|
||||
@value.setter
|
||||
def value(self, val):
|
||||
if not isinstance(val, expected_type):
|
||||
raise TypeError(f'Expected {expected_type}')
|
||||
setattr(self, private_name, val)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
String = lambda name: typedproperty(name, str)
|
||||
Integer = lambda name: typedproperty(name, int)
|
||||
Float = lambda name: typedproperty(name, float)
|
||||
|
||||
# Example
|
||||
if __name__ == '__main__':
|
||||
class Stock:
|
||||
name = String('name')
|
||||
shares = Integer('shares')
|
||||
price = Float('price')
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
|
||||
[Back](ex5_4.md)
|
||||
50
Exercises/soln5_5.md
Normal file
50
Exercises/soln5_5.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Exercise 5.5 - Solution
|
||||
|
||||
```python
|
||||
# reader.py
|
||||
|
||||
import csv
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def convert_csv(lines, converter, *, headers=None):
|
||||
rows = csv.reader(lines)
|
||||
if headers is None:
|
||||
headers = next(rows)
|
||||
|
||||
records = []
|
||||
for rowno, row in enumerate(rows, start=1):
|
||||
try:
|
||||
records.append(converter(headers, row))
|
||||
except ValueError as e:
|
||||
log.warning('Row %s: Bad row: %s', rowno, row)
|
||||
log.debug('Row %s: Reason: %s', rowno, row)
|
||||
return records
|
||||
|
||||
def csv_as_dicts(lines, types, *, headers=None):
|
||||
return convert_csv(lines,
|
||||
lambda headers, row: { name: func(val) for name, func, val in zip(headers, types, row) })
|
||||
|
||||
def csv_as_instances(lines, cls, *, headers=None):
|
||||
return convert_csv(lines,
|
||||
lambda headers, row: cls.from_row(row))
|
||||
|
||||
def read_csv_as_dicts(filename, types, *, headers=None):
|
||||
'''
|
||||
Read CSV data into a list of dictionaries with optional type conversion
|
||||
'''
|
||||
with open(filename) as file:
|
||||
return csv_as_dicts(file, types, headers=headers)
|
||||
|
||||
def read_csv_as_instances(filename, cls, *, headers=None):
|
||||
'''
|
||||
Read CSV data into a list of instances
|
||||
'''
|
||||
with open(filename) as file:
|
||||
return csv_as_instances(file, cls, headers=headers)
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex5_5.md)
|
||||
129
Exercises/soln5_6.md
Normal file
129
Exercises/soln5_6.md
Normal file
@ -0,0 +1,129 @@
|
||||
# Exercise 5.6 - Solution
|
||||
|
||||
## (b) Unit testing
|
||||
|
||||
```python
|
||||
# teststock.py
|
||||
|
||||
import stock
|
||||
import unittest
|
||||
|
||||
class TestStock(unittest.TestCase):
|
||||
def test_create(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
def test_create_keyword(self):
|
||||
s = stock.Stock(name='GOOG', shares=100, price=490.1)
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
def test_cost(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(s.cost, 49010.0)
|
||||
|
||||
def test_sell(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
s.sell(25)
|
||||
self.assertEqual(s.shares, 75)
|
||||
|
||||
def test_from_row(self):
|
||||
s = stock.Stock.from_row(['GOOG','100','490.1'])
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
def test_repr(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(repr(s), "Stock('GOOG', 100, 490.1)")
|
||||
|
||||
def test_eq(self):
|
||||
a = stock.Stock('GOOG', 100, 490.1)
|
||||
b = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertTrue(a==b)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
```
|
||||
|
||||
## (c) Testing for Errors
|
||||
|
||||
```python
|
||||
# teststock.py
|
||||
|
||||
import stock
|
||||
import unittest
|
||||
|
||||
class TestStock(unittest.TestCase):
|
||||
def test_create(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
def test_create_keyword(self):
|
||||
s = stock.Stock(name='GOOG', shares=100, price=490.1)
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
def test_cost(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(s.cost, 49010.0)
|
||||
|
||||
def test_sell(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
s.sell(25)
|
||||
self.assertEqual(s.shares, 75)
|
||||
|
||||
def test_from_row(self):
|
||||
s = stock.Stock.from_row(['GOOG','100','490.1'])
|
||||
self.assertEqual(s.name, 'GOOG')
|
||||
self.assertEqual(s.shares, 100)
|
||||
self.assertEqual(s.price, 490.1)
|
||||
|
||||
def test_repr(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertEqual(repr(s), "Stock('GOOG', 100, 490.1)")
|
||||
|
||||
def test_eq(self):
|
||||
a = stock.Stock('GOOG', 100, 490.1)
|
||||
b = stock.Stock('GOOG', 100, 490.1)
|
||||
self.assertTrue(a==b)
|
||||
|
||||
# Tests for failure conditions
|
||||
def test_shares_badtype(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
with self.assertRaises(TypeError):
|
||||
s.shares = '50'
|
||||
|
||||
def test_shares_badvalue(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
with self.assertRaises(ValueError):
|
||||
s.shares = -50
|
||||
|
||||
def test_price_badtype(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
with self.assertRaises(TypeError):
|
||||
s.price = '45.23'
|
||||
|
||||
def test_price_badvalue(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
with self.assertRaises(ValueError):
|
||||
s.price = -45.23
|
||||
|
||||
def test_bad_attribute(self):
|
||||
s = stock.Stock('GOOG', 100, 490.1)
|
||||
with self.assertRaises(AttributeError):
|
||||
s.share = 100
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex5_6.md)
|
||||
43
Exercises/soln6_1.md
Normal file
43
Exercises/soln6_1.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Exercise 6.1 - Solution
|
||||
|
||||
## (a) Simplified Structures
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
|
||||
class Structure:
|
||||
_fields = ()
|
||||
def __init__(self, *args):
|
||||
if len(args) != len(self._fields):
|
||||
raise TypeError('Expected %d arguments' % len(self._fields))
|
||||
for name, arg in zip(self._fields,args):
|
||||
setattr(self, name, arg)
|
||||
```
|
||||
|
||||
## (b) Making a Useful Representation
|
||||
|
||||
[source, python]
|
||||
```
|
||||
class Structure:
|
||||
...
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (type(self).__name__,
|
||||
', '.join(repr(getattr(self, name)) for name in self._fields))
|
||||
```
|
||||
|
||||
## (c) Restricting Attribute Names
|
||||
|
||||
[source, python]
|
||||
```
|
||||
class Structure:
|
||||
...
|
||||
def __setattr__(self, name, value):
|
||||
if name.startswith('_') or name in self._fields:
|
||||
super().__setattr__(name, value)
|
||||
else:
|
||||
raise AttributeError('No attribute %s' % name)
|
||||
```
|
||||
|
||||
|
||||
|
||||
[Back](ex6_1.md)
|
||||
32
Exercises/soln6_2.md
Normal file
32
Exercises/soln6_2.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Exercise 6.2 - Solution
|
||||
|
||||
```python
|
||||
# structure.py
|
||||
|
||||
import sys
|
||||
|
||||
class Structure:
|
||||
_fields = ()
|
||||
|
||||
@staticmethod
|
||||
def _init():
|
||||
locs = sys._getframe(1).f_locals
|
||||
self = locs.pop('self')
|
||||
for name, val in locs.items():
|
||||
setattr(self, name, val)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name.startswith('_') or name in self._fields:
|
||||
super().__setattr__(name, value)
|
||||
else:
|
||||
raise AttributeError('No attribute %s' % name)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (type(self).__name__,
|
||||
', '.join(repr(getattr(self, name)) for name in self._fields))
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[Back](ex6_2.md)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user