什么才是具备Python风格的类

Python没有编译器,应该管我们安装的python环境叫做python的解释器,每一个python语句的执行,都有很多我们看不见的步骤,让我们把其中某些看不见的步骤利用起来,写一个具有python风格的类。
我将实现一个Vector2d类,这个类表示的就是一个二维的点。
参考《流畅的Python》一书第九章,9.1节-9.6节

一开始,我把最后的成品摆在这里,后面逐个逐个解释
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):
        # yield self.x;   yield self.y
        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))

    def angle(self):
        return math.atan2(self.y,self.x)

    def __format__(self, format_spec=''):
        if format_spec.endswith('p'):
            format_spec = format_spec[:-1]
            coords = (abs(self),self.angle())
            outer_fmt = '<{},{}>'
        #使用内置的 format 函数把 fmt_spec 应用到向量的各个分量上,构建一个可迭代的格式化字符串。
        components = (format(c,format_spec) for c in self)
        return '({},{})'.format(*components)

你会发现,这个类有大量下划线开头的函数,这些函数将完成各种基本功能

第一部分,关于python会自动调用的类方法

我们看这段代码

a = 'abcd'
class my_a():
    def __init__(self,words):
        self.words = words
print(a)
print(my_a('abcd'))
'''
abcd
<__main__.my_a object at 0x000001F9F9234CF8>
'''

第一行输出abcd是我们预期的,你是否奇怪为什么第二行会输出这样的东西呢,我认为是因为,在python中,若未指定,则类都会继承与一个基类,在这个基类中实现了__repr__方法,而调用print()的时候,就会去自动调用基类中的__repr__,我们尝试证明一下

a = 'abcd'
class my_a():
    def __init__(self,words):
        self.words = words

    def __repr__(self):
        return self.words
print(a)
print(my_a('abcd'))
'''
abcd
abcd
'''

当我在my_a类中重写__repr__方法,这时,print()方法就会调用当前类的__repr__方法,我们将成员变量abcd返回出去,看到这时输出和我们预想的一样。
Python有很多内置的方法,这些方法大多长__xxx__这样,两个双下划线位于开头和结尾,比如我们常见的__init__就是一个,只要我们在自定义的类中实现这些内置方法(或者叫重写更合理一点)就可以控制我们类的很多行为了

(这里要插个话,__str__也可以实现相同的作用,关于__str__和__repr__的知识有很多可以查看这篇博客了解,这里不赘述了

第二部分,我们的python风格的类

我将这个类中的每个方法逐一作解释

#使得该类的对象可迭代。了解过yield则知道,定义了yield的函数将变成生成器。当然,我们也可以利用return来实现
def __iter__(self):
        # yield self.x;   yield self.y
        return (i for i in(self.x,self.y))

'''
将具备如下特性
'''
tt = Vector2d(3,4)
for i in tt:
    print(i)
'''输出
3.0
4.0
'''
# 当执行repr()函数时默认调用的方法
def __repr__(self):
        class_name = type(self).__name__
        return '{}({!r},{!r})'.format(class_name,*self)

'''
将具备如下特性
'''
tt = Vector2d(3,4)
print(repr(tt))
'''输出
Vector2d(3.0,4.0)
'''
# 当执行str()函数时默认调用的方法
def __str__(self):
        return str(tuple(self))

'''
将具备如下特性
'''
tt = Vector2d(3,4)
print(str(tt))
'''输出
(3.0, 4.0)
'''
# 当执行bytes()函数时默认调用的方法,返回实例的二进制表示形式
def __bytes__(self):
        return (bytes[ord(self.typecode)] + bytes(array(self.typecode,self)))

'''
将具备如下特性
'''
tt = Vector2d(3,4)
print(bytes(tt))
'''输出
b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@'
'''
#用于两个实例比较的时候的判断
def __eq__(self,other):
        return tuple(self) == tuple(other)

'''
将具备如下特性
'''
tt = Vector2d(3,4)
tt2 = Vector2d(3,4)
tt3 = Vector2d(5,6)
print(tt == tt2)
print(tt == tt3)
'''输出
True
False
'''
#返回绝对值,对于坐标而言,我们返回模
def __abs__(self):
        return math.hypot(self.x,self.y)

'''
将具备如下特性
'''
tt = Vector2d(3,4)
print(abs(tt))
'''输出
5.0
'''
#自定义一种判断实例布尔值的方法,这里我们认为模大于0则为True
def __bool__(self):
        return bool(abs(self))

'''
将具备如下特性
'''
tt = Vector2d(3,4)
tt2 = Vector2d(0,0)
print(bool(tt))
print(bool(tt2))
'''输出
True
False
'''
# 这个需要重点说下,格式化输出
# 在python中,有一个函数format,可以很方便的格式化输出
# 她的功能非常强大,可以参考该链接
# https://www.runoob.com/python/att-string-format.html
# 菜鸟教程,其实并没有把所有用法讲全,但是谁会把那么多用法全部记住呢,需要时候可以查
def __format__(self, format_spec=''):
        if format_spec.endswith('p'):
            format_spec = format_spec[:-1]
            coords = (abs(self),self.angle())
            outer_fmt = '<{},{}>'
        #使用内置的 format 函数把 fmt_spec 应用到向量的各个分量上,构建一个可迭代的格式化字符串。
        components = (format(c,format_spec) for c in self)
        return '({},{})'.format(*components)

关于format()这块的讲解我担心我写的会很乱很糟,完全可以在单独写一篇介绍format(),我们直接看下大神是怎么说的:
以下是《流畅的python》一书截图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
膜拜大神的作品,真的很有收获

第三部分,总结

是的,就像我们上面演示的那样,我们的python类可以实现(重写)很多类方法,让我们的类更加健壮,可以支持更多的操作,用两个下划线开头和结尾来区分这些“魔法”函数,但是并不是我们自定义的类都需要重写这些方法,就像作者说的 “要想得到功能完善的对象,这些方法可能是必备的。当然,如果你的应用用不到,就没必要全部实现 这些方法。客户并不关心你的对象是否符合 Python 风格。”

希望对你有帮助
虽然我进行了检查,但是难免有错误出现,若有错误望指出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值