资讯专栏INFORMATION COLUMN

编写符合Python风格的对象

ethernet / 1414人阅读

摘要:自定义向量类型从自定义向量类型入手写出符合风格的对象,这离不开特殊方法的支持。将对象定为不可变的通过使用两个前导下划线。程序员约定使用一个下划线前缀编写受保护的属性即,他们认为应该使用命名约定来避免意外覆盖属性。

导语:本文章记录了本人在学习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"
#其它方法实现省略

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41262.html

相关文章

  • 如何用PEP 8编写优雅Python代码

    摘要:如果需要在二元运算符周围做换行操作,例如和,那么需要将换行操作放在前面,这条规则源于数学,数学家同意在二元运算符之前换行以可提高可读性,比较以下两个例子。在二元运算符之前换行可以让代码更加具有可读性,所鼓励这种方式。 原文地址:How to Write Beautiful Python Code With PEP 8 作者:Jasmine Finer 翻译:howie6879 ...

    seanHai 评论0 收藏0
  • Python学习之路28-符合Python风格对象

    摘要:本篇继续学习之路,实现更多的特殊方法以让自定义类的行为跟真正的对象一样。之所以要让向量不可变,是因为我们在计算向量的哈希值时需要用到和的哈希值,如果这两个值可变,那向量的哈希值就能随时变化,这将不是一个可散列的对象。 《流畅的Python》笔记。本篇是面向对象惯用方法的第二篇。前一篇讲的是内置对象的结构和行为,本篇则是自定义对象。本篇继续Python学习之路20,实现更多的特殊方法以让...

    Eric 评论0 收藏0
  • 流畅python读书笔记-第九章-符合Python风格对象

    摘要:以便于用户理解的方式返回对象的字符串表示形式。函数会调用函数,对来说,输出的是一个有序对。此外,还有用于支持内置的构造函数的方法。可散列实现了方法,使用推荐的异或运算符计算实例属性的散列值私有属性最好用命名规则来实现这种方式有好有坏 绝对不要使用两个前导下划线,这是很烦人的自私行为。——Ian Bicking 对象表示形式 repr()  以便于开发者理解的方式返回对象的字符串表示形式...

    fai1017 评论0 收藏0
  • Python序列修改、散列和切片

    摘要:一基本的序列协议首先,需要就维向量和二维向量的显示模的计算等差异重新调整。假设维向量最多能处理维向量,访问向量分量的代码实现如下若传入的参数在备选分量中可进行后续处理判断分量的位置索引是否超出实例的边界不支持非法的分量访问,抛出。 导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、了解协议的...

    jubincn 评论0 收藏0
  • Python基础之(七)函数

    摘要:函数建立函数在中,规定了一种定义函数的格式,下面的举例就是一个函数,以这个函数为例来说明定义函数的格式和调用函数的方法。返回值所谓返回值,就是函数向调用函数的地方返回的数据。 函数 建立函数 在Python中,规定了一种定义函数的格式,下面的举例就是一个函数,以这个函数为例来说明定义函数的格式和调用函数的方法。 def add_function(a, b): #冒号必须 c = ...

    microelec 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<