zoukankan      html  css  js  c++  java
  • 类代码编写细节

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

    存储数据

    使用类来管理数据:

    class SharedData:
        spam = 42    # 由所有实例共享
    
    # 创建两个实例
    x = SharedData()
    y = SharedData()
    
    x.spam, y.spam
    
    (42, 42)
    
    x.spam = 7     # 修改实例 x
    x.spam, y.spam   # y 没有被修改
    
    (7, 42)
    
    SharedData.spam = 45 # 通过类修改数据
    x.spam, y.spam   # y 被修改
    
    (7, 45)
    

    如果想要同时修改 xy,你需要这样:

    SharedData.spam = 45
    x = SharedData()
    y = SharedData()
    x.spam, y.spam   # x, y 均被修改
    
    (45, 45)
    
    id(x) == id(y)
    
    False
    
    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__)。特殊的方法, 如这些是挂钩, 让您连接到内置的操作:

    # 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
    
    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 当作第一个参数传递,并且方括号内的索引值传递给第二个参数。

    class Indexer:
        def __getitem__(self, index):
            return index ** 2
    
    X = Indexer()
    
    X[2]
    
    4
    
    for i in range(5):
        print(X[i], end=' ')
    
    0 1 4 9 16 
    

    拦截分片

    __getitem__ 也实现分片操作:

    L = [5, 6, 7, 8, 9] 
    
    L[2:4]
    
    [7, 8]
    
    L[:-2]
    
    [5, 6, 7]
    
    L[::2]
    
    [5, 7, 9]
    
    L[slice(2, 4)]
    
    [7, 8]
    
    L[slice(1, None)]
    
    [6, 7, 8, 9]
    
    L[slice(None,-2)]
    
    [5, 6, 7]
    
    L[slice(None, None, 2)]
    
    [5, 7, 9]
    

    重载:

    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
    
    X = Indexer() 
    
    X[0]
    
    getitem: 0
    
    
    
    
    
    5
    
    X[1]
    
    getitem: 1
    
    
    
    
    
    6
    
    X[-1]
    
    getitem: -1
    
    
    
    
    
    9
    
    X[2:4]
    
    getitem: slice(2, 4, None)
    
    
    
    
    
    [7, 8]
    
    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:

    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__

    class StepperIndex:
        def __getitem__(self, i): 
            return self.data[i]
    
    X = StepperIndex()
    
    X.data = 'Spam'
    
    X[1]
    
    'p'
    
    for item in X:
        print(item, end=' ')
    
    S p a m 
    
    'p' in X  # All call __getitem__ too
    
    True
    
    [c for c in X]                    # List comprehension
    
    ['S', 'p', 'a', 'm']
    
    list(map(str.upper, X))           # map calls
    
    ['S', 'P', 'A', 'M']
    
    (a, b, c, d) = X  # Sequence assignments
    a, c, d 
    
    ('S', 'a', 'm')
    
    list(X), tuple(X), ''.join(X)     # And so on... 
    
    (['S', 'p', 'a', 'm'], ('S', 'p', 'a', 'm'), 'Spam')
    
    X
    
    <__main__.StepperIndex at 0x1c0ef8b3a20>
    

    Iterable Objects: __iter__ and __next__

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

    # 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
    
    for i in Squares(1, 5):
        print(i, end=' ')
    
    1 4 9 16 25 
    

    下面实现了跳跃迭代:

    # 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:

    def gen(x): 
        for i in range(x): 
            yield i ** 2
    
    G = gen(5)  # Create a generator with __iter__ and __next__
    G.__iter__() == G        # Both methods exist on the same object 
    
    True
    
    I = iter(G)
    next(I), next(I) 
    
    (0, 1)
    
    list(gen(5))
    
    [0, 1, 4, 9, 16]
    

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

    # 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
    
    for i in Squares(1, 5):
        print(i, end=' ')
    
    1 4 9 16 25 
    
    S = Squares(1, 5)  # Runs __init__: class saves instance state
    S
    
    <__main__.Squares at 0x1c0ff230160>
    
    I = iter(S)                # Runs __iter__: returns a generator 
    
    next(I), next(I),  next(I), next(I), next(I) 
    
    (1, 4, 9, 16, 25)
    
    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__ 方法应将成员身份定义为应用于映射的键 (并且可以使用快速查找), 并作为对序列的搜索。

    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__ 方法:

    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:
    

    还可以进一步的改进:

    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:
    

    最后我们将其合并为:

    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__, 无论是内置类型还是用户定义类, 因此切片在我们的类中是自动的:

    X = Iters('spam')
    X[0]
    
    get[0]:
    
    
    
    
    's'
    
    'spam'[1:]
    
    'pam'
    
    'spam'[slice(1, None)] 
    
    'pam'
    
    X[1:] 
    
    get[slice(1, None, None)]:
    
    
    
    
    'pam'
    
    X[:-1] 
    
    get[slice(None, -1, None)]:
    
    
    
    
    'spa'
    
    list(X) 
    
    iter=> next:next:next:next:next:
    
    
    
    
    ['s', 'p', 'a', 'm']
    

    Attribute Access: __getattr__ and __setattr__

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

    class Empty:
            def __getattr__(self, attrname):           # On self.undefined
                if attrname == 'age':
                    return 40
                else:
                    raise AttributeError(attrname)
    
    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__ 方法,这样可以让类实例类似于函数。

    class Callee:
        def __call__(self, *pargs, **kargs):  # Intercept instance calls
            print('Called:', pargs, kargs)  # Accept arbitrary arguments
    
    C = Callee()
    
    C(1, 2, 3)
    
    Called: (1, 2, 3) {}
    
    C(1, 2, 3, x=4, y=5)
    
    Called: (1, 2, 3) {'x': 4, 'y': 5}
    

    更正式的说,所有的参数传递方式,__call__ 方法都支持:

    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
    

    都匹配如下调用方式:

    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
    
  • 相关阅读:
    实验四 决策树算法及应用
    实验三 朴素贝叶斯算法及应用
    实验二 K-近邻算法及应用
    实验一 感知器及其应用
    园区网核心、防火墙、边界出口冗余实验配置
    PNETLab模拟器部署及使用配置
    基于RPS开发模式的可视化设计解决方案
    Python学习手册《Learning Python》
    【转】Windows 64bit下Python环境配置与集成IDE PyCharm
    时间复杂度完美例子---数组最大子序列和
  • 原文地址:https://www.cnblogs.com/q735613050/p/9879901.html
Copyright © 2011-2022 走看看