符合python对象的风格

对象表示形式

Python提供了两种方式。
repr()
以便于开发者理解的方式返回对象的字符串表示形式。

str()
以便于用户理解的方式返回对象的字符串表示形式。

正如你所知,我们要实现__repr__和__str__特殊方法,为repr()和str()提供支持。

再谈向量类

#示例1:Vector2d实例有多种表示形式
from array import array
import math
class Vector2d:
    typecode = 'd'  
    def __init__(self, x, y):
        self.x = float(x)   
        self.y = float(y)
    def __iter__(self):
        return (i for i in (self.x, self.y))  
    def __repr__(self):
        class_name = type(self).__name__
        return '{}({!r}, {!r})'.format(class_name, *self)  
    def __str__(self):
        return str(tuple(self)) 
>>> v1 = Vector2d(3, 4) 
>>> print(v1.x, v1.y)  #➊ 
3.0 4.0 
>>> x, y = v1  #➋ 
>>> x, y 
(3.0, 4.0) 
>>> v1  #➌ 
Vector2d(3.0, 4.0) 
>>> v1_clone = eval(repr(v1))  #➍ 
>>>> v1 == v1_clone  #➎ 
True 
>>> print(v1)  #➏ 
(3.0, 4.0) 
>>> octets = bytes(v1)  #➐ 
>>> octets 
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@' 
>>> abs(v1)  #➑ 
5.0 
>>> bool(v1), bool(Vector2d(0, 0))  #➒ 
(True, False)

➊ Vector2d实例的分量可以直接通过属性访问(无需调用读值方法)。
➋ Vector2d实例可以拆包成变量元组。
➌ repr函数调用Vector2d实例,得到的结果类似于构建实例的源码。
➍ 这里使用eval函数,表明repr函数调用Vector2d实例得到的是对构造方法的准确表述。
➎ Vector2d实例支持使用==比较;这样便于测试。
➏ print函数会调用str函数,对Vector2d来说,输出的是一个有序对。
➐ bytes函数会调用__bytes__方法,生成实例的二进制表示形式。
➑ abs函数会调用__abs__方法,返回Vector2d实例的模。
➒ bool函数会调用__bool__方法,如果Vector2d实例的模为零,返回False,否则返回True。

#示例2:目前定义的都是特殊方法
from array import array 
import math 
 
 
class Vector2d: 
    typecode = 'd'  #➊ 
 
    def __init__(self, x, y): 
        self.x = float(x)   #➋ 
        self.y = float(y) 
 
    def __iter__(self): 
        return (i for i in (self.x, self.y))  #➌ 
 
    def __repr__(self): 
        class_name = type(self).__name__ 
        return '{}({!r}, {!r})'.format(class_name, *self)  #➍ 
 
    def __str__(self): 
        return str(tuple(self))  #➎ 
        def __bytes__(self): 
        return (bytes([ord(self.typecode)]) +   #➏ 
                bytes(array(self.typecode, self)))  #➐ 
 
    def __eq__(self, other): 
        return tuple(self) == tuple(other)  #➑ 
 
    def __abs__(self): 
        return math.hypot(self.x, self.y)  #➒ 
 
    def __bool__(self): 
        return bool(abs(self))  #➓

➊ typecode是类属性,在Vector2d实例和字节序列之间转换时使用。
➋ 在__init__方法中把x和y转换成浮点数,尽早捕获错误,以防调用Vector2d函数时传入不当参数。
➌ 定义__iter__方法,把Vector2d实例变成可迭代的对象,这样才能拆包(例如,x, y = my_vector)。这个方法的实现方式很简单,直接调用生成器表达式一个接一个产出分量。
➍ __repr__方法使用{!r}获取各个分量的表示形式,然后插值,构成一个字符串;因为Vector2d实例是可迭代的对象,所以*self会把x和y分量提供给format函数。
➎ 从可迭代的Vector2d实例中可以轻松地得到一个元组,显示为一个有序对。
➏ 为了生成字节序列,我们把typecode转换成字节序列,然后……
➐ ……迭代Vector2d实例,得到一个数组,再把数组转换成字节序列。
➑ 为了快速比较所有分量,在操作数中构建元组。对Vector2d实例来说,可以这样做,不过仍有问题。参见下面的警告。
➒ 模是x和y分量构成的直角三角形的斜边长。
➓ __bool__方法使用abs(self)计算模,然后把结果转换成布尔值,因此,0.0是False,非零值是True。

备选构造方法

我们可以把Vector2d实例转换成字节序列了;同理,也应该能从字节序列转换成Vector2d实例。在标准库中探索一番之后,我们发现array.array有个类方法.frombytes正好符合需求。

#示例3:这段代码只列出了frombytes类方法
@classmethod  #➊ 
    def frombytes(cls, octets):  #➋ 
        typecode = chr(octets[0])  #➌ 
        memv = memoryview(octets[1:]).cast(typecode)  #➍ 
        return cls(*memv)  #➎

➊ 类方法使用classmethod装饰器修饰。
➋ 不用传入self参数;相反,要通过cls传入类本身。
➌ 从第一个字节中读取typecode。
➍ 使用传入的octets字节序列创建一个memoryview,然后使用typecode转换。
➎ 拆包转换后的memoryview,得到构造方法所需的一对参数。

classmethod与staticmethod

先来看classmethod。示例3展示了它的用法:定义操作类,而不是操作实例的方法。
classmethod改变了调用方法的方式,因此类方法的第一个参数是类本身,而不是实例。
classmethod最常见的用途是定义备选构造方法

staticmethod装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义。

示例4对classmethod和staticmethod的行为做了对比。

#示例4:比较classmethod和staticmethod的行为
>>> class Demo: 
...     @classmethod 
...     def klassmeth(*args): 
...         return args  # ➊ 
...     @staticmethod 
...     def statmeth(*args): 
...         return args  # ➋ 
... 
>>> Demo.klassmeth()  # ➌ 
(<class '__main__.Demo'>,) 
>>> Demo.klassmeth('spam') 
(<class '__main__.Demo'>, 'spam') 
>>> Demo.statmeth()   # ➍ 
() 
>>> Demo.statmeth('spam') 
('spam',)

➊ klassmeth返回全部位置参数。
➋ statmeth也是。
➌ 不管怎样调用Demo.klassmeth,它的第一个参数始终是Demo类。
➍ Demo.statmeth的行为与普通的函数相似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值