Copy on write using weakrefs
Working model of copy-on-write. Aims to close #10954, alternative to #10973, extension of #11207.
## Copy-on-Write Behavior:

**Setting on child doesn't affect parent, but still uses views when can for efficiency**

```
parent = pd.DataFrame({'col1':[1,2], 'col2':[3,4]})
child = parent.loc[0:0,]
child._is_view

Out[1]: True

child.loc[0:0] = -88
child

Out[2]: 
   col1  col2
0   -88   -88

parent

Out[3]: 
   col1  col2
0     1     3
1     2     4
```

**Setting on parent doesn't affect child**

```
parent = pd.DataFrame({'col1':[1,2], 'col2':[3,4]})
child = parent.loc[0:0,]
child._is_view

Out[4]: True

parent.loc[0:0, 'col1'] = -88
child

Out[5]: 
   col1  col2
0     1     3

parent

Out[6]: 
   col1  col2
0   -88     3
1     2     4
```

**One exception is dictionary-like access, where views are preserved**
_(as suggested here: https://github.com/pydata/pandas/issues/10954#issuecomment-136521398 )_

```
parent = pd.DataFrame({'col1':[1,2], 'col2':[3,4]})
child = parent['col1']
child._is_view

Out[7]: True

parent.loc[0:0, 'col1'] = -88
child

Out[8]: 
0   -88
1     2
Name: col1, dtype: int64

parent

Out[9]: 
   col1  col2
0   -88     3
1     2     4
```

**Safe for views of views**

```
parent = pd.DataFrame({'col1':[1,2], 'col2':[3,4]})
child = parent.loc[0:0,]
child._is_view
Out[20]: True

child_of_child = child.loc[0:0,'col1':'col1']
child_of_child._is_view

Out[21]: True

child_of_child.loc[0:0, 'col1'] = -88
child_of_child

Out[22]: 
   col1
0   -88

child

Out[23]: 
   col1  col2
0     1     3

parent

Out[24]: 
   col1  col2
0     1     3
1     2     4
```
## Chained indexing behavior now consistent

**Will always fail unless first class is a dictionary-like call for a single series (since that will always be a view)**

**Will fail if first call not dict-like**

```
parent = pd.DataFrame({'col1':[1,2], 'col2':[3,4]})
parent.loc[0:0, 'col1']['col1'] = -88
parent

Out[10]: 
   col1  col2
0     1     3
1     2     4
```

**Will always succeed if first call dict-like**

```
parent = pd.DataFrame({'col1':[1,2], 'col2':[3,4]})
parent['col1'].loc[0:0] = -88
parent

Out[11]: 
   col1  col2
0   -88     3
1     2     4
```

To Do:
- Get feedback on implementation; 
- if sound in behavior and principles, remove all SettingWithCopy machinery
- Add docs

@jreback 
@TomAugspurger 
@shoyer 
@JanSchulz 
cc @ellisonbg
cc @CarstVaartjes 
