当前位置: 首页 > news >正文

编写符合Python风格的对象

导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。

本文重点:

1、掌握编写Pythonic code背后常用的特殊方法;
2、掌握可扩展的格式化输出方法;
3、了解可散列对象的设置以及节省内存的__slots__对象。

一、自定义具有Python风格的类

自定义的向量类需要支持基本的输出,迭代,求模。

1、自定义向量类型

从自定义向量类型入手写出符合Python风格的对象,这离不开特殊方法的支持。
我们期望的自定义向量类型应支持的基本功能:

  • 构造,__init__
  • 输出,__repr__和__str__
  • 迭代,__iter__
  • 求模,__abs__
  • 转化为字节序列,__bytes__

代码实现如下:

import math
from array import array
class Vector2d:
    typecode='d'
    def __init__(self,x,y):
        self.x=float(x)
        self.y=float(y)

    def __str__(self):
        return str(tuple(self))

    def __iter__(self):
        return (i for i in (self.x,self.y))

    def __repr__(self):
        classname=type(self).__name__
        s="{}({},{})".format(classname,*self)
        return s

    def __abs__(self):
        return math.hypot(self.x,self.y)

    def __bytes__(self):
        return (bytes(self.typecode,encoding='utf-8')+
                bytes(array(self.typecode,self)))

2、使用一个类方法实现备选构造方法

我们能将实例转化为字节序列,那么也应构造一个将实例转化为字节序列的方法。

    @classmethod
    def frombytes(cls,seqs):
        typecode=chr(seqs[0])
        memv=memoryview(seqs[1:]).cast(typecode)
        return cls(*memv)

memoryview是泛化和去数学化的数组。

3、classmethod和staticmethod两个装饰器

classmethod:定义操作类而不是操作实例的方法,类方法的第一个参数是类本身而不是实例。最常见的用途是定义备选构造方法(返回cls(*))

staticmethod:是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义。

二、格式化显示

1、扩展内置的format函数

通过改写format背后的__format__可以写出可扩展的格式。
实例1:实现format对向量类的处理

     def __format__(self,fmt_spec=''):
        components=(format(v,fmt_spec)for v in self)
        return "({},{})".format(*components)

实例2:通过尾部自定义格式代码p实现将直角坐标向量转化为极坐标向量。

    def __format__(self,fmt_spec=''):
        if fmt_spec[-1]=="p":
            coord=(abs(self),self.angle())
            spec=fmt_spec[:-1]
            components=(format(v,spec)for v in coord)
            outer="<{},{}>"
        else:
            coord=self
            components = (format(v, fmt_spec) for v in self)
            outer = "({},{})"
        return outer.format(*components)

本段代码的重点在于判断格式中是否存在自定义格式符p,并进行对应的格式处理。

三、将对象变为可散列的

目前的向量是不可散列的,而可散列对象需要满足:

(1)支持hash()函数,并且通过hash()得到的散列值是不变的;
(2)支持通过__eq__()方法来检测相等性;
(3)若a==b为真,则hash(a)=hash(b)也为真。

所以我们需要把对象定为不可变,然后自定义__hash__。

1、将对象定为不可变的

通过使用两个前导下划线。将属性标记为私有的。

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

2、自定义__hash__()

使用异或运算符实现。

    def __hash__(self):
        return hash(self.x)^hash(self.y)

四、其它

1、只读属性的设置

  • 私有属性的设置只是避免修改方法意外访问不应更改的值,而无法防止有意的改动。
  • 通过__dict__属性可以查询Python如何存储向量的属性名,然后只要编写a._Vector2d__x=5这样的代码就会恶意赋值。
  • Python程序员约定使用一个下划线前缀编写“受保护”的属性即self._x,他们认为应该使用命名约定来避免意外覆盖属性。

2、利用__slots__节省内存

默认情况下,Python在各个实例中名为__dict__的字典中储存实例属性,相应地会消耗大量内存。
通过__slots__类属性,并让解释器把实例属性存储在元组中,可以节省大量内存。

class Vector2d:
    __slots__ = ('__x','__y')
    typecode='d'
#其他方法实现省略

使用__slots__应注意的问题:

  • __slots__无法从超类继承而来,每个子类都需要定义__slots__属性;
  • 实例只能拥有__slots__中列出的属性,除非把'__dict__'加入到__slots__中(这样做就失去了节省内存的初衷)
  • 如果不把'weakref__'加入__slots__,实例就不能作为弱引用的目标。

当处理的实例规模较小时,禁止创建动态属性或不支持弱引用是比较好的选择。

3、覆盖类属性

通过创建子类可以把继承自父类的实例属性覆盖掉。

class Shortvector2d(Vector2d):
    typecode = 'f'
#其它方法实现省略

相关文章:

  • 二叉树基础之序列化和反序列化二叉树
  • 数组作业
  • Linux进程管理
  • Spring系列之-Aware系列接口
  • 如何正确配置 Ubuntu 14.04 服务器?
  • JDK 6和JDK 7中的substring()方法
  • 使用事件和消息队列实现分布式事务(转+补充)
  • JFinal极速开发框架使用笔记(三) 分析Model和ActiveRecord
  • 3138 栈练习2
  • innerHTML、html('')与empty在IE上不同的区别
  • 配置tomcat监听80端口、配置tomcat虚拟机、tomcat日志
  • 关于Docker的一些常识
  • linux下tar、zip 压缩文件不带文件路径
  • 【Amaple教程】5. 插件
  • 数值的整数次方
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 2017-08-04 前端日报
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Apache的80端口被占用以及访问时报错403
  • JavaScript标准库系列——Math对象和Date对象(二)
  • sessionStorage和localStorage
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • vue 配置sass、scss全局变量
  • Vue2.0 实现互斥
  • windows下使用nginx调试简介
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 技术胖1-4季视频复习— (看视频笔记)
  • 精彩代码 vue.js
  • 前端临床手札——文件上传
  • 如何使用 JavaScript 解析 URL
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 怎样选择前端框架
  • 【云吞铺子】性能抖动剖析(二)
  • 数据可视化之下发图实践
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​ssh免密码登录设置及问题总结
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (TOJ2804)Even? Odd?
  • (vue)页面文件上传获取:action地址
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (四)模仿学习-完成后台管理页面查询
  • (转)创业的注意事项
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .dwp和.webpart的区别
  • .NET Framework杂记
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .net 验证控件和javaScript的冲突问题
  • .xml 下拉列表_RecyclerView嵌套recyclerview实现二级下拉列表,包含自定义IOS对话框...
  • @ModelAttribute使用详解
  • [ 常用工具篇 ] POC-bomber 漏洞检测工具安装及使用详解
  • [2016.7 day.5] T2
  • [android] 看博客学习hashCode()和equals()