IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python 部分特殊方法的使用 -> 正文阅读

[Python知识库]Python 部分特殊方法的使用

摘自流畅的python 第九章 符合Python风格的对象

什么是特殊方法

它们在面向对象的Python的处处皆是。它们是一些可以让你对类添加“魔法”的特殊方法。它们经常是两个下划线包围来命名的(比如 __init__ , __lt__ )


  • 接下来让我们在代码中了解特殊方法的妙用

vector2d_v3_slots.py

我们封装一个类 用于描述矢量 ,该类有两个私有属性 x,y 分表表示坐标

class Vector2d:

    def __init__(self, x, y):
        # 在__init__方法中把x和y转换成浮点数,尽早捕获错误,以防调用Vector2d函数时传入不当参数
        self.__x = float(x)  # 使用两个前导下划线(尾部没有下划线,或者有一个下划线),把属性标记为私有
        self.__y = float(y)
        
    @property  # 该装饰器把读值方法标记为特性
    def x(self):  # 读值方法与公开属性同名,都是x
        '''
        该装饰器装饰的变量 需要单独重构赋值方式,如果未重构将会出现一下报错 。 在此示例中 x 变量只能访问
        a = Vector2d(1,1)
        print(a.x)
        a.x = 2
        1.0
        Traceback (most recent call last):
          File "/Users/coco/PycharmProjects/FluentPython/09-pythonic-obj/vector2d_v3.py", line 158, in <module>
            a.x = 2
        AttributeError: can't set attribute
        :return:
        '''
        return self.__x  # 直接返回self.__x

    @property
    def y(self):
        return self.__y

__iter__

    def __iter__(self):
        '''
        定义 :使该对象可迭代
        示例 :
        a = Vector2d(1,1)
        x,y = a
        print(x,y)
        1.0 1.0
        print(*a)
        1.0 1.0
        :return:
        '''
        # 定义__iter__方法,把Vector2d实例变成可迭代对象,这样才能拆包(例如,x,y = my_vector)。这个方法的实现方式很简单,直接调用生成器表达式一个接一个产出分量
        return (i for i in (self.x, self.y))

__repr__

    def __repr__(self):
        '''
        定义 :重构交互模式下打印 (例如:Python Console下的打印)
        示例 :
        在Python Console 中定义如下变量
        >>> a = Vector2d(1,1)
        >>> a
        Vector2d(1.0,1.0)
        与 __str__ 回显并不相同
        当 __str__ 方法未重构时
        print() 会调用__repr__
        :return:
        '''
        class_name = type(self).__name__
        # __repr__方法使用{!r}获取各个分量的表示形式,然后插值,构成一个字符串;因为Vector2d实例是可迭代的对象,所以*self会把x和y分量提供给format函数
        return '{}({!r},{!r})'.format(class_name, *self)

__str__

    def __str__(self):
        '''
        定义 :重构用户打印
        示例:
        分别在实现与实现的情况下 运行这条语句 print(Vector2d(1,1))
        不实现该方法时 打印如下
        <__main__.Vector2d object at 0x7ffc726e8fa0>
        实现该方法时 打印如下
        (1.0, 1.0)
        :return:
        '''
        return str(tuple(self))  # 从可迭代的Vector2d实例中可以轻松地得到一个元祖,显示为一个有序对

__bytes__

    def __bytes__(self):
        '''
        定义 : 重构bytes() 方法 返回字节序列的逻辑
        打印 :
        print(bytes(Vector2d(1,1)))
        b'd\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?'
        :return:
        '''
        # 为了生成字节序列,我们typecode转换成字节序列,然后
        return bytes([ord(self.typecode)]) + \
               bytes(array(self.typecode, self))  # 迭代Vector2d实例,得到一个数组,再把数组转换成字节序列

__eq__

    def __eq__(self, other):
        '''
        定义 :重构 == 运算逻辑
        示例 :
        print(Vector2d(1,1) == Vector2d(1,1))
        True
        print(Vector2d(1,1) == Vector2d(1,2))
        False
        :param other:
        :return:
        '''
        # 为了快速比较所有分量,在操作数中构建元祖。对Vector2d实例来说,可以这样做,不过仍有问题。参见下面的警告
        # 该方法将自身转换成元祖与比较对象比较,在进行如下比较时 也会输出True,这显然是不符合预期的,可以在方法中添加对象类型的判断,这样更严谨
        # print(Vector2d(1, 1) == (1, 1))
        return tuple(self) == tuple(other)

__abs__

 def __abs__(self):
        '''
        定义 : 重构abs() 判断逻辑
        示例 :
        print(abs(Vector2d(1,1)))
        1.4142135623730951
        :return:
        '''
        return math.hypot(self.x, self.y)  # 模是x和y分量构成的直角三角形的斜边长

__bool__

 def __bool__(self):
        '''
        定义 : 重构bool() 判断逻辑
        示例 :
        print(bool(Vector2d(1,1)))
        True
        :return:
        '''
        return bool(abs(self))  # __bool__方法使用abs(self)计算模,然后把结果转换成布尔值,因此,0.0是False,非零值是True

__fotmat__

    def __format__(self, format_spec):
        '''
        定义 : 重构format() 运行逻辑
        示例 :
        print(format(Vector2d(1,1),'p'))
        <1.4142135623730951, 0.7853981633974483>
        print(format(Vector2d(1,1),'.3ep'))
        <1.414e+00, 7.854e-01>
        print(format(Vector2d(1,1),'.5fp'))
        <1.41421, 0.78540>
        :param format_spec:
        :return:
        '''
        if format_spec.endswith('p'):  # 如果格式代码以'p'结尾,使用极坐标
            format_spec = format_spec[:-1]  # 从fmt_spec中删除'p'后缀
            coords = (abs(self), self.angle())  # 构建一个元祖,表示极坐标:(magnitude,angle)
            outer_fmt = '<{}, {}>'  # 把外层格式设为一对尖括号
        else:
            coords = self  # 如果不以'p'结尾,使用self的x和y分量构建直角坐标
            outer_fmt = '({}, {})'  # 把外层格式设为一对圆括号
        components = (format(c, format_spec) for c in coords)  # 使用各个分量生成可迭代的对象,构成格式化字符串
        return outer_fmt.format(*components)  # 把格式化字符串代入外层格式

__hash__

    def __hash__(self):
        '''
        定义 : 试该对象可散列
        示例 :
        v1 = Vector2d(3,4)
        v2 = Vector2d(3.1,4.2)
        print(hash(v1))
        print(hash(v2))
        print(set([v1,v2]))
        7
        384307168202284039
        {Vector2d(3.1,4.2), Vector2d(3.0,4.0)}

        :return:
        '''
        return hash(self.x) ^ hash(self.y)

小结

以上使用的特殊方法和约定的机构,定义了行为良好且符合Python风格的类

  • 用于获取字符串和字节序列表示形式的方法:__repr__,__str__,__format__和__bytes__
  • 把对象转换成数字的方法:__abs__,__bool__,__hash__
  • 用于测试字节序列转换和支持散列(连同__hash__方法)的__eq__运算符

完整代码


from array import array
import math


class Vector2d:
    typecode = 'd'  # typecode 是类属性,在Vector2d实例和字节序列之间转换时使用
    # 在类中定义__slots__属性的目的是告诉解释器:这个类多有的实例属性都在这儿了,Python 会在各个实例中使用类似元祖的结构存储实例变量,
    # 从而避免使用消耗内存的__dict__属性。如果有百万个实例同时活动,这样做能节省大量内存
    __slots__ = ('__x', '__y')

    def __init__(self, x, y):
        # 在__init__方法中把x和y转换成浮点数,尽早捕获错误,以防调用Vector2d函数时传入不当参数
        self.__x = float(x)  # 使用两个前导下划线(尾部没有下划线,或者有一个下划线),把属性标记为私有
        self.__y = float(y)

    @property  # 该装饰器把读值方法标记为特性
    def x(self):  # 读值方法与公开属性同名,都是x
        '''
        该装饰器装饰的变量 需要单独重构赋值方式,如果未重构将会出现一下报错 。 在此示例中 x 变量只能访问
        a = Vector2d(1,1)
        print(a.x)
        a.x = 2
        1.0
        Traceback (most recent call last):
          File "/Users/coco/PycharmProjects/FluentPython/09-pythonic-obj/vector2d_v3.py", line 158, in <module>
            a.x = 2
        AttributeError: can't set attribute
        :return:
        '''
        return self.__x  # 直接返回self.__x

    @property
    def y(self):
        return self.__y

    def __iter__(self):
        '''
        定义 :使该对象可迭代
        示例 :
        a = Vector2d(1,1)
        x,y = a
        print(x,y)
        1.0 1.0
        print(*a)
        1.0 1.0
        :return:
        '''
        # 定义__iter__方法,把Vector2d实例变成可迭代对象,这样才能拆包(例如,x,y = my_vector)。这个方法的实现方式很简单,直接调用生成器表达式一个接一个产出分量
        return (i for i in (self.x, self.y))

    def __repr__(self):
        '''
        定义 :重构交互模式下打印 (例如:Python Console下的打印)
        示例 :
        在Python Console 中定义如下变量
        >>> a = Vector2d(1,1)
        >>> a
        Vector2d(1.0,1.0)
        与 __str__ 回显并不相同
        当 __str__ 方法未重构时
        print() 会调用__repr__
        :return:
        '''
        class_name = type(self).__name__
        # __repr__方法使用{!r}获取各个分量的表示形式,然后插值,构成一个字符串;因为Vector2d实例是可迭代的对象,所以*self会把x和y分量提供给format函数
        return '{}({!r},{!r})'.format(class_name, *self)

    def __str__(self):
        '''
        定义 :重构用户打印
        示例:
        分别在实现与实现的情况下 运行这条语句 print(Vector2d(1,1))
        不实现该方法时 打印如下
        <__main__.Vector2d object at 0x7ffc726e8fa0>
        实现该方法时 打印如下
        (1.0, 1.0)
        :return:
        '''
        return str(tuple(self))  # 从可迭代的Vector2d实例中可以轻松地得到一个元祖,显示为一个有序对

    def __bytes__(self):
        '''
        定义 : 重构bytes() 方法 返回字节序列的逻辑
        打印 :
        print(bytes(Vector2d(1,1)))
        b'd\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?'
        :return:
        '''
        # 为了生成字节序列,我们typecode转换成字节序列,然后
        return bytes([ord(self.typecode)]) + \
               bytes(array(self.typecode, self))  # 迭代Vector2d实例,得到一个数组,再把数组转换成字节序列

    def __eq__(self, other):
        '''
        定义 :重构 == 运算逻辑
        示例 :
        print(Vector2d(1,1) == Vector2d(1,1))
        True
        print(Vector2d(1,1) == Vector2d(1,2))
        False
        :param other:
        :return:
        '''
        # 为了快速比较所有分量,在操作数中构建元祖。对Vector2d实例来说,可以这样做,不过仍有问题。参见下面的警告
        # 该方法将自身转换成元祖与比较对象比较,在进行如下比较时 也会输出True,这显然是不符合预期的,可以在方法中添加对象类型的判断,这样更严谨
        # print(Vector2d(1, 1) == (1, 1))
        return tuple(self) == tuple(other)

    def __abs__(self):
        '''
        定义 : 重构abs() 判断逻辑
        示例 :
        print(abs(Vector2d(1,1)))
        1.4142135623730951
        :return:
        '''
        return math.hypot(self.x, self.y)  # 模是x和y分量构成的直角三角形的斜边长

    def __bool__(self):
        '''
        定义 : 重构bool() 判断逻辑
        示例 :
        print(bool(Vector2d(1,1)))
        True
        :return:
        '''
        return bool(abs(self))  # __bool__方法使用abs(self)计算模,然后把结果转换成布尔值,因此,0.0是False,非零值是True

    def angle(self):
        # math.atan2 方法以弧度返回y / x的反正切
        return math.atan2(self.y, self.x)

    def __format__(self, format_spec):
        '''
        定义 : 重构format() 运行逻辑
        示例 :
        print(format(Vector2d(1,1),'p'))
        <1.4142135623730951, 0.7853981633974483>
        print(format(Vector2d(1,1),'.3ep'))
        <1.414e+00, 7.854e-01>
        print(format(Vector2d(1,1),'.5fp'))
        <1.41421, 0.78540>
        :param format_spec:
        :return:
        '''
        if format_spec.endswith('p'):  # 如果格式代码以'p'结尾,使用极坐标
            format_spec = format_spec[:-1]  # 从fmt_spec中删除'p'后缀
            coords = (abs(self), self.angle())  # 构建一个元祖,表示极坐标:(magnitude,angle)
            outer_fmt = '<{}, {}>'  # 把外层格式设为一对尖括号
        else:
            coords = self  # 如果不以'p'结尾,使用self的x和y分量构建直角坐标
            outer_fmt = '({}, {})'  # 把外层格式设为一对圆括号
        components = (format(c, format_spec) for c in coords)  # 使用各个分量生成可迭代的对象,构成格式化字符串
        return outer_fmt.format(*components)  # 把格式化字符串代入外层格式

    def __hash__(self):
        '''
        定义 : 试该对象可散列
        示例 :
        v1 = Vector2d(3,4)
        v2 = Vector2d(3.1,4.2)
        print(hash(v1))
        print(hash(v2))
        print(set([v1,v2]))
        7
        384307168202284039
        {Vector2d(3.1,4.2), Vector2d(3.0,4.0)}

        :return:
        '''
        return hash(self.x) ^ hash(self.y)

    @classmethod  # 类方法使用classmethod装饰器修饰
    def frombytes(cls, octets):  # 不用传入self参数;相反,要通过cls传入类本身
        typecode = chr(octets[0])  # 从第一个字节中读取typecode
        memv = memoryview(octets[1:]).cast(typecode)  # 使用传入的octets字节序列创建一个memoryview,然后使用typecode转换
        return cls(*memv)  # 拆包转换后的memoryview,得到构造方法所需的一对参数

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-02-05 21:41:53  更:2022-02-05 21:43:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 2:41:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码