`class` 是 Python **语句**。`class` 语句是对象的创建者并且是一个隐含的赋值运算——执行时，它会产生类对象，并把引用值存储在前面所使用的变量名。

# 存储数据

使用类来管理数据：


```python
class SharedData:
    spam = 42    # 由所有实例共享
```


```python
# 创建两个实例
x = SharedData()
y = SharedData()
```


```python
x.spam, y.spam
```




    (42, 42)




```python
x.spam = 7     # 修改实例 x
x.spam, y.spam   # y 没有被修改
```




    (7, 42)




```python
SharedData.spam = 45 # 通过类修改数据
x.spam, y.spam   # y 被修改
```




    (7, 45)



如果想要同时修改 `x`、 `y`，你需要这样：


```python
SharedData.spam = 45
x = SharedData()
y = SharedData()
x.spam, y.spam   # x, y 均被修改
```




    (45, 45)




```python
id(x) == id(y)
```




    False




```python
x.spam = 7     # 修改实例 x
x.spam, y.spam   # y 没有被修改
```




    (7, 45)



# 命名空间

- 无点号运算的变量名（如，X）与作用域相对应
- 点号的属性名（例如，object.X）使用的是对象的命名空间
- 有些作用域会对对象的命名空间进行初始化（模块和类）

# 类与模块

- 模块
    - 是数据/逻辑包
    - 通过编写 Python 文件或 C 扩展来创建
    - 通过导入来使用
- 类
    - 实现新的对象
    - 由 `class` 语句创建
    - 通过调用来使用
    - 总是位于一个模块中

# 运算符重载

**运算符重载**只是意味着在类方法中**拦截**内置的操作——当类的实例出现在内置操作中，Python 自动调用你的方法，并且你的方法的返回值变成了相应操作的结果。

- 运算符重载允许类拦截常规的 Python 操作。
- 类可以重载所有 Python 表达式运算符。
- 类还可以重载内置操作, 如打印、函数调用、属性访问等. 
- 重载使类实例的作用更类似于内置类型。
- 通过在类中提供特殊命名的方法来实现重载

## Constructors and Expressions: `__init__` and `__sub__` 

作为审查, 请考虑以下简单示例: 它的 **Number** 类, 编码在文件 `number.py` 中, 提供一种方法来拦截实例构造 (`__init__`), 以及一个用于捕获减法表达式 (`__sub__`)。特殊的方法, 如这些是挂钩, 让您连接到内置的操作:


```python
# File number.py
class Number:
    def __init__(self, start):  # On Number(start)
        self.data = start

    def __sub__(self, other):  # On instance - other
        return Number(self.data - other)  # Result is a new instance
```


```python
from number import Number  # Fetch class from module
X = Number(5)  # Number.__init__(X, 5)
Y = X - 2  # Number.__sub__(X, 2)
Y.data  # Y is new Number instance
```




    3




### 常用的运算符重载方法

方法|重载|调用
:-|:-|:-
`__init__`|Constructor，构造器| 建立对象: `X = Class(args)`
`__del__`| Destructor，析构器| Object reclamation(回收) of X 
`__add__` |`+`| `X + Y`, `X += Y` if no `__iadd__` 
`__or__` |$\vert$ (bitwise OR)| `X` $\vert$ `Y`, `X` $\vert$= `Y` if no `__ior__`
`__repr__`, `__str__`| Printing, conversions(转换)| `print(X)`, `repr(X)`, `str(X)`
`__call__`| Function calls(函数调用)| `X(*args, **kargs)`
`__getattr__`| Attribute fetch(提取特征或属性) |`X.undefined`
`__setattr__`| Attribute assignment，属性赋值 |`X.any = value`
`__delattr__`| 属性删除| `del X.any`
`__getattribute__`| Attribute fetch| `X.any`
`__getitem__`| Indexing, slicing, iteration| `X[key]`, `X[i:j]`, for loops and other iterations if no `__iter__`
`__setitem__`| Index and slice assignment| `X[key] = value`, `X[i:j] = iterable`
`__delitem__`| Index and slice deletion| `del X[key]`, `del X[i:j]`
`__len__`| 长度| `len(X)`, truth tests（真值测试） if no `__bool__`
`__bool__`| Boolean tests|`bool(X)`
`__lt__`, `__gt__`, `__le__`, `__ge__`, `__eq__`, `__ne__`| Comparisons| `X < Y`, `X > Y`, `X <= Y`, `X >= Y`, `X == Y`, `X != Y` 
`__radd__`| Right-side operators，右侧加法| `Other + X`
`__iadd__`| In-place augmented operators，实地（增强的）加法| `X += Y` (or else `__add__`) 
`__iter__`, `__next__`| Iteration contexts| `I=iter(X)`, `next(I)`; for loops, in if no `__con tains__`, all comprehensions, map(F,X), others 
`__contains__` |Membership test，成员关系测试 |`item in X` (any iterable) 
`__index__` |Integer value| `hex(X)`, `bin(X)`, `oct(X)`, `O[X]`, `O[X:]` 
`__enter__`, `__exit__`| Context manager| `with obj as var:`
`__get__`, `__set__`, `__delete__`| Descriptor attributes | `X.attr`, `X.attr = value`, `del X.attr`
`__new__`| Creation | Object creation, before `__init__`

# Indexing and Slicing: `__getitem__` and `__setitem__` 

**索引**（`__getitem__`）和**切片**（`__setitem__`）：当实例 `X` 出现 `X[i]` 这样的索引运算时，Python 会调用这个实例继承的 `__getitem__` 方法（如果有的话），把 `X` 当作第一个参数传递，并且方括号内的索引值传递给第二个参数。


```python
class Indexer:
    def __getitem__(self, index):
        return index ** 2
```


```python
X = Indexer()
```


```python
X[2]
```




    4




```python
for i in range(5):
    print(X[i], end=' ')
```

    0 1 4 9 16 

## 拦截分片

`__getitem__` 也实现分片操作：


```python
L = [5, 6, 7, 8, 9] 
```


```python
L[2:4]
```




    [7, 8]




```python
L[:-2]
```




    [5, 6, 7]




```python
L[::2]
```




    [5, 7, 9]




```python
L[slice(2, 4)]
```




    [7, 8]




```python
L[slice(1, None)]
```




    [6, 7, 8, 9]




```python
L[slice(None,-2)]
```




    [5, 6, 7]




```python
L[slice(None, None, 2)]
```




    [5, 7, 9]



重载：


```python
class Indexer:
    data = [5, 6, 7, 8, 9]

    def __getitem__(self, index):  # Called for index or slice
        print('getitem:', index)
        return self.data[index]  # Perform index or slice
```


```python
X = Indexer() 
```


```python
X[0]
```

    getitem: 0
    




    5




```python
X[1]
```

    getitem: 1
    




    6




```python
X[-1]
```

    getitem: -1
    




    9




```python
X[2:4]
```

    getitem: slice(2, 4, None)
    




    [7, 8]




```python
X[::2]
```

    getitem: slice(None, None, 2)
    




    [5, 7, 9]



If used, the `__setitem__` index assignment method similarly intercepts both index and slice assignments it receives a slice object for the latter, which may be passed along in another index assignment or used directly in the same way: 


```py
class IndexSetter:    
    def __setitem__(self, index, value):    # Intercept index or slice assignment       
        ...        
        self.data[index] = value            # Assign index or slice 
```

In fact,`__getitem__` may be called automatically in even more contexts than indexing and slicing—it’s also an iteration fallback option, as we’ll see in a moment.

## Index Iteration: `__getitem__` 


```python
class StepperIndex:
    def __getitem__(self, i): 
        return self.data[i]
```


```python
X = StepperIndex()
```


```python
X.data = 'Spam'
```


```python
X[1]
```




    'p'




```python
for item in X:
    print(item, end=' ')
```

    S p a m 


```python
'p' in X  # All call __getitem__ too
```




    True




```python
[c for c in X]                    # List comprehension
```




    ['S', 'p', 'a', 'm']




```python
list(map(str.upper, X))           # map calls
```




    ['S', 'P', 'A', 'M']




```python
(a, b, c, d) = X  # Sequence assignments
a, c, d 
```




    ('S', 'a', 'm')




```python
list(X), tuple(X), ''.join(X)     # And so on... 
```




    (['S', 'p', 'a', 'm'], ('S', 'p', 'a', 'm'), 'Spam')




```python
X
```




    <__main__.StepperIndex at 0x1c0ef8b3a20>



# Iterable Objects: `__iter__` and `__next__`

Python 中的所有迭代 context 都将先尝试 `__iter__` 方法, 然后再尝试 `__getitem__`。


```python
# File squares.py
class Squares:
    def __init__(self, start, stop):    # Save state when created
        self.value = start - 1
        self.stop = stop

    def __iter__(self):                 # Get iterator object on iter
        return self

    def __next__(self):                 # Return a square on each iteration
        if self.value == self.stop:     # Also called by next built-in
            raise StopIteration
        self.value += 1
        return self.value ** 2
```


```python
for i in Squares(1, 5):
    print(i, end=' ')
```

    1 4 9 16 25 

下面实现了跳跃迭代：


```python
# File skipper.py
class SkipObject:
    def __init__(self, wrapped):  # Save item to be used
        self.wrapped = wrapped

    def __iter__(self):
        return SkipIterator(self.wrapped)  # New iterator each time


class SkipIterator:
    def __init__(self, wrapped):
        self.wrapped = wrapped  # Iterator state information
        self.offset = 0

    def __next__(self):
        if self.offset >= len(self.wrapped):  # Terminate iterations
            raise StopIteration
        else:
            item = self.wrapped[self.offset]  # else return and skip
            self.offset += 2
            return item


if __name__ == '__main__':
    alpha = 'abcdef'
    skipper = SkipObject(alpha)  # Make container object
    I = iter(skipper)  # Make an iterator on it
    print(next(I), next(I), next(I))  # Visit offsets 0, 2, 4
    for x in skipper:  # for calls __iter__ automatically
        for y in skipper:  # Nested fors call __iter__ again each time
            print(x + y, end=' ')  # Each iterator has its own state, offset
```

    a c e
    aa ac ae ca cc ce ea ec ee 

## Coding Alternative: `__iter__` plus yield

由于生成器函数会自动保存局部变量状态并创建所需的迭代器方法, 因此它们非常适合最小化用户定义的自定义迭代器的编码要求, 并补充了从类中获取的状态保留和其他实用程序。

> As a review, recall that any function that contains a yield statement is turned into a generator function. When called, it returns a new generator object with automatic retention of local scope and code position, an automatically created `__iter__` method that simply returns itself, and an automatically created `__next__` method that starts the function or resumes it where it last left off: 


```python
def gen(x): 
    for i in range(x): 
        yield i ** 2
```


```python
G = gen(5)  # Create a generator with __iter__ and __next__
G.__iter__() == G        # Both methods exist on the same object 
```




    True




```python
I = iter(G)
next(I), next(I) 
```




    (0, 1)




```python
list(gen(5))
```




    [0, 1, 4, 9, 16]



即使具有`yield` 的生成器函数恰好是名为 `__iter__` 的方法, 这仍然是正确的: 每当迭代上下文工具调用时, 此类方法将返回具有必需 `__next__` 的新生成器对象。作为额外的奖励, 在类中编码为方法的生成器函数可以访问实例属性和局部范围变量中的已保存状态。


```python
# File squares_yield.py
class Squares:  # __iter__ + yield generator
    def __init__(self, start, stop):  # __next__ is automatic/implied
        self.start = start
        self.stop = stop

    def __iter__(self):
        for value in range(self.start, self.stop + 1):
            yield value**2
```


```python
for i in Squares(1, 5):
    print(i, end=' ')
```

    1 4 9 16 25 


```python
S = Squares(1, 5)  # Runs __init__: class saves instance state
S
```




    <__main__.Squares at 0x1c0ff230160>




```python
I = iter(S)                # Runs __iter__: returns a generator 
```


```python
next(I), next(I),  next(I), next(I), next(I) 
```




    (1, 4, 9, 16, 25)




```python
next(I) 
```


    ---------------------------------------------------------------------------

    StopIteration                             Traceback (most recent call last)

    <ipython-input-88-cba4a91f39e1> in <module>()
    ----> 1 next(I)
    

    StopIteration: 


# Membership: `__contains__`, `__iter__`, and `__getitem__`

在迭代域中, 类可以使用 `__iter__` 或 `__getitem__` 方法实现 `in` 成员资格运算符作为迭代。不过, 为了支持更具体的成员身份, 类可以编写 `__contains__` 方法——当存在时, 此方法优先于 `__iter__`, `__iter__` 优于 `__getitem__`。`__contains__` 方法应将成员身份定义为应用于**映射**的键 (并且可以使用快速查找), 并作为对**序列**的搜索。


```python
class Iters:
    def __init__(self, value):
        self.data = value

    def __getitem__(self, i):  # Fallback for iteration
        print('get[%s]:' % i, end='')  # Also for index, slice
        return self.data[i]

    def __next__(self):
        print('next:', end='')
        if self.ix == len(self.data):
            raise StopIteration
        item = self.data[self.ix]
        self.ix += 1
        return item


if __name__ == '__main__':
    X = Iters([1, 2, 3, 4, 5])  # Make instance
    print(3 in X)  # Membership
    for i in X:  # for loops
        print(i, end=' | ')
    print()
    # Other iteration contexts    print( list(map(bin, X)) )
    print([i**2 for i in X])
    # Manual iteration (what other contexts do)
    I = iter(X)
    while True:
        try:
            print(next(I), end=' @ ')
        except StopIteration:
            break
```

    get[0]:get[1]:get[2]:True
    get[0]:1 | get[1]:2 | get[2]:3 | get[3]:4 | get[4]:5 | get[5]:
    get[0]:get[1]:get[2]:get[3]:get[4]:get[5]:[1, 4, 9, 16, 25]
    get[0]:1 @ get[1]:2 @ get[2]:3 @ get[3]:4 @ get[4]:5 @ get[5]:

可以看出上面的显示很乱，为此我们重载 `__iter__` 方法：


```python
class Iters1(Iters):
    def __iter__(self):  # Preferred for iteration
        print('iter=> ', end='')  # Allows only one active iterator
        self.ix = 0
        return self
    
if __name__ == '__main__':
    X = Iters1([1, 2, 3, 4, 5])  # Make instance
    print(3 in X)  # Membership
    for i in X:  # for loops
        print(i, end=' | ')
    print()
    # Other iteration contexts    print( list(map(bin, X)) )
    print([i**2 for i in X])
    # Manual iteration (what other contexts do)
    I = iter(X)
    while True:
        try:
            print(next(I), end=' @ ')
        except StopIteration:
            break
```

    iter=> next:next:next:True
    iter=> next:1 | next:2 | next:3 | next:4 | next:5 | next:
    iter=> next:next:next:next:next:next:[1, 4, 9, 16, 25]
    iter=> next:1 @ next:2 @ next:3 @ next:4 @ next:5 @ next:

还可以进一步的改进：


```python
class Iters2(Iters1):
    def __contains__(self, x):  # Preferred for 'in'
        print('contains: ', end='')
        return x in self.data


if __name__ == '__main__':
    X = Iters2([1, 2, 3, 4, 5])  # Make instance
    print(3 in X)  # Membership
    for i in X:  # for loops
        print(i, end=' | ')
    print()
    # Other iteration contexts    print( list(map(bin, X)) )
    print([i**2 for i in X])
    # Manual iteration (what other contexts do)
    I = iter(X)
    while True:
        try:
            print(next(I), end=' @ ')
        except StopIteration:
            break
```

    contains: True
    iter=> next:1 | next:2 | next:3 | next:4 | next:5 | next:
    iter=> next:next:next:next:next:next:[1, 4, 9, 16, 25]
    iter=> next:1 @ next:2 @ next:3 @ next:4 @ next:5 @ next:

最后我们将其合并为：


```python
class Iters:
    def __init__(self, value):
        self.data = value

    def __getitem__(self, i):  # Fallback for iteration
        print('get[%s]:' % i, end='')  # Also for index, slice
        return self.data[i]

    def __iter__(self):  # Preferred for iteration
        print('iter=> ', end='')  # Allows only one active iterator
        self.ix = 0
        return self

    def __next__(self):
        print('next:', end='')
        if self.ix == len(self.data):
            raise StopIteration
        item = self.data[self.ix]
        self.ix += 1
        return item

    def __contains__(self, x):  # Preferred for 'in'
        print('contains: ', end='')
        return x in self.data


if __name__ == '__main__':
    X = Iters([1, 2, 3, 4, 5])  # Make instance
    print(3 in X)  # Membership
    for i in X:  # for loops
        print(i, end=' | ')
    print()
    # Other iteration contexts    print( list(map(bin, X)) )
    print([i**2 for i in X])
    # Manual iteration (what other contexts do)
    I = iter(X)
    while True:
        try:
            print(next(I), end=' @ ')
        except StopIteration:
            break
```

    contains: True
    iter=> next:1 | next:2 | next:3 | next:4 | next:5 | next:
    iter=> next:next:next:next:next:next:[1, 4, 9, 16, 25]
    iter=> next:1 @ next:2 @ next:3 @ next:4 @ next:5 @ next:

正如我们所看到的, `__getitem__` 方法更通用: 除了迭代, 它还截获显式索引以及切片。切片表达式使用包含边界的切片对象触发 `__getitem__`, 无论是内置类型还是用户定义类, 因此切片在我们的类中是自动的:


```python
X = Iters('spam')
X[0]
```

    get[0]:




    's'




```python
'spam'[1:]
```




    'pam'




```python
'spam'[slice(1, None)] 
```




    'pam'




```python
X[1:] 
```

    get[slice(1, None, None)]:




    'pam'




```python
X[:-1] 
```

    get[slice(None, -1, None)]:




    'spa'




```python
list(X) 
```

    iter=> next:next:next:next:next:




    ['s', 'p', 'a', 'm']



# Attribute Access: `__getattr__` and `__setattr__`

`__getattr__` 方法是拦截属性点号运算的。具体地说, 对于从类创建的对象, 点运算符表达式对象. 属性也可以由代码实现, 用于引用、赋值和删除上下文。具体地说, 对于从类创建的对象, 点运算符表达式 `object.attribute` 也可以由代码实现, 用于引用、赋值和删除上下文。


```python
class Empty:
        def __getattr__(self, attrname):           # On self.undefined
            if attrname == 'age':
                return 40
            else:
                raise AttributeError(attrname)
```


```python
X = Empty()
X.age
```




    40



 `age` 变成了一个 dynamically computed attribute（动态计算的属性）—its value is formed by running code, not fetching an object. 

- The `__getattribute__` method intercepts all attribute fetches, not just those that are undefined, but when using it you must be more cautious than with `__get attr__` to avoid loops. 
- The property built-in function allows us to associate methods with fetch and set operations on a specific class attribute. 
- *Descriptors* provide a protocol for associating `__get__` and `__set__` methods of a class with accesses to a specific class attribute. - Slots attributes are declared in classes but create implicit storage in each instance. 

# Call 表达式: `__call__`

Python 为应用于您的实例的函数调用表达式运行 `__call__` 方法，这样可以让类实例类似于函数。


```python
class Callee:
    def __call__(self, *pargs, **kargs):  # Intercept instance calls
        print('Called:', pargs, kargs)  # Accept arbitrary arguments
```


```python
C = Callee()
```


```python
C(1, 2, 3)
```

    Called: (1, 2, 3) {}
    


```python
C(1, 2, 3, x=4, y=5)
```

    Called: (1, 2, 3) {'x': 4, 'y': 5}
    

更正式的说，所有的参数传递方式，`__call__` 方法都支持：


```python
class C:
    def __call__(self, a, b, c=5, d=6): ...        # Normals and defaults


class C:
    def __call__(self, *pargs, **
                 kargs): ...       # Collect arbitrary arguments


class C:
    def __call__(self, *pargs, d=6, **kargs): ...  # keyword-only argument
```

都匹配如下调用方式：


```python
X = C()
X(1, 2)  # Omit defaults
X(1, 2, 3, 4)  # Positionals
X(a=1, b=2, d=4)  # Keywords
X(*[1, 2], **dict(c=3, d=4))  # Unpack arbitrary arguments
X(1, *(2, ), c=3, **dict(d=4))  # Mixed modes
```