zoukankan      html  css  js  c++  java
  • Python Revisited Day 06 (面向对象程序设计)

    《Python 3 程序开发指南》 学习笔记

    6.1 面向对象方法

    duck typing

    “如果走起来像只鸭子,叫起来也像只鸭子,那它就是一只鸭子。”

    访问限制 __

    class Circle:
        def __init__(self, x=0, y=0, radius=0):
            self.x = x
            self.y = y
            self.radius = radius
            self.__PI = 3.1415926  #私有属性 __
        
        def get_PI(self):
            
            return self.__PI  #我们可以通过方法来获得此属性,当然相应的改变也可以
            
        def get_area(self):
            
            return self.PI * self.radius ** 2
    
    c = Circle(2, 2, 2)
    c.x, c.y,c.radius  #(2, 2, 2)
    c.__PI   # AttributeError
    c.get_PI()   #3.1415926
    

    6.2 自定义类

    class className:
    	suite
    class className(base_classes):
    	suite
    

    6.2.1 属性与方法

    
    class Circle:
        def __init__(self, x=0, y=0, radius=0):
            self.x = x
            self.y = y
            self.radius = radius
            self.__PI = 3.1415926
    
        def get_PI(self):
            return self.__PI
    
        def get_area(self):
            return self.PI * self.radius ** 2
    
        def __eq__(self, other):
            return (self.x, self.y, self.radius) == (other.x, other.y, other.radius)
        
        def __repr__(self):
            return "Circle({0.x!r}, {0.y!r}, {0.radius!r})".format(self)  # !r  强制使用表现形式
        
        def __str__(self):
            return "({0.x!r}, {0.y!r}, {0.radius!r})".format(self)
        
    c = Circle(2, 2, 2)
    repr(c) # 'Point(2, 2, 2)'   == c.__repr__()
    str(c) # '(2, 2, 2)'   == c.__str__()
    c2 = Circle(2, 2, 2)
    c == c2 # True   == c.__eq__(c2)
    c != c2  # False
    

    预定义的特殊方法 _..._

    一般的方法名起始和结尾不应该使用俩个下划线,除非是预定义的特殊方法(大概就是操作符所对应的方法,还有一些固定的方法?)。

    比较的特殊方法

    特殊方法 使用 描述
    __it__(self, other) x < y 如果x比y小,则返回True
    __le__(self, other) x <= y ...
    __eq__(self, other) x == y ...
    __ne__(self, other) x != y ...
    __ge__(self, other) x >= y ...
    __gt__(self, other) x > y ...

    默认情况下,自定义类的实例都是可哈希运算的。如果重新实现了__eq__(),实例便不可哈希运算。

    class Circle:
        def __init__(self, x=0, y=0, radius=0):
            self.x = x
            self.y = y
            self.radius = radius
            self.__PI = 3.1415926
        
    c = Circle(2, 2, 2)
    c2 = Circle(2, 2, 2)
    c == c2 # False
    
        def __eq__(self, other):
            if not isinstance(other, Circle):  #assert isinstance(other, Circle)
                raise TypeError() # NotImplem-entled
            return (self.x, self.y, self.radius) == (other.x, other.y, other.radius)
    

    上述对__eq__()的改写,可以避免类似 "c == 1"。

    c = Circle(2, 2, 2) 
    c2 = eval(repr(c)) #如果Circle是引用来的,要加入模块 c.__module__+'.'+repr(c)
    c == c2
    

    6.2.2 继承与多态

    import math
    
    
    class Point:
    
        def __init__(self, x=0, y=0):
            self.x = x
            self.y = y
    
        def distance_from_origin(self):
            return math.hypot(self.x, self.y)
    
        def __eq__(self, other):
    
            if not isinstance(other, Point):
                raise TypeError()
            return (self.x ,self.y) == (other.x, other.y)
    
        def __repr__(self):
            return "Point({0.x!r}, {0.y!r})".format(self)
    
        def __str__(self):
            return "({0.x!r}, {0.y!r})".format(self)
    
    
    
    
    
    class Circle(Point):
        def __init__(self, x=0, y=0, radius=0):
            super().__init__(x, y)
            self.radius = radius
            self.__PI = 3.1415926
    
        def get_PI(self):
            return self.__PI
    
        def edge_distance_from_origin(self):
            return abs(self.distance_from_origin() - self.radius)
    
        def area(self):
            return self.PI * self.radius ** 2
    
        def circumference(self):
            return 2 * self.__PI * self.radius
    
        def __eq__(self, other):
            if not isinstance(other, Circle):  #assert isinstance(other, Circle)
                raise TypeError() # NotImplem-entled
            return self.radius == other.radius and super().__eq__(other) #!!!
    
        def __repr__(self):
            return "Circle({0.x!r}, {0.y!r}, {0.radius!r})".format(self)
    
        def __str__(self):
            return "({0.x!r}, {0.y!r}, {0.radius!r})".format(self)
    

    如果__eq_() 里用Circle.__eq_(self, other)

        def __eq__(self, other):
            if not isinstance(other, Circle):  #assert isinstance(other, Circle)
                raise TypeError() # NotImplem-entled
            return self.radius == other.radius and Circle.__eq__(self, other) #!!!
    c = Circle(2, 2, 2)
    c2 = Circle(2, 2, 2)
    c == c2  #会进入无限迭代,因为实际上调用的Circle类里的__eq__而不是Point类里的
    #另外,使用 super().__eq__(other), python 会自动传入self
    

    6.2.3 使用特性进行属性存取控制 @property

    
    class Circle(Point):
        def __init__(self, radius, x=0, y=0):  #注意radius的位置 且无默认值
            super().__init__(x, y)
            self.radius = radius #!!!!!!!!!!!!!!!!!!
            self.__PI = 3.1415926
    
        def get_PI(self):
            return self.__PI
    
        @property
        def radius(self):
            """The Circle's radius
    
            >>> circle = Circle(-2)
            Traceback (most recent call last):
            ...
            AssertionError: radius must be nonzero and non-negative
            >>> circle = Circle(4)
            >>> circle.radius = -1
            Traceback (most recent call last):
            ...
            AssertionError: radius must be nonzero and non-negative
            >>> circle.radius = 6
    
            """
            return self.__radius
    
        @radius.setter
        def radius(self, radius):
            assert radius > 0, "radius must be nonzero and non-negative"
            self.__radius = radius
    
    
        @property
        def edge_distance_from_origin(self):
            return abs(self.distance_from_origin() - self.radius)
    
        @property
        def area(self):
            return self.PI * self.radius ** 2
    
        def circumference(self):
            return 2 * self.__PI * self.radius
    
        def __eq__(self, other):
            if not isinstance(other, Circle):  #assert isinstance(other, Circle)
                raise TypeError() # NotImplem-entled
            return self.radius == other.radius and super().__eq__(other)
    
        def __repr__(self):
            return "Circle({0.x!r}, {0.y!r}, {0.radius!r})".format(self)
    
        def __str__(self):
            return "({0.x!r}, {0.y!r}, {0.radius!r})".format(self)
    
    
    

    如果

        def __init__(self, radius, x=0, y=0):  #注意radius的位置 且无默认值
            super().__init__(x, y)
            self.radius = radius #!!!!!!!!!!!!!!!!!!
            self.__PI = 3.1415926
    

    改为

        def __init__(self, radius, x=0, y=0):  #注意radius的位置 且无默认值
            super().__init__(x, y)
            self.__radius = radius #!!!!!!!!!!!!!!!!!!
            self.__PI = 3.1415926
    

    那么

    c = Circle(-1) 
    

    不会报错。

    每个创建的特性(即用@property)修饰之后,都包含getter,setter,deleter等属性。

    6.2.4 创建完全整合的数据类型

    基本的特殊方法

    特殊方法 使用 描述
    __bool__(self) bool(x) 如果提供,就返回x的真值,对 if x:... 是有用的
    __format__(self, format_spec) "{0}".format(x) 为自定义类提供str.format()支持
    __hash__(self) hash(x) 如果提供,那么x可用作字典的键或存放在集合中
    __init__(self, args) x = X(args) 对象初始化调用
    __new__(cls, args) x = X(args) 创建对象时调用
    __repr__(self) repr(x) 返回x的字符串表示,在可能的地方eval(repr(x)) == x
    __repr_(self) ascii(x) 仅使用ASCII返回x的字符串表示
    str(self) str(x) 返回x的适合阅读的字符串表示

    数值型与位逻辑运算的特殊方法

    特殊方法 使用 特殊方法 使用
    __abs__(self) abs(x) __complex__(self) complex(x)
    __float__(self) float(x) __init__(self) int(x)
    __index__(self) bin(x) oct(x) hex(x) __round__(self, digits) round(x, digits)
    __pos__(self) +x __neg__(self) -x
    __add__(self, other) x + y __sub__(self, other) x - y
    __iadd__(self, other) x += y __isub__(self, other) x -= y
    __radd__(self, other) y + x __rsub__(self, other) y - x
    __mul__(self, other) x * y __mod__(self, other) x % y
    __imul__(self, other) x *= y __imod__(self, other) x %= y
    __rmul__(self, other) y * x __rmod__( self, other) y % x
    __floordiv__(self, other) x // y __truediv__(self, other) x / y
    __ifloordiv__(self, other) x //= y __itruediv__(self, other) x /= y
    __rfloordiv__(self, other) y // x __rtruediv__(self,other) y / x
    __divmod__(self, other) divmod(x, y) __rdivmod__(self, other) divmod(y, x)
    __pow__(self, other) x ** y __and__(self, other) x & y
    __ipow__(self, other) x **= y __iand__(self, other) x &= y
    __rpow__(self, other) y ** x __rand__(self, other) y & x
    __xor__(self, other) x ^ y __or__(self, other) x | y
    __ixor__(self, other) x ^= y __ior__(self, other) x |= y
    __rxor__(self, other) y ^ x __ror__(self, other) y | x
    __lshift__(self, other) x << y __rshift__(self, other) x >> y
    __ilshift__(self, other) x <<= y __irshift__(self, other) x >>= y
    __rlshift__(self, other) y << x __rrshift__(self, other) y >> x
    __invert__(self) ~x

    6.2.4.1 从头开始创建数据类型

    class FuzzyBool:
    
        """从头开始创建数据类型FuzzyBool:模糊型布尔值
        FuzzyBool扩展了二元值true 和 false. 1.0 表示 true,
        0.0 表示false, 0.5 表示 50% true.
    
    
        >>> a = FuzzyBool(0.875)
        >>> b = FuzzyBool(0.25)
    
        实例提供比较"> >= < <= = !="
        >>> a >= b
        True
        >>> a == 1
        Traceback (most recent call last):
        ...
        TypeError
    
        实例支持bool()操作
        >>> bool(a), bool(b)
        (True, False)
    
        实例支持位操作符
        >>> ~a
        FuzzyBool(0.125)
        >>> a & b
        FuzzyBool(0.25)
        >>> b |= FuzzyBool(.5)
    
        支持format
        >>> "a={0:.1%} b={1:.0%}".format(a, b)
        'a=87.5% b=50%'
    
        """
        def __init__(self, value=0.0):
            """初始化函数 value 默认值为0.0
            且传入的值要求在0,1之间,否则取0.0
            """
            self.__value = value if 0.0 <= value <= 1.0 else 0.0
    
        def __invert__(self):
            """
            倒置操作符 ~
            :return: FuzzyBool(1.0 - self.__value)
            """
            return FuzzyBool(1.0 - self.__value)
    
        def __and__(self, other):
            """
            & 的特殊方法
            :param other: 相同的鸭子。。。
            :return: self.__value 与 other.__value的小的
            """
            if not isinstance(other, FuzzyBool):
                raise  TypeError()
            return FuzzyBool(min(self.__value, other.__value))
    
        def __iand__(self, other):
            """
            &= 的特殊方法
            :param other: 相同的鸭子。。。
            :return: self.__value更新为self.__value和other.__value中较小的那个, 返回self.
            """
            if not isinstance(other, FuzzyBool):
                raise TypeError()
            self.__value = min(self.__value, other.__value)
            return self
    
        def __or__(self, other):
            """
            |
            :param other:
            :return:
            """
            if not isinstance(other, FuzzyBool):
                raise TypeError()
            return FuzzyBool(max(self.__value, other.__value))
    
        def __ior__(self, other):
            """
            |=
            :param other:
            :return:
            """
            if not isinstance(other, FuzzyBool):
                raise TypeError()
            return FuzzyBool(max(self.__value, other.__value))
    
        def __repr__(self):
            """
            表象形式
            :return:
            """
            return "{0}({1})".format(self.__class__.__name__,
                                     self.__value)
    
        def __str__(self):
            """
            字符串形式
            :return:
            """
            return str(self.__value)
    
        def __bool__(self):
            """
            if self.__value > 0.5
            :return: True
            """
            return self.__value > 0.5
    
        def __int__(self):
            """
            整数形式
            :return:
            """
            return round(self.__value)
    
        def __float__(self):
            """
            浮点数形式
            :return:
            """
            return self.__value
    
        # 要想完整的比较操作符集< > <= >= == != 只需要提供其中3个即可(< <= ==)
        # 余下的Python自己会推导出来
        def __lt__(self, other):
            """
            <
            :param other:
            :return:
            """
            if not isinstance(other, FuzzyBool):
                raise TypeError()
            return self.__value < other.__value
    
    
        def __eq__(self, other):
            """
            ==
            :param other:
            :return:
            """
            if not isinstance(other, FuzzyBool):
                raise  TypeError()
            return self.__value == other.__value
    
        def __le__(self, other):
            """
            <=
            :param other:
            :return:
            """
            if not isinstance(other, FuzzyBool):
                raise  TypeError()
            return self.__value <= other.__value
    
        def __hash__(self):
            """
            因为重写了__eq__(),所以需要提供__hash__()来使其可哈希
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            不能把self.__value作为哈希值,因为它是可变的
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            :return:
            """
            return hash(id(self))
    
        def  __format__(self, format_spec):
            return format(self.__value, format_spec)
    
        @staticmethod
        def conjunction(*fuzzies):
            """
            结链处理
            :param fuzzies:
            :return: fuzzies[0] & fuzzies[1] ... & fuzzies[n]
            """
            return FuzzyBool(min(float(x) for x in fuzzies))
    
    if __name__ == "__main__":
    
        import doctest
        doctest.testmod()
    
    

    6.2.4.2 从其他数据类型创建数据类型

    
    
    class FuzzyBool(float):
    
        """从头开始创建数据类型FuzzyBool:模糊型布尔值
        FuzzyBool扩展了二元值true 和 false. 1.0 表示 true,
        0.0 表示false, 0.5 表示 50% true.
    
    
        >>> a = FuzzyBool(0.875)
        >>> b = FuzzyBool(0.25)
    
        实例提供比较"> >= < <= = !="
        >>> a >= b
        True
    
        实例支持bool()操作
        >>> bool(a), bool(b)
        (True, False)
    
        实例支持位操作符
        >>> ~a
        FuzzyBool(0.125)
        >>> a & b
        FuzzyBool(0.25)
        >>> b |= FuzzyBool(.5)
    
        支持format
        >>> "a={0:.1%} b={1:.0%}".format(a, b)
        'a=87.5% b=50%'
    
        不支持+,-,*,/等运算符
        >>> -a
        Traceback (most recent call last):
        ...
        NotImplementedError
        >>> a + b
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand type(s) for '+':'FuzzyBool' and 'FuzzyBool'
    
    
    
        """
        def __new__(cls, value=0.0):
            """
            创建一个新类时,通常是可变的。对于固定类,
            我们需要在一个步骤中同时完成创建和初始化,
            因为对于固定对象而言,一旦创建,就不能更改。
            :param value:
            :return:
            """
            return super().__new__(cls,
                                   value if 0.0 <= value <= 1.0 else 0)
    
        def __invert__(self):
            return FuzzyBool(1.0 - float(self)) #注意是self!!!
    
        def __and__(self, other):
            return FuzzyBool(min(self, other))
    
        def __iand__(self, other):
            """
            因为是固定类型,所以,实际上依旧是创建了一个新实例
            :param other:
            :return:
            """
            return FuzzyBool(min(self, other))
    
        def __or__(self, other):
            return FuzzyBool(max(self, other))
    
        def __ior__(self, other):
            return FuzzyBool(max(self, other))
    
        def __repr__(self):
            return "{0}({1})".format(self.__class__.__name__,
                                     super().__repr__())
    
        def __bool__(self):
            return self > 0.5
    
        def __int__(self):
            return round(self)
    
        def __add__(self, other):
            """
            FuzzyBool类型加法是没有意义的
            :param other:
            :return:
            """
            raise  TypeError("unsupported operand type(s) for '+':"
                             "'{0}' and '{1}'".format(
                self.__class__.__name__, other.__class__.__name__
            ))
    
        def __iadd__(self, other):
            raise NotImplementedError()
    
        def __radd__(self, other):
            """
            通过TypeError异常
            :param other:
            :return:
            """
    
            raise  TypeError("unsupported operand type(s) for '+':"
                             "'{0}' and '{1}'".format(
                self.__class__.__name__, other.__class__.__name__
            ))
    
        def __neg__(self):
            raise NotImplementedError()
    
        def __eq__(self, other):
            raise NotImplemented
    
    
    
    if __name__ == "__main__":
    
        import doctest
        doctest.testmod()
    
    Tips 如何快速无效化不要的方法 exec()

    将下段代码放在FuzzyBool控制范围内即可无效化"-"和"index()"。
    该代码主要是用到了exec()函数。
    当然,下面无效化的是单值操作,二元操作符等要更加复杂。

    for name, operator in (("__neg__", "-"),
                           ("__index__", "index()")):
        message = "bad operand type for unary {0}: '{{self}}'".format(
            operator
        )
        exec("def {0}(self): raise TypeError("{1}".format("
             "self=self.__class__.__name__))".format(name, message))
    

    6.3 自定义组合类

    本节将展式3种自定义类:

    • Image 用于存放图像数据
    • SortedList
    • SortedDict

    6.3.1 创建聚集组合数据的类

    用于表示2D颜色图像的一个简单方法是使用一个2维数组存储,每个数组元素代表一种颜色。
    Image将采用一种更加高效的做法:记入一种单一的背景色,以及图像种不同于背景色的颜色。

    
    
    
    """
    This module provides the Image class which holds (x, y, color) triples
    and a background color to provide a kind of sparse-array representation of
    an image. A method to export the image in XPM format is also provided.
    
    >>> import os
    >>> import tempfile
    >>> red = "#FF0000"
    >>> blue = "#0000FF"
    >>> img = os.path.join(tempfile.gettempdir(), "test.img")
    >>> xpm = os.path.join(tempfile.gettempdir(), "test.xpm")
    >>> image = Image(10, 8, img)
    >>> for x, y in ((0, 0), (0, 7), (1, 0), (1, 1), (1, 6), (1, 7), (2, 1),
    ...             (2, 2), (2, 5), (2, 6), (2, 7), (3, 2), (3, 3), (3, 4),
    ...             (3, 5), (3, 6), (4, 3), (4, 4), (4, 5), (5, 3), (5, 4),
    ...             (5, 5), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (7, 1),
    ...             (7, 2), (7, 5), (7, 6), (7, 7), (8, 0), (8, 1), (8, 6),
    ...             (8, 7), (9, 0), (9, 7)):
    ...    image[x, y] = blue
    >>> for x, y in ((3, 1), (4, 0), (4, 1), (4, 2), (5, 0), (5, 1), (5, 2),
    ...             (6, 1)):
    ...    image[(x, y)] = red
    >>> print(image.width, image.height, len(image.colors), image.background)
    10 8 3 #FFFFFF
    >>> border_color = "#FF0000" # red
    >>> square_color = "#0000FF" # blue
    >>> width, height = 240, 60
    >>> midx, midy = width // 2, height // 2
    >>> image = Image(width, height, img, "#F0F0F0")
    >>> for x in range(width):
    ...     for y in range(height):
    ...         if x < 5 or x >= width - 5 or y < 5 or y >= height -5:
    ...             image[x, y] = border_color
    ...         elif midx - 20 < x < midx + 20 and midy - 20 < y < midy + 20:
    ...             image[x, y] = square_color
    >>> print(image.width, image.height, len(image.colors), image.background)
    240 60 3 #F0F0F0
    >>> image.save()
    >>> newimage = Image(1, 1, img)
    >>> newimage.load()
    >>> print(newimage.width, newimage.height, len(newimage.colors), newimage.background)
    240 60 3 #F0F0F0
    >>> image.export(xpm)
    >>> image.thing
    Traceback (most recent call last):
    ...
    AttributeError: 'Image' object has no attribute 'thing'
    >>> for name in (img, xpm):
    ...     try:
    ...         os.remove(name)
    ...     except EnvironmentError:
    ...         pass
    """
    
    import os, pickle
    """
    在Python中,pickling是将Python对象进行序列化的一种方法。Pickling之所以
    强大,是因为进行pickling处理的对象可以是组合数据类型。并且,即便要进行
    pickling处理的对象内部包含其他对象,仍然可以统一进行pickling处理————并且
    不会使得对象重复出现。
    说实话,并没有很深的理解,有空找官方文档看看吧。
    """
    
    """
    定义异常
    """
    class ImageError(Exception): pass
    class CoordinateError(ImageError): pass
    class LoadError(ImageError): pass
    class SaveError(ImageError): pass
    class ExportError(ImageError): pass
    class NoFilenameError(ImageError): pass
    
    """
    Image 类
    """
    
    class Image:
        """Class Image provides some methods about image, such as building and saving.
    
    
        """
        def __init__(self, width, height, filename="",
                     background="#FFFFFF"):
            """
            the keys of self.__data are (x, y)
            :param 
            :param height:
            :param filename: default: ""
            :param background:  default: "#FFFFFF"  white
            """
            self.filename = filename
            self.__background = background
            self.__data = {}
            self.__width = width
            self.__height = height
            self.__colors = {self.__background}
    
    
        @property
        def background(self):
            return self.__background
    
        @property
        def width(self):
            return self.__width
    
        @property
        def height(self):
            return self.__height
    
        @property
        def colors(self):
            """
            why we set() the set. In, fact, we return
            a copy of self.__colors to avoid the changing in accident.
            :return:
            """
            return set(self.__colors) #返回一个复制而避免外界不小心的改变 | {self.__background}
    
        def __getitem__(self, coordinate):
            """
            y[k] 方法的实现 同时要求输入的2元组
            :param coordinate:
            :return:
            """
            assert len(coordinate) == 2, "coordinate should be a 2-tuple"
            if (not (0 <= coordinate < self.width) or
                not (0 <= coordinate[1] < self.height)):
                raise  CoordinateError(str(coordinate))
            return self.__data.get(tuple(coordinate), self.__background)
    
        def __setitem__(self, coordinate, color):
            """
            y[k] = v 方法的实现 同时要求输入2元组
            :param coordinate: 坐标
            :param color: 该坐标上的颜色
            :return: None
            """
            assert len(coordinate) == 2, "coordinate should be a 2-tuple"
            if (not (0 <= coordinate[0] < self.width) or
                not (0 <= coordinate[1] < self.height)):
                raise CoordinateError(str(coordinate))
            if color == self.__background:
                self.__data.pop(tuple(coordinate), None) # 不用del的原因是 避免产生异常
            else:
                self.__data[tuple(coordinate)] = color
                self.__colors.add(color)
    
    
        def __delitem__(self, coordinate):
            assert len(coordinate) == 2, "coordinate should be a 2-tuple"
            if (not (0 <= coordinate[0] < self.width) or
                    not (0 <= coordinate[1] < self.height)):
                raise CoordinateError(str(coordinate))
            self.__data.pop(tuple(coordinate), None)
    
        def save(self, filename=None):
            """
            save the image...
    
            first block addresses the filename, if no filename is provided,
            raise Error.
    
            second block is the process to save
    
            :param filename:
            :return:
            """
            if filename is not None:
                self.filename = filename
            elif not self.filename:
                raise NoFilenameError()
    
    
    
            fh = None
            try:
                data = [self.width, self.height, self.__background,
                        self.__data]
                fh = open(self.filename, "wb") #二进制打开文件
                pickle.dump(data, fh, pickle.HIGHEST_PROTOCOL) #pickle.HIGHEST_PROTOCOL 是一种紧凑的二进制格式
            except (EnvironmentError, pickle.PicklingError) as err:
                raise SaveError(str(err))
            finally:
                if fh is not None:
                    fh.close()
    
        def load(self, filename=None):
            """
            for load image...
            the first block is the same as save...
            the second block is the process to loading...
            :param filename:
            :return: None
            """
            if filename is not None:
                self.filename = filename
            elif not self.filename:
                raise NoFilenameError()
    
    
            fh = None
            try:
                fh = open(self.filename, "rb")
                data = pickle.load(fh)
                (self.__width, self.__height,
                 self.__background, self.__data) = data
                self.__colors = (set(self.__data.values()) |
                                 {self.__background}) # s.union(t) == s|t
            except (EnvironmentError, pickle.UnpicklingError) as err:
                raise LoadError(str(err))
            finally:
                if fh is not None:
                    fh.close()
    
        def export(self, filename):
            if filename.lower().endswith(".xpm"):
                self.__export_xpm(filename) #
            else:
                raise ExportError("unsupported export format:" +
                                  os.path.splitext(filename)[1])
    
    
        def __export_xpm(self, filename):  #直接从源代码中复制过来的
            """Exports the image as an XPM file if less than 8930 colors are
            used
            """
            name = os.path.splitext(os.path.basename(filename))[0]
            count = len(self.__colors)
            chars = [chr(x) for x in range(32, 127) if chr(x) != '"']
            if count > len(chars):
                chars = []
                for x in range(32, 127):
                    if chr(x) == '"':
                        continue
                    for y in range(32, 127):
                        if chr(y) == '"':
                            continue
                        chars.append(chr(x) + chr(y))
            chars.reverse()
            if count > len(chars):
                raise ExportError("cannot export XPM: too many colors")
            fh = None
            try:
                fh = open(filename, "w", encoding="ascii")
                fh.write("/* XPM */
    ")
                fh.write("static char *{0}[] = {{
    ".format(name))
                fh.write("/* columns rows colors chars-per-pixel */
    ")
                fh.write('"{0.width} {0.height} {1} {2}",
    '.format(
                         self, count, len(chars[0])))
                char_for_colour = {}
                for color in self.__colors:
                    char = chars.pop()
                    fh.write('"{char} c {color}",
    '.format(**locals()))
                    char_for_colour[color] = char
                fh.write("/* pixels */
    ")
                for y in range(self.height):
                    row = []
                    for x in range(self.width):
                        color = self.__data.get((x, y), self.__background)
                        row.append(char_for_colour[color])
                    fh.write('"{0}",
    '.format("".join(row)))
                fh.write("};
    ")
            except EnvironmentError as err:
                raise ExportError(str(err))
            finally:
                if fh is not None:
                    fh.close()
    
    
    
    
    if __name__ == "__main__":
    
        import doctest
        doctest.testmod()
    

    组合类型的特殊方法 [ ], in

    特殊方法 使用 描述
    __contains__(self, x) x in y 如果x在序列y中,或x是映射y种的键,就返回True
    __delitem__(self, k) del y[k] 删除序列y中的第k项或映射y中键为k的项
    __getitem__(self, k) y[k] 返回序列y中第k项或映射y中键为k的项的值
    __iter__(self) for x in y: pass 返回序列y中的项或映射y中键的迭代子
    __len__(self) len(y) 返回y中项的个数
    __reversed__(self) reversed(y) 返回序列y中的项或映射y中的键的反向迭代子
    __setitem__(self, k, v) y[k] = v 将序列y中的第k项(或映射y中键为k的项)设置为v

    6.3.2 使用聚集创建组合类 SortedList

    """
    >>> L = SortedList((5, 8, -1, 3, 4, 22))
    >>> L[2] = 18 #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ...
    TypeError: use add() to insert a value and rely on the...
    >>> list(L)
    [-1, 3, 4, 5, 8, 22]
    >>> L.add(5)
    >>> L.add(5)
    >>> L.add(6)
    >>> list(L)
    [-1, 3, 4, 5, 5, 5, 6, 8, 22]
    >>> L.index(4)
    2
    >>> L.count(5), L.count(2)
    (3, 0)
    >>> L.insert(2, 9)
    Traceback (most recent call last):
    ...
    AttributeError: 'SortedList' object has no attribute 'insert'
    >>> L.reverse()
    Traceback (most recent call last):
    ...
    AttributeError: 'SortedList' object has no attribute 'reverse'
    >>> L.sort()
    Traceback (most recent call last):
    ...
    AttributeError: 'SortedList' object has no attribute 'sort'
    
    >>> import collections
    >>> isinstance(L, collections.Sequence)
    False
    """
    
    
    _identity = lambda x: x
    
    class SortedList:
        def __init__(self, sequence=None, key=None):
            """Creates a SortedList that orders using < on the items,
            or on the results of using the given key function
    
            >>> L = SortedList()
            >>> print(L)
            []
            >>> L = SortedList((5, 8, -1, 3, 4, 22))
            >>> print(L)
            [-1, 3, 4, 5, 8, 22]
            >>> L = SortedList({9, 8, 7, 6, -1, -2})
            >>> print(L)
            [-2, -1, 6, 7, 8, 9]
            >>> L = SortedList([-5, 4, -3, 8, -2, 16, -1, 0, -3, 8])
            >>> print(L)
            [-5, -3, -3, -2, -1, 0, 4, 8, 8, 16]
            >>> L2 = SortedList(L)
            >>> print(L2)
            [-5, -3, -3, -2, -1, 0, 4, 8, 8, 16]
            >>> L = SortedList(("the", "quick", "brown", "fox", "jumped"))
            >>> print(L)
            ['brown', 'fox', 'jumped', 'quick', 'the']
            >>> L.index('1')
            Traceback (most recent call last):
            ...
            ValueError: SortedList.index(x): x not in list
            """
            self.__key = key or _identity  #_identity = lambda x: x
            assert hasattr(self.__key, "__call__") # 对象是否能调用
            if sequence is None:
                self.__list = []
            elif (isinstance(sequence, SortedList) and
                  sequence.key == self.__key):
                """因为Python采用的是短路检测,所以不用担心后面部分会报错
                如果key使用lambdaIf创建的,那么这部分就不会执行,所以,这部分
                代码可能不会带来多大效率的提升。
                """
                self.__list = sequence.__list[:]
            else:
                self.__list = sorted(list(sequence), key=self.__key)
    
    
        @property
        def key(self):
            return self.__key
    
        def add(self, value):
            """
            书上说为了避免index超出限制,才分开来
            事实上,没问题啊,即便超出了也是加末尾
            所以不需要分类讨论
            是Python版本的问题?
            :param value:
            :return:
            """
    
            index = self.__bisect_left(value)
            if index == len(self.__list):
                self.__list.append(value)
            else:
                self.__list.insert(index, value)
    
        def __bisect_left(self, value):
            """
            二叉树算法找插入的index
            :param value:
            :return:
            """
            key = self.__key(value)
            left, right = 0, len(self.__list)
            while left < right:
                middle = (left + right) // 2
                if self.__key(self.__list[middle]) < key:
                    left = middle + 1
                else:
                    right = middle
    
            return left
    
        def remove(self, value):
            index = self.__bisect_left(value)
            if index < len(self.__list) and self.__list[index] == value:
                del self.__list[index]
            else:
                raise ValueError("{0}.remove(x): x not in list.".format(
                    self.__class__.__name__
                ))
    
        def remove_every(self, value):
            """
            删除每一个值为value的项
            :param value:
            :return:
            """
            count = 0
            index = self.__bisect_left(value)
            while (index < len(self.__list) and
                   sekf.__list[index] == value):
                del self.__list[index]
                count += 1
            return count
    
        def count(self, value):
            """
            :param value:
            :return: 值为value的项的数目
            """
            count = 0
            index = self.__bisect_left(value)
            while(index < len(self.__list) and
                  self.__list[index] == value):
                index += 1
                count += 1
    
            return count
    
        def index(self, value):
            """返回值为value的index,如果不存在报错"""
            index = self.__bisect_left(value)
            if (index < len(self.__list) and
                self.__list[index] == value):
                return index
            raise ValueError("{0}.index(x): x not in list".format(
                self.__class__.__name__
            ))
    
        def __delitem__(self, index):
            del self.__list[index]
    
        def __getitem__(self, index):
            return self.__list[index]
    
        def __setitem__(self, index, value):
            """
            禁止 L[n] = k
            但是我觉得可以
            __delitem__()
            add()
            :param index:
            :param value:
            :return:
            """
            raise TypeError("use add() to insert a value and rely on"
                            "the list to pu it in the right place")
    
        def __iter__(self):
            """
            list(L), 此时Python将调用SortedList.__iter__(L)
            来提供list()函数所需要的序列。
            :return:
            """
            return iter(self.__list)
    
        def __reversed__(self):
            return reversed(self.__list)
    
        def __contains__(self, value):
            """是否包含value"""
            index = self.__bisect_left(value)
            return (index < len(self.__list) and
                    self.__list[index] == value)
        def clear(self):
            self.__list = []
    
        def pop(self, index=-1):
            return self.__list.pop(index)
    
        def __len__(self):
            return len(self.__list)
    
        def __str__(self):
            return str(self.__list)
    
        def copy(self):
            return SortedList(self, self.__key)
    
        __copy__ = copy #其意义在于,使得copy.copy()也将调用copy()方法
    
    
    
    if __name__ == "__main__":
        import doctest
        doctest.testmod()
    

    6.3.3 使用继承创建组合类

    """A dictionary that is sorted by < over its keys or by < over
    the result of the key function applied to the keys
    
    These are tests for inherited methods that aren't reimplemented
    >>> d = SortedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6))
    >>> d["i"]
    4
    >>> d["y"]
    6
    >>> d["z"]
    Traceback (most recent call last):
    ...
    KeyError: 'z'
    >>> d = SortedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6))
    >>> d.get("X", 21)
    21
    >>> d.get("i")
    4
    >>> d = SortedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6))
    >>> "a" in d
    True
    >>> "x" in d
    False
    >>> d = SortedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6))
    >>> len(d)
    6
    >>> del d["n"]
    >>> del d["y"]
    >>> len(d)
    4
    >>> d.clear()
    >>> len(d)
    0
    >>> d = SortedDict(dict(V=1, E=2, I=3, N=4, S=5))
    >>> str(d)
    "{'E': 2, 'I': 3, 'N': 4, 'S': 5, 'V': 1}"
    """
    
    
    from practice import SortedList
    
    
    class SortedDict(dict):
    
        def __init__(self, dictionary=None, key=None, **kwargs):
            """Initializes with a shallow copy of the given dictionary
                   and/or with keyword key=value pairs and preserving order using
                   the key function. All keys must be unique.
    
                   key is a key function which defaults to the identity
                   function if it is not specified
    
                   >>> d = SortedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6))
                   >>> list(d.items())
                   [('a', 2), ('i', 4), ('n', 3), ('s', 1), ('t', 5), ('y', 6)]
                   >>> dict(SortedDict())
                   {}
                   >>> e = SortedDict(d)
                   >>> list(e.items())
                   [('a', 2), ('i', 4), ('n', 3), ('s', 1), ('t', 5), ('y', 6)]
                   >>> dict(e)
                   {'a': 2, 'i': 4, 'n': 3, 's': 1, 't': 5, 'y': 6}
                   >>> f = SortedDict(key=str.lower, S=1, a=2, n=3, I=4, T=5, y=6)
                   >>> dict(f)
                   {'a': 2, 'I': 4, 'n': 3, 'S': 1, 'T': 5, 'y': 6}
                   """
            dictionary = dictionary or {}
            super().__init__(dictionary)
            if kwargs:
                super().update(kwargs)
            self.__keys = SortedList.SortedList(super().keys(), key)
    
        def update(self, dictionary=None, **kwargs):
            if dictionary is None:
                pass
            elif isinstance(dictionary, dict):
                super().update(dictionary)
            else:
                for key, value in dictionary.items():#如果没有提供items方法,AttributeError
                    super().__setitem__(key, value)
            if kwargs:
                super().update(kwargs)
            self.__keys = SortedList.SortedList(super().keys(), self.__keys.key)
    
        @classmethod #类方法 可以调用类属性ClassCase.classmethod() 会自动传入cls
        def fromkeys(cls, iterable, value=None, key=None):
            return cls({k: value for k in iterable}, key)
    
        def __setitem__(self, key, value):
            if key not in self:
                self.__keys.add(key)
            return super().__setitem__(key, value)
    
        def __delitem__(self, key):
            try:
                self.__keys.remove(key)
            except ValueError:
                raise KeyError(key)
            return super().__delitem__(key)
    
        def setdefault(self, key, value=None):
            if key not in self:
                self.__keys.add(key)
            return super().setdefault(key, value)
    
        def pop(self, key, *args):
            """
            d.pop(k)
            d.pop(k, value)
            :param key:
            :param args:
            :return:
            """
            if key not in self:
                if len(args) == 0:
                    raise KeyError(key)
                return args[0]
            self.__keys.remove(key)
            return super().pop(key, args)
    
        def popitem(self):
            """
            移除并返回字典中一个随机的键-值对
            :return:
            """
            item = super().popitem()
            self.__keys.remove(item[0])
            return item
    
        def clear(self):
            super().clear()
            self.__keys.clear()
    
        def values(self):
            """
            返回的是一个迭代子
            :return:
            """
            for key in self.__keys:
                yield self[key]
    
        def items(self):
            """
            迭代子
            :return:
            """
            for key in self.__keys:
                yield key, self[key]
    
        def __iter__(self):
            return iter(self.__keys)
    
        keys = __iter__ #相同功效
    
        def __repr__(self):
            """不能eval()的表象形式"""
            return object.__repr__(self)
    
        def __str__(self):
            return "{" + ", ".join(["{0!r}: {1!r}".format(k, v)
                                    for k, v in self.items()]) + "}"
    
        def copy(self):
            """
            不带参数的时候,super()将针对基类与对象进行工作。
            这里我们显示地传递类与对象
            :return:
            """
            d = SortedDict()
            super(SortedDict, d).update(self) # == dict.update(d, self) | d.update(self)
            d.__keys = self.__keys.copy()
            return d
    
        __copy__ = copy
    
        def value_at(self, index):
            """因为这是有序地dict所以可以根据位置来获取"""
            return self[self.__keys[index]]
    
        def set_value_at(self, index, value):
            self[self.__keys[index]] = value
    
    
    
    if __name__ == "__main__":
        import doctest
        doctest.testmod()
    

    静态方法与类方法

    静态方法:

    class foo:
        @staticmethod
        def f():
            print("Q!!!!")
            
    f = foo()
    foo.f(), f.f()
    

    类方法:

    class foo:
        @classmethod
        def f(cls):
            print("Q!!!!")
            
    f = foo()
    foo.f(), f.f()
    

    上面的输出都是:

    Q!!!!
    Q!!!!
    (None, None)
    

    @staticmethod是修饰器,具体如何实现我不知道,但是,如果像下面一样定义foo:

    class foo:
        def f():
            print("Q!!!!")
    

    执行foo.f() 没有问题
    但是执行f.f()的时候就有问题了,

    TypeError                                 Traceback (most recent call last)
    <ipython-input-54-4591815b19b5> in <module>
    	4 
     	5 f = foo()
    ----> 6 foo.f(), f.f()
    TypeError: f() takes 0 positional arguments but 1 was given
    

    大概是Python在执行的时候,默认当期为实例方法,会把实例作为第一个参数传入而报错。@staticmethod的作用大概就是修正这一点。
    而@classmethod的作用则是,无论是通过类还是实例调用方法,都只会把类作为第一个参数传入。这大概就是修饰器的作用所在。修饰器的强大可见一斑。

    6.5 练习

    import pickle
    
    class AccountError(Exception): pass
    class SaveError(AccountError): pass
    class LoadError(AccountError): pass
    
    class Transaction:
        """
        实现一个Transaction类
    
        >>> t = Transaction(100, "2019-2-18", "RMB", 0.1476, "Go forward...")
        >>> t.amount
        100
        >>> t.date
        '2019-2-18'
        >>> t.currency
        'RMB'
        >>> t.usd_conversion_rate
        0.1476
        >>> t.description
        'Go forward...'
        """
        def __init__(self, amount, date, currency="USD",
                     usd_conversion_rate=1, description=None):
            """
            属性均为私有
            :param amount:
            :param date:
            :param currency: 默认为"USD",U.S. dollars
            :param usd_conversion_rate: 默认为1
            :param description: 默认为None
            """
            self.__amount = amount
            self.__date = date
            self.__currency = currency
            self.__usd_conversion_rate = usd_conversion_rate
            self.__description = description
    
        @property
        def amount(self):
            return self.__amount
    
        @property
        def date(self):
            return self.__date
    
        @property
        def currency(self):
            return self.__currency
    
        @property
        def usd_conversion_rate(self):
            return self.__usd_conversion_rate
    
        @property
        def description(self):
            return self.__description
    
        @property
        def usd(self):
            return self.__amount * self.__usd_conversion_rate
    
    
    
    class Account:
        """
        >>> import os
        >>> import tempfile
        >>> name = os.path.join(tempfile.gettempdir(), "account01")
        >>> account = Account(name, "Qtrac Ltd.")
        >>> os.path.basename(account.number), account.name,
        ('account01', 'Qtrac Ltd.')
        >>> account.balance, account.all_usd, len(account)
        (0.0, True, 0)
        >>> account.apply(Transaction(100, "2008-11-14"))
        >>> account.apply(Transaction(150, "2008-12-09"))
        >>> account.apply(Transaction(-95, "2009-01-22"))
        >>> account.balance, account.all_usd, len(account)
        (155.0, True, 3)
        >>> account.apply(Transaction(50, "2008-12-09", "EUR", 1.53))
        >>> account.balance, account.all_usd, len(account)
        (231.5, False, 4)
        >>> account.save()
        >>> newaccount = Account(name, "Qtrac Ltd.")
        >>> newaccount.balance, newaccount.all_usd, len(newaccount)
        (0.0, True, 0)
        >>> newaccount.load()
        >>> newaccount.balance, newaccount.all_usd, len(newaccount)
        (231.5, False, 4)
        >>> try:
        ...     os.remove(name + ".acc")
        ... except EnvironmentError:
        ...     pass
        """
        def __init__(self, number, name):
            self.__number = number
            self.__name = name
            self.__transactions = []
    
        @property
        def number(self):
            return self.__number
    
        @property
        def name(self):
            return self.__name
    
        @name.setter
        def name(self, name):
            assert isinstance(name, str) and len(name) > 3, 
                "name must be string whose length >= 4"
            self.__name = name
    
        def __len__(self):
            return len(self.__transactions)
    
        @property
        def balance(self):
            """
            交易额  单位USD
            :return:
            """
            total = 0.0
            for transaction in self.__transactions:
                total += transaction.usd
            return total
    
        @property
        def all_usd(self):
            """是否均为USD"""
            for transaction in self.__transactions:
                if transaction.currency is not "USD":
                    return False
            return True
    
        def apply(self, transaction):
            if not isinstance(transaction, Transaction):
                raise TypeError("{0} is not Transaction".format(transaction))
            self.__transactions.append(transaction)
    
    
        def save(self):
            """数据保存为number.acc"""
            fh = None
            try:
                data = [self.__number, self.__name, self.__transactions]
                fh = open(self.__number + ".acc", "wb")
                pickle.dump(data, fh, pickle.HIGHEST_PROTOCOL)
            except (EnvironmentError, pickle.PicklingError) as err:
                raise SaveError(str(err))
    
            finally:
                if fh is not None:
                    fh.close()
    
        def load(self):
            """加载数据,原有数据会被覆盖"""
            fh = None
            try:
                fh = open(self.__number + ".acc", "rb")
                data = pickle.load(fh)
                assert self.__number == data[0], "not match"
                self.__name, self.__transactions = data[1:]
            except (EnvironmentError, pickle.UnpicklingError) as err:
                raise LoadError(str(err))
            finally:
                if fh is not None:
                    fh.close()
    
    
    
    if __name__ == "__main__":
        import doctest
        doctest.testmod()
    
    
  • 相关阅读:
    zoj 1239 Hanoi Tower Troubles Again!
    zoj 1221 Risk
    uva 10192 Vacation
    uva 10066 The Twin Towers
    uva 531 Compromise
    uva 103 Stacking Boxes
    稳定婚姻模型
    Ants UVA
    Golden Tiger Claw UVA
    关于upper、lower bound 的探讨
  • 原文地址:https://www.cnblogs.com/MTandHJ/p/10528186.html
Copyright © 2011-2022 走看看